Как отличается атрибут android: onClick XML от setOnClickListener?

Из того, что я прочитал, вы можете назначить обработчик onClick кнопке двумя способами.

Использование атрибута android:onClick XML, в котором вы просто используете имя общедоступного метода с сигнатурой void name(View v) или с помощью метода setOnClickListener, где вы передаете объект, реализующий интерфейс OnClickListener. Последнее часто требует анонимного класса, который лично мне не нравится (личный вкус) или определения внутреннего класса, который реализует OnClickListener.

Используя атрибут XML, вам просто нужно определить метод вместо класса, чтобы я был интересно, может ли это быть сделано через код, а не в XML-макете.

Ответ 1

Нет, это невозможно с помощью кода. Android просто реализует OnClickListener для вас, когда вы определяете атрибут android:onClick="someMethod".

Эти два фрагмента кода равны, просто реализованы двумя разными способами.

Внедрение кода

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
}

Выше представлена ​​реализация кода OnClickListener. И это реализация XML.

Внедрение 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 -->

В фоновом режиме Android делает не что иное, как код Java, вызывая ваш метод при событии клика.

Обратите внимание, что с помощью XML выше Android будет искать метод onClick myFancyMethod() только в текущей Activity. Это важно помнить, если вы используете фрагменты, так как даже если вы добавите XML выше, используя фрагмент, Android не будет искать метод onClick в файле .java фрагмента, используемого для добавления XML.

Еще одна важная вещь, которую я заметил. Вы упомянули, что не предпочитаете анонимные методы. Вы хотели сказать, что вам не нравятся анонимные классы.

Ответ 2

Когда я увидел верхний ответ, я понял, что моя проблема не в том, чтобы поместить параметр (View v) в причудливый метод:

public void myFancyMethod(View v) {}

При попытке получить доступ к нему из XML, следует использовать

android:onClick="myFancyMethod"/>

Надеюсь, что это помогает кому-то.

Ответ 3

android:onClick для уровня API 4, и поэтому, если вы нацеливаете < 1.6, то вы не можете его использовать.

Ответ 4

Проверьте, не забыл ли вы опубликовать этот метод!

Ответ 5

Указание атрибута android:onClick приводит к тому, что экземпляр Button вызывает setOnClickListener внутренне. Следовательно, нет никакой разницы.

Чтобы иметь ясное понимание, давайте посмотрим, как XML-атрибут onClick обрабатывается каркасом.

Когда файл макета завышен, все Представленные в нем Представления создаются. В этом конкретном случае экземпляр Button создается с помощью конструктора public Button (Context context, AttributeSet attrs, int defStyle). Все атрибуты в теге XML считываются из пакета ресурсов и передаются как AttributeSet в конструктор.

Button класс наследуется от класса View, в результате которого вызывается конструктор View, который заботится о настройке обработчика обратного вызова щелчка через setOnClickListener.

Атрибут onClick, определенный в attrs.xml, указан в View.java как R.styleable.View_onClick.

Вот код View.java, который выполняет большую часть работы для вас, вызывая setOnClickListener сам по себе.

 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;

Как вы можете видеть, для регистрации обратного вызова вызывается setOnClickListener, как в нашем коде. Единственное отличие заключается в том, что он использует Java Reflection для вызова метода обратного вызова, определенного в нашей деятельности.

Вот причина проблем, упомянутых в других ответах:

  • Метод обратного вызова должен быть общедоступным. Поскольку используется Java Class getMethod, выполняются только функции с указателем общего доступа для. В противном случае будьте готовы обработать исключение IllegalAccessException.
  • При использовании кнопки с onClick in Fragment обратный вызов должен быть определен в Activity: getContext().getClass().getMethod() вызов ограничивает поиск метода текущим контекстом, который является Activity в случае Fragment. Следовательно, поиск метода выполняется в классе Activity, а не в классе Fragment.
  • Метод обратного вызова должен принимать параметр View. Поскольку Java Class getMethod ищет метод, который принимает View.class как параметр.

Ответ 6

Обратите внимание, что если вы хотите использовать функцию onClick XML, соответствующий метод должен иметь один параметр, тип которого должен соответствовать объекту XML.

