Por lo que he leído, puede asignar un controlador onClick
a un botón de dos maneras.
Utilizando el atributo android:onClick
XML donde solo utiliza el nombre de un método público con el void name(View v)
la firma void name(View v)
o mediante el método setOnClickListener
donde pasa un objeto que implementa la interfaz OnClickListener
. Este último a menudo requiere una clase anónima que personalmente no me gusta (gusto personal) o definir una clase interna que implementa OnClickListener
.
Al usar el atributo XML, solo necesita definir un método en lugar de una clase, por lo que me pregunto si lo mismo se puede hacer a través del código y no en el diseño XML.
No, eso no es posible a través del código. Android solo implementa OnClickListener
cuando defines el atributo android:onClick="someMethod"
.
Esos dos fragmentos de código son iguales, simplemente implementados de dos maneras diferentes.
Implementación del código
Button btn = (Button) findViewById(R.id.mybutton); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myFancyMethod(v); } }); // some more code public void myFancyMethod(View v) { // does something very interesting }
Arriba está una implementación de código de OnClickListener
. Y esta es la implementación de XML.
Implementación XML
En segundo plano, Android no hace nada más que el código Java, llamando a su método en un evento de clic.
Tenga en cuenta que con el XML anterior, Android buscará el método onClick
myFancyMethod()
solo en la Actividad actual. Es importante recordar esto si está utilizando fragmentos, ya que incluso si agrega el XML anterior utilizando un fragmento, Android no buscará el método onClick
en el archivo .java
del fragmento utilizado para agregar el XML.
Otra cosa importante que noté. Usted mencionó que no prefiere los métodos anónimos. Querías decir que no te gustan las clases anónimas.
Cuando vi la respuesta principal, me di cuenta de que mi problema no era poner el parámetro (Ver v) en el elegante método:
public void myFancyMethod(View v) {}
Al intentar acceder desde el xml, uno debe usar
android:onClick="myFancyMethod"/>
Espero que ayude a alguien.
android:onClick
es para el nivel 4 de la API en adelante, por lo que si tiene como objective <1.6, entonces no puede usarlo.
¡Comprueba si olvidaste poner el método en público!
Especificar el atributo android:onClick
da como resultado una instancia de Button
llama a setOnClickListener
internamente. Por lo tanto, no hay absolutamente ninguna diferencia.
Para tener un entendimiento claro, veamos cómo el framework maneja el atributo XML onClick
.
Cuando un archivo de diseño está inflado, todas las Vistas especificadas en él se instancian. En este caso específico, la instancia de Button
se crea utilizando public Button (Context context, AttributeSet attrs, int defStyle)
constructor. Todos los atributos en la etiqueta XML se leen del paquete de recursos y pasan como AttributeSet
al constructor.
Button
clase Button
se hereda de la clase View
, lo que da como resultado que se llame al constructor View
, que se encarga de configurar el controlador de callback mediante setOnClickListener
.
El atributo onClick definido en attrs.xml se refiere en View.java como R.styleable.View_onClick
.
Aquí está el código de View.java
que hace la mayor parte del trabajo llamando a setOnClickListener
por sí mismo.
case R.styleable.View_onClick: if (context.isRestricted()) { throw new IllegalStateException("The android:onClick attribute cannot " + "be used within a restricted context"); } final String handlerName = a.getString(attr); if (handlerName != null) { setOnClickListener(new OnClickListener() { private Method mHandler; public void onClick(View v) { if (mHandler == null) { try { mHandler = getContext().getClass().getMethod(handlerName, View.class); } catch (NoSuchMethodException e) { int id = getId(); String idText = id == NO_ID ? "" : " with id '" + getContext().getResources().getResourceEntryName( id) + "'"; throw new IllegalStateException("Could not find a method " + handlerName + "(View) in the activity " + getContext().getClass() + " for onClick handler" + " on view " + View.this.getClass() + idText, e); } } try { mHandler.invoke(getContext(), View.this); } catch (IllegalAccessException e) { throw new IllegalStateException("Could not execute non " + "public method of the activity", e); } catch (InvocationTargetException e) { throw new IllegalStateException("Could not execute " + "method of the activity", e); } } }); } break;
Como puede ver, se llama a setOnClickListener
para registrar la callback, como lo hacemos en nuestro código. La única diferencia es que utiliza Java Reflection
para invocar el método de callback definido en nuestra actividad.
Estos son los motivos de los problemas mencionados en otras respuestas:
Java Class getMethod
, solo se buscan las funciones con el especificador de acceso público. De lo contrario, prepárese para manejar la excepción IllegalAccessException
. getContext().getClass().getMethod()
call restringe la búsqueda de métodos al contexto actual, que es Activity en caso de Fragment. Por lo tanto, el método se busca dentro de la clase Activity y no en la clase Fragment. Java Class getMethod
busca el método que acepta View.class
como parámetro. Tenga en cuenta que si desea utilizar la función onClick XML, el método correspondiente debe tener un parámetro, cuyo tipo debe coincidir con el objeto XML.
Por ejemplo, un botón se vinculará a su método a través de su cadena de nombre: android:onClick="MyFancyMethod"
pero la statement del método debería mostrar: ...MyFancyMethod(View v) {...
Si intenta agregar esta característica a un elemento de menú , tendrá la misma syntax exacta en el archivo XML, pero su método se declarará como: ...MyFancyMethod(MenuItem mi) {...
Aquí hay respuestas muy buenas, pero quiero agregar una línea:
En android:onclick
en XML, Android usa el reflection de Java detrás de la escena para manejar esto.
Y como se explica aquí, la reflexión siempre ralentiza el rendimiento. (especialmente en Dhalvik VM). El registro de onClickListener
es una mejor manera.
Con Java 8, probablemente puedas usar Method Reference para lograr lo que quieres.
Supongamos que es su controlador de eventos onClick
para un botón.
private void onMyButtonClicked(View v) { if (v.getId() == R.id.myButton) { // Do something when myButton was clicked } }
Luego, pasa la referencia de método de instancia onMyButtonClicked
en una setOnClickListener()
a setOnClickListener()
como esta.
Button myButton = (Button) findViewById(R.id.myButton); myButton.setOnClickListener(this::onMyButtonClicked);
Esto le permitirá evitar usted mismo la definición explícita de una clase anónima. Sin embargo, debo enfatizar que la Referencia de método de Java 8 es en realidad solo un azúcar sintáctico. En realidad, crea una instancia de la clase anónima para usted (al igual que lo hizo la expresión lambda), por lo tanto, se aplica una precaución similar a la del controlador de eventos lambda-expression-style cuando se elimina el registro de su controlador de eventos. Este artículo lo explica muy bien.
PD. Para aquellos que tienen curiosidad acerca de cómo realmente puedo usar la característica de lenguaje Java 8 en Android, es una cortesía de la biblioteca retrolambda .
Otra forma de configurar sus escuchas en clic sería usar XML. Solo agrega el atributo android: onClick a tu etiqueta.
Es una buena práctica usar el atributo xml “onClick” sobre una clase Java anónima siempre que sea posible.
Antes que nada, echemos un vistazo a la diferencia en el código:
Atributo XML Attribute / onClick
Porción de XML
Porción de Java
public void showToast(View v) { //Add some logic }
Anónimo Java Class / setOnClickListener
Porción XML
Porción de Java
findViewById(R.id.button1).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { //Add some logic } });
Aquí están los beneficios de usar el atributo XML sobre una clase Java anónima:
Por supuesto, no siempre es posible usar el atributo Xml, aquí están las razones por las cuales no lo elegimos:
Al usar el atributo XML, solo necesita definir un método en lugar de una clase, por lo que me pregunto si lo mismo se puede hacer a través del código y no en el diseño XML.
Sí, puedes hacer que tu fragment
o activity
implemente View.OnClickListener
y cuando inicialice sus nuevos objetos de vista en código, simplemente puede hacer mView.setOnClickListener(this);
y esto establece automáticamente todos los objetos de vista en código para usar el método onClick(View v)
que tiene su fragment
o activity
etc.
para distinguir qué vista ha llamado el método onClick
, puede usar una instrucción switch en el método v.getId()
.
Esta respuesta es diferente de la que dice “No, eso no es posible a través del código”
Respaldo a la respuesta de Ruivo, sí, debes declarar el método como “público” para poder utilizar en onclick XML de Android. Estoy desarrollando una orientación de aplicación desde API Nivel 8 (minSdk …) a 16 (targetSdk …).
Estaba declarando mi método como privado y provocó un error, simplemente declararlo como público funciona muy bien.
Add Button in xml and give onclick attribute name that is the name of Method. Button btnAdd = (Button) findViewById(R.id.mybutton); btnAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { addNumber(v); } }); Private void addNumber(View v){ //Logic implement switch (v.getId()) { case R.id.btnAdd : break; default: break; }}
Supongamos que desea agregar un evento de clic como este main.xml
En el archivo java, debe escribir un método como este.
public void register(View view) { }
Estoy escribiendo este código en un archivo xml …
Y escribe este código en fragmentos …
public void register(View view) { }
La mejor forma de hacerlo es con el siguiente código:
Button button = (Button)findViewById(R.id.btn_register); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //do your fancy method } });
Tenga cuidado, aunque android:onClick
XML parece ser una forma conveniente de manejar el clic, la implementación setOnClickListener
hace algo más que agregar onClickListener
. De hecho, puso la propiedad view clickable
en true.
Si bien es posible que no sea un problema en la mayoría de las implementaciones de Android, de acuerdo con el constructor del teléfono, el botón siempre es predeterminado para hacer clic = verdadero, pero otros constructores en algún modelo de teléfono pueden tener un clic = falso predeterminado en vistas de botones.
Entonces, establecer el XML no es suficiente, tienes que pensar todo el tiempo para agregar android:clickable="true"
en el botón no, y si tienes un dispositivo donde el valor predeterminado es clickable = true e incluso olvidas poner este XML atributo, no se dará cuenta del problema en tiempo de ejecución, pero recibirá los comentarios en el mercado cuando esté en manos de sus clientes.
Además, nunca podemos estar seguros de cómo Proguard ofuscará y cambiará el nombre de los atributos XML y el método de clase, por lo que no es 100% seguro de que nunca tendrán un error algún día.
Entonces, si nunca quieres tener problemas y nunca lo piensas, es mejor usar setOnClickListener
o bibliotecas como ButterKnife con la anotación @OnClick(R.id.button)