¿Cómo exactamente el atributo android: onClick XML difiere de setOnClickListener?

De lo que he leído puede asignar un manejador onClick a un botón de dos maneras.

Uso del atributo android:onClick XML donde sólo se utiliza el nombre de un método público con el void name(View v) la firma void name(View v) o utilizando el método setOnClickListener en el que se 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 la definición de una clase interna que implementa OnClickListener .

Mediante el uso del atributo XML sólo es necesario definir un método en lugar de una clase por lo que me preguntaba si lo mismo puede hacerse a través de código y no en el diseño XML.

No, eso no es posible a través del código. Android solo implementa OnClickListener para ti cuando defines el atributo android:onClick="someMethod" .

Estos dos fragmentos de código son iguales, 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 XML.

Implementación de 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 método onClick myFancyMethod() sólo en la actividad actual. Esto es importante para recordar si está usando fragmentos, ya que incluso si agrega el XML anterior usando un fragmento, Android no buscará el método onClick en el archivo .java del fragmento usado para agregar el XML.

Otra cosa importante que noté. Mencionaste que no prefieres métodos anónimos. Quieres decir que no te gustan las clases anónimas.

Cuando vi la respuesta superior, me hizo darse cuenta de mi problema que no estaba poniendo el parámetro (View v) en el método de fantasía

 public void myFancyMethod(View v) {} 

Al intentar acceder desde el xml

 android:onClick="myFancyMethod"/> 

Esperanza que ayuda a alguien

android:onClick es para el nivel 4 de la API en adelante, por lo que si está dirigido a <1.6, entonces no puede utilizarlo.

¡Comprueba si olvidaste de poner el método en público!

Especificar el atributo android:onClick da como resultado la instancia Button llama a setOnClickListener internamente. Por lo tanto, no hay absolutamente ninguna diferencia.

Para tener un entendimiento claro, veamos cómo el atributo XML onClick es manejado por el framework.

Cuando se infla un archivo de diseño, todas las vistas especificadas en él se instancian. En este caso específico, la instancia Button se crea mediante public Button (Context context, AttributeSet attrs, int defStyle) constructor public Button (Context context, AttributeSet attrs, int defStyle) . Todos los atributos de la etiqueta XML se leen desde el paquete de recursos y se transmiten como AttributeSet al constructor.

Button clase Button se hereda de la clase View que da como resultado el constructor View se llama, el cual se ocupa de establecer el controlador de devolución de llamada de setOnClickListener través de 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 para usted 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 se puede ver, setOnClickListener se llama para registrar la devolución de llamada, como lo hacemos en nuestro código. La única diferencia es que utiliza Java Reflection para invocar el método de devolución de llamada definido en nuestra Actividad.

Aquí están los motivos de los problemas mencionados en otras respuestas:

  • El método de devolución de llamada debe ser público : Dado que se utiliza la Java Class getMethod , solo se buscan las funciones con especificador de acceso público. De lo contrario, esté preparado para manejar la excepción IllegalAccessException .
  • Mientras se usa Button con onClick en Fragment, la devolución de llamada debe definirse en Activity : getContext().getClass().getMethod() call restringe la búsqueda del método al contexto actual, que es Activity en el caso de Fragment. Por lo tanto, el método se busca en la clase de actividad y no en la clase Fragmento.
  • El método de devolución de llamada debe aceptar el parámetro View : Dado que 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 estará vinculado a su método a través de su cadena de nombre: android:onClick="MyFancyMethod" pero la declaración del método debe mostrar: ...MyFancyMethod(View v) {...

Si intenta agregar esta función a un elemento de menú , tendrá la misma sintaxis exacta en el archivo XML, pero su método se declarará como: ...MyFancyMethod(MenuItem mi) {...

Hay respuestas muy bien aquí, pero quiero agregar una línea:

En android:onclick en XML, Android utiliza la reflexión 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). Registrar onClickListener es una manera mejor.

Con Java 8, probablemente podría utilizar Referencia de método para lograr lo que desea.

Supongamos que este 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 } } 

A continuación, pasa la referencia de método instancia onMyButtonClicked en una llamada setOnClickListener() como ésta.

 Button myButton = (Button) findViewById(R.id.myButton); myButton.setOnClickListener(this::onMyButtonClicked); 

Esto le permitirá evitar definir explícitamente una clase anónima por usted mismo. Sin embargo, debo enfatizar que la Referencia de Método de Java 8 es en realidad sólo un azúcar sintáctico. De hecho, crea una instancia de la clase anónima para usted (al igual que la expresión lambda), por lo que se ha aplicado una precaución similar a la del manejador de eventos lambda-expression-style cuando se llega al desregistro de su controlador de eventos. Este artículo lo explica muy bien.

PD. Para los que curiosos acerca de cómo puedo realmente utilizar la característica de lenguaje Java 8 en Android, es una cortesía de la biblioteca retrolambda .

Otra forma de establecer sus oyentes de clic sería utilizar XML. Simplemente añada el atributo android: onClick a su etiqueta.

Es una buena práctica usar el atributo xml "onClick" sobre una clase Java anónima siempre que sea posible.

