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

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

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

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

16 ответов


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

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

Реализация Кода

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.

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


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

public void myFancyMethod(View v) {}

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

android:onClick="myFancyMethod"/>

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


android:onClick предназначен для уровня API 4, поэтому, если вы нацелены на


проверьте, если вы забыли поставить метод!


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

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

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

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

атрибут onClick, определенный в attrs.в XML, упоминается в виду.java as 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 во фрагменте обратный вызов должен быть определен в Activity : getContext().getClass().getMethod() вызов ограничивает поиск метода текущим контекстом, который является активностью в случае фрагмента. Следовательно, метод ищется в классе Activity, а не в классе Fragment.
  • метод обратного вызова должен принимать параметр View : С Java Class getMethod поиск метода, который принимает View.class в качестве параметра.

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

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

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


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

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

и как пояснили здесь, отражение всегда замедляет работу. (особенно на DHALVIK VM). Регистрация onClickListener лучший способ.


С 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 библиотека.


другой способ установить прослушиватели on click - это использовать 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 мы должны активно искать элемент внутри представления (часть findViewById), но с атрибутом XML Android делает это за нас.
  • анонимный класс Java требует не менее 5 строк кода, так как мы можем см., но с атрибутом XML достаточно 3 строк кода.
  • С анонимным классом Java мы должны назвать наш метод " onClick", но с атрибутом XML мы можем добавить любое имя, которое мы хотим, которое будет драматически помогите с удобочитаемостью нашего кода.
  • атрибут Xml "onClick" имеет добавлено Google на уровне API 4 release, что означает, что это немного более современный синтаксис и современный синтаксис почти всегда лучше.

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

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

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

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

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

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

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

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


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

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


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

Предположим, вы хотите добавить событие 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) {
}

Я пишу этот код в 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) {
}

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

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

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

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

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

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

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