Например, кнопка будет привязана к вашему методу через строку имени: android:onClick="MyFancyMethod", но объявление метода должно показать: ...MyFancyMethod(View v) {...

Если вы пытаетесь добавить эту функцию в элемент меню, она будет иметь тот же синтаксис в файле XML, но ваш метод будет объявлен как: ...MyFancyMethod(MenuItem mi) {...

Ответ 7

Здесь есть очень хорошие ответы, но я хочу добавить одну строку:

В android:onclick в XML Android использует Java-отражение за сценой, чтобы справиться с этим.

И , как объясняется здесь, отражение всегда замедляет производительность. (особенно на Далвик В.М.). Регистрация onClickListener - лучший способ.

Ответ 8

Другим способом установки ваших прослушивателей кликов будет использование XML. Просто добавьте android: атрибут onClick в свой тег.

Хорошей практикой является использование атрибута xml "onClick" над анонимным классом Java, когда это возможно.

Прежде всего, давайте рассмотрим разницу в коде:

атрибут атрибута XML/onClick

Часть XML

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

Часть Java

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

Анонимный класс Java/setOnClickListener

Часть XML

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

Часть Java

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

Вот преимущества использования атрибута XML над анонимным классом Java:

  • С анонимным Java-классом мы всегда должны указывать идентификатор для нашего элементов, но с идентификатором атрибута XML можно опустить.
  • С анонимным Java-классом мы должны активно искать элемент внутри части view (findViewById), но с атрибутом XML Android делает это для нас.
  • Анонимный класс Java требует не менее 5 строк кода, так как мы можем см., но с атрибутом XML достаточно 3 строк кода.
  • С анонимным классом Java мы должны назвать наш метод "onClick" , но с атрибутом XML мы можем добавить любое имя, которое мы хотим, которое будет значительно помогают с читабельностью нашего кода.
  • Атрибут Xml "onClick" был добавлен Google во время уровня API 4, что означает, что это немного более современный синтаксис и современный синтаксис почти всегда лучше.

Конечно, не всегда можно использовать атрибут Xml, вот почему мы его не выбрали:

  • Если мы работаем с фрагментами. атрибут onClick может быть добавлен только к активности, поэтому, если у нас есть фрагмент, мы должны будем использовать анонимный класс.
  • Если мы хотим переместить прослушиватель onClick в отдельный класс (возможно, если это очень сложно и/или мы хотели бы повторно использовать его в различные части нашего приложения), то мы не хотели бы использовать xml.

Ответ 9

Используя атрибут XML, вам просто нужно определить метод вместо класс, поэтому мне было интересно, можно ли сделать то же самое с помощью кода, а не в XML-макет.

Да, вы можете реализовать fragment или activity View.OnClickListener

и когда вы инициализируете свои новые объекты в коде, вы можете просто сделать mView.setOnClickListener(this);

и это автоматически устанавливает все объекты вида в коде, чтобы использовать метод onClick(View v), который имеет ваш fragment или activity и т.д.

чтобы отличить, какой вид вызвал метод onClick, вы можете использовать оператор switch для метода v.getId().

Этот ответ отличается от того, который говорит "Нет, что невозможно с помощью кода"

Ответ 10

   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;
    }}

Ответ 11

С помощью Java 8 вы, вероятно, можете использовать Справочник по методу для достижения того, что вы хотите.

Предположим, что это ваш обработчик событий onClick для кнопки.

private void onMyButtonClicked(View v) {
    if (v.getId() == R.id.myButton) {
        // Do something when myButton was clicked
    }
}

Затем вы передаете ссылку метода экземпляра onMyButtonClicked в вызове setOnClickListener(), подобном этому.

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

Это позволит вам избежать явного определения анонимного класса самостоятельно. Однако я должен подчеркнуть, что Java 8 Method Reference на самом деле является просто синтаксическим сахаром. Он фактически создает для вас экземпляр анонимного класса (так же, как и выражение лямбда), поэтому аналогичная осторожность, поскольку обработчик событий типа lambda-expression-style применяется, когда вы приходите к незарегистрированию вашего обработчика событий. Эта статья объясняет, что это действительно приятно.

PS. Для тех, кто интересуется, как я могу использовать Java 8-языковую функцию в Android, это любезно предоставлено retrolambda.

Ответ 12

Поддержка ответа Ruivo, да, вы должны объявить метод "общедоступным", чтобы иметь возможность использовать в Android XML onclick. Я разрабатываю таргетинг приложений с уровня API 8 (minSdk...) до 16 (targetSdk...).

Я объявлял свой метод как закрытый, и он вызывал ошибку, просто объявляя его общедоступным.

Ответ 13

Предположим, вы хотите добавить событие click, например, 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"/>

В java файле вам нужно написать такой метод, как этот метод.

public void register(View view) {
}

Ответ 14

Будьте осторожны, хотя android:onClick XML, по-видимому, является удобным способом обработки щелчка, реализация setOnClickListener делает что-то дополнительное, чем добавление onClickListener. В самом деле, он поместил свойство view clickable в значение true.

Хотя это может и не быть проблемой для большинства реализаций Android, по словам конструктора телефона, кнопка всегда по умолчанию имеет значение clickable = true, но другие конструкторы на какой-либо модели телефона могут иметь по умолчанию clickable = false для просмотров без кнопок.

Таким образом, для установки XML недостаточно, вы должны постоянно думать о том, чтобы добавить android:clickable="true" в не-кнопку, и если у вас есть устройство, в котором значение по умолчанию - clickable = true, и вы даже один раз забываете поместить этот атрибут XML, вы не заметите проблему во время работы, но получите обратную связь на рынке, когда она будет в руках ваших клиентов!

Кроме того, мы никогда не можем быть уверены в том, что proguard будет запутывать и переименовывать атрибуты XML и метод класса, поэтому не на 100% безопаснее, чтобы в один прекрасный день у них никогда не было ошибки.

Итак, если вы никогда не хотите иметь проблемы и никогда не задумываетесь об этом, лучше использовать setOnClickListener или библиотеки вроде ButterKnife с аннотацией @OnClick(R.id.button)

Ответ 15

Я пишу этот код в 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"/>

И напишите этот код в фрагменте...

public void register(View view) {
}

Ответ 16

Лучший способ сделать это - со следующим кодом:

 Button button = (Button)findViewById(R.id.btn_register);
 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //do your fancy method
            }
        });

Ответ 17

How android:onclick xml attribute works in Android

просто используйте имя метода в значении атрибута android: onClick. Убедитесь, что перед именем метода используется публичное ключевое слово.

Пример Android onClick XML