¿En qué se diferencia exactamente el atributo XML android:onClick de setOnClickListener?

Resuelto emitrax asked hace 54 años • 17 respuestas

Por lo que he leído, puedes asignar un onClickcontrolador a un botón de dos maneras.

Usando el android:onClickatributo XML donde simplemente usa el nombre de un método público con la firma void name(View v)o usando el setOnClickListenermétodo donde pasa un objeto que implementa la OnClickListenerinterfaz. 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.

emitrax avatar Jan 01 '70 08:01 emitrax
Aceptado

No, eso no es posible mediante código. Android simplemente implementa el OnClickListenerpor 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 onClickmé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 onClickmétodo en el .javaarchivo 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 .

Octavian Helm avatar Nov 11 '2010 11:11 Octavian Helm

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"/>
jp093121 avatar Apr 07 '2013 22:04 jp093121

android:onClickes para API nivel 4 en adelante, por lo que si su objetivo es <1.6, entonces no puede usarlo.

James avatar Nov 11 '2010 11:11 James

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

Ruivo avatar Jul 08 '2011 20:07 Ruivo

La especificación android:onClickde atributos da como resultado Buttonuna llamada de instancia setOnClickListenerinterna. Por tanto, no hay absolutamente ninguna diferencia.

Para tener una comprensión clara, veamos cómo onClickel 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 Buttoninstancia 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 AttributeSetal constructor.

Buttonla clase se hereda de Viewla clase, lo que da como resultado que Viewse 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.javaque hace la mayor parte del trabajo llamando setOnClickListenersolo.

 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, setOnClickListenerse llama para registrar la devolución de llamada, como lo hacemos en nuestro código. La única diferencia es que se utiliza Java Reflectionpara 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 getMethodse utiliza, solo se buscan funciones con especificador de acceso público. De lo contrario, prepárese para manejar IllegalAccessExceptionla 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 getMethodbusca el método que acepta View.classcomo parámetro.
Manish Mulimani avatar May 01 '2014 04:05 Manish Mulimani