Чем именно атрибут XML android: onClick отличается от setOnClickListener? - PullRequest
388 голосов
/ 11 ноября 2010

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

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

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

Ответы [ 17 ]

573 голосов
/ 11 ноября 2010

Нет, это невозможно с помощью кода. 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, вызывая ваш метод для события click.

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

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

82 голосов
/ 08 апреля 2013

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

public void myFancyMethod(View v) {}

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

android:onClick="myFancyMethod"/>

Надеюсь, это кому-нибудь поможет.

73 голосов
/ 11 ноября 2010

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

30 голосов
/ 09 июля 2011

Убедитесь, что вы забыли опубликовать метод!

26 голосов
/ 01 мая 2014

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

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

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

Button класс наследуется от класса View, что приводит к вызову конструктора View, который заботится о настройке обработчика обратного вызова click через 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 исключение.
  • При использовании Button с onClick во Fragment обратный вызов должен быть определен в Activity : getContext().getClass().getMethod() вызов ограничивает поиск метода текущим контекстом, что является Активностью в случае Фрагмента.Следовательно, метод ищется в классе Activity, а не в классе Fragment.
  • Метод обратного вызова должен принимать параметр просмотра : так как Java Class getMethod ищет метод, который принимает View.class какпараметр.
14 голосов
/ 15 января 2013

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

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

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

11 голосов
/ 14 сентября 2014

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

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

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

5 голосов
/ 11 июня 2016

Другим способом настройки прослушивателей при нажатии будет использование 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:

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

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

  • Если мы работаем с фрагментами. Атрибут onClick можно добавить только к деятельности, поэтому если у нас есть фрагмент, мы должны были бы использовать анонимный класс.
  • Если мы хотим переместить слушателя onClick в отдельный класс (возможно, если это очень сложно и / или мы хотели бы использовать его в различные части нашего приложения), то мы бы не хотели использовать атрибут xml либо.
4 голосов
/ 15 июня 2015
   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;
    }}
4 голосов
/ 01 апреля 2016

В 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 на самом деле является просто синтаксическим сахаром.На самом деле он создает для вас экземпляр анонимного класса (так же как и лямбда-выражения), следовательно, с той же осторожностью, что и обработчик событий в стиле лямбда-выражения, применялся, когда вы приходите к отмене регистрации вашего обработчика событий.Эта статья объясняет это действительно хорошо.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...