¿En qué se diferencia exactamente el atributo XML android:onClick de setOnClickListener?
Por lo que he leído, puedes asignar un onClick
controlador a un botón de dos maneras.
Usando el android:onClick
atributo XML donde simplemente usa el nombre de un método público con la firma void name(View v)
o usando el setOnClickListener
método donde pasa un objeto que implementa la OnClickListener
interfaz. Esto último a menudo requiere una clase anónima que personalmente no me gusta (gusto personal) o definir una clase interna que implemente el archivo OnClickListener
.
Al utilizar el atributo XML, solo necesita definir un método en lugar de una clase, por lo que me preguntaba si se puede hacer lo mismo mediante código y no en el diseño XML.
No, eso no es posible mediante código. Android simplemente implementa el OnClickListener
por ti cuando defines el android:onClick="someMethod"
atributo.
Esos dos fragmentos de código son iguales, solo que se implementan de dos maneras diferentes.
Implementación de 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 se muestra una implementación de código de un archivo OnClickListener
. Y esta es la implementación XML.
Implementación XML
<?xml version="1.0" encoding="utf-8"?>
<!-- layout elements -->
<Button android:id="@+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click me!"
android:onClick="myFancyMethod" />
<!-- even more layout elements -->
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 onClick
método myFancyMethod()
solo en la Actividad actual. Es importante recordar esto si estás usando fragmentos, ya que incluso si agregas el XML anterior usando un fragmento, Android no buscará el onClick
método en el .java
archivo del fragmento usado para agregar el XML.
Otra cosa importante que noté. Mencionaste que no prefieres 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 método elegante:
public void myFancyMethod(View v) {}
Al intentar acceder a él desde el xml, se debe utilizar
android:onClick="myFancyMethod"/>
android:onClick
es para API nivel 4 en adelante, por lo que si su objetivo es <1.6, entonces no puede usarlo.
¡Comprueba si olvidaste poner el método público!
La especificación android:onClick
de atributos da como resultado Button
una llamada de instancia setOnClickListener
interna. Por tanto, no hay absolutamente ninguna diferencia.
Para tener una comprensión clara, veamos cómo onClick
el marco maneja el atributo XML.
Cuando se infla un archivo de diseño, se crean instancias de todas las Vistas especificadas en él. En este caso específico, la Button
instancia se crea mediante public Button (Context context, AttributeSet attrs, int defStyle)
el constructor. Todos los atributos de la etiqueta XML se leen del paquete de recursos y se pasan AttributeSet
al constructor.
Button
la clase se hereda de View
la clase, lo que da como resultado que View
se llame al constructor, que se encarga de configurar el controlador de devolución de llamada de clic a través de setOnClickListener
.
El atributo onClick definido en attrs.xml se denomina en View.java R.styleable.View_onClick
.
Aquí está el código View.java
que hace la mayor parte del trabajo llamando setOnClickListener
solo.
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 puedes ver, setOnClickListener
se llama para registrar la devolución de llamada, como lo hacemos en nuestro código. La única diferencia es que se utiliza Java Reflection
para invocar el método de devolución de llamada definido en nuestra Actividad.
Aquí está el motivo de los problemas mencionados en otras respuestas:
- El método de devolución de llamada debe ser público : dado que
Java Class getMethod
se utiliza, solo se buscan funciones con especificador de acceso público. De lo contrario, prepárese para manejarIllegalAccessException
la excepción. - Al usar Button con onClick en Fragmento, la devolución de llamada debe definirse en Actividad :
getContext().getClass().getMethod()
la llamada restringe la búsqueda del método al contexto actual, que es Actividad en el caso de Fragmento. Por lo tanto, el método se busca dentro de la clase Actividad y no en la clase Fragmento. - El método de devolución de llamada debe aceptar el parámetro Ver : ya que
Java Class getMethod
busca el método que aceptaView.class
como parámetro.