En primer lugar, echemos un vistazo a la diferencia en el código:

Atributo XML / onClick

Porción XML

 <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button1" android:onClick="showToast"/> 

Porción de Java

 public void showToast(View v) { //Add some logic } 

Clase Java anónima / setOnClickListener

Porción XML

 <Button android:layout_width="wrap_content" android:layout_height="wrap_content"/> 

Porción de Java

 findViewById(R.id.button1).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { //Add some logic } }); 

Estas son las ventajas de usar el atributo XML sobre una clase Java anónima:

  • Con la clase Java anónima siempre tenemos que especificar un id para nuestros elementos, pero con el identificador de atributo XML se puede omitir.
  • Con la clase Java anónima tenemos que buscar activamente el elemento dentro de la vista (porción findViewById), pero con el atributo XML Android lo hace por nosotros.
  • La clase Java anónima requiere al menos 5 líneas de código, como podemos ver, pero con el atributo XML 3 líneas de código son suficientes.
  • Con la clase Anonymous Java tenemos que nombrar nuestro método "onClick", pero con el atributo XML podemos agregar cualquier nombre que queramos, lo que ayudará dramáticamente con la legibilidad de nuestro código.
  • Xml El atributo "onClick" ha sido añadido por Google durante la versión API de nivel 4, lo que significa que es una sintaxis un poco más moderna y la sintaxis moderna es casi siempre mejor.

Por supuesto, no siempre es posible usar el atributo Xml, aquí están las razones por las que no lo elegimos:

  • Si estamos trabajando con fragmentos. El atributo onClick sólo se puede agregar a una actividad, así que si tenemos un fragmento, tendríamos que usar una clase anónima.
  • Si queremos mover el listener onClick a una clase separada (quizás si es muy complicado y / o nos gustaría reutilizarlo en diferentes partes de nuestra aplicación), entonces no querríamos usar el atributo xml ya sea.

Apoyando la respuesta de Ruivo, sí tienes que declarar el método como "público" para poder usarlo en el XML de onclick de Android. Estoy desarrollando una orientación de aplicación desde el nivel 8 de la API (minSdk …) a 16 (targetSdk …).

Estaba declarando mi método como privado y causó error, simplemente declararlo como obras públicas grandes.

  Add Button in xml and give onclick attribute name that is the name of Method. <!--xml --!> <Button android:id="@+id/btn_register" android:layout_margin="1dp" android:onClick="addNumber" android:text="Add" /> 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; }} 

Mediante el uso del atributo XML sólo es necesario definir un método en lugar de una clase por lo que me preguntaba si lo mismo puede hacerse a través de código y no en el diseño XML.

Sí, puede hacer que su 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 utilizar una instrucción switch en el método v.getId() .

Esta respuesta es diferente de la que dice "No que no es posible a través de código"

Supongamos que desea agregar evento de clic como este main.xml

 <Button android:id="@+id/btn_register" android:layout_margin="1dp" android:layout_marginLeft="3dp" android:layout_marginTop="10dp" android:layout_weight="2" android:onClick="register" android:text="Register" android:textColor="#000000"/> 

En el archivo java, usted tiene que escribir un método como este método.

 public void register(View view) { } 

Estoy escribiendo este código en archivo xml …

 <Button android:id="@+id/btn_register" android:layout_margin="1dp" android:layout_marginLeft="3dp" android:layout_marginTop="10dp" android:layout_weight="2" android:onClick="register" android:text="Register" android:textColor="#000000"/> 

Y escribir este código en fragmento …

 public void register(View view) { } 

La mejor manera de hacerlo es con el código siguiente:

  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 hacer algo más que agregar el onClickListener . De hecho, puso la propiedad de vista que se clickable hacer 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 clickable = true, pero otros constructores en algún modelo de teléfono pueden tener un valor predeterminado clickable = false en las vistas sin botón.

Así que configurar 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 y te olvidas incluso una vez para poner este XML , Usted no notará el problema en tiempo de ejecución, pero obtendrá la retroalimentación en el mercado cuando estará en manos de sus clientes!

Además, nunca podemos estar seguros acerca de cómo proguard ofuscará y cambiará el nombre de atributos XML y método de clase, por lo que no es 100% seguro que nunca tendrán un error un día.

Así que si nunca quieres tener problemas y nunca pensar en ello, es mejor usar setOnClickListener o bibliotecas como ButterKnife con anotación @OnClick(R.id.button)

  • Android OnClickListener no dispara en GridView (sólo 2.2)
  • Cómo eliminar el fondo de ImagePack en Android, pero sigo haciendo clic en Resaltar
  • Android no puede encontrar mi método onClick
  • Interceptación de clics desde subview en Android
  • Android cómo escribir clic evento de un ImageView
  • Haga que un botón de Android cambie el fondo al hacer clic en XML
  • GetCheckedRadioButtonId () devuelve int inútil?
  • Android: ¿Cómo harías que los botones no se pudieran hacer clic durante algún tiempo?
  • Eliminar un detector de onclick
  • Establecer OnClickListener para elementos en ListView con 2 vistas
  • Fragmento onListItemClick
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.