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

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

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

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

Solutions Collecting From Web of "Как отличается атрибут android: onClick XML от setOnClickListener?"

Нет, это невозможно с помощью кода. 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 будет искать метод myFancyMethod() только в текущей Activity. Это важно помнить, если вы используете фрагменты, так как даже если вы добавите XML выше, используя фрагмент, Android не будет искать метод onClick в .java файле фрагмента, используемого для добавления XML.

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

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

 public void myFancyMethod(View v) {} 

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

 android:onClick="myFancyMethod"/> 

Надеюсь, что кто-то поможет

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

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

Указание android:onClick атрибут setOnClickListener приводит к тому, что экземпляр 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 .
  • При использовании Button with onClick in Fragment обратный вызов должен быть определен в Activity : getContext().getClass().getMethod() ограничивает поиск метода текущим контекстом, который является Activity в случае Fragment. Следовательно, поиск метода выполняется в классе Activity, а не в классе Fragment.
  • Метод обратного вызова должен принимать параметр View : Поскольку Java Class getMethod ищет метод, который принимает параметр View.class как параметр.

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

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

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

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

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

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

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

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

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

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

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

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

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

Другим способом установки ваших прослушивателей кликов будет использование 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, что означает, что это немного более современный синтаксис, и современный синтаксис почти всегда лучше.

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

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

Поддерживая ответ Ruivo, да, вы должны объявить метод «общедоступным» для использования в XML-формате Android onclick. Я разрабатываю таргетинг приложений с уровня 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; }} 1  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; }} 

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

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

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

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

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

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

Предположим, вы хотите добавить событие 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"/> 1 <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"/> 1 <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 . В самом деле, это свойство свойства clickable true.

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

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

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

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