Як саме атрибут android: onClick XML відрізняється від setOnClickListener?


416

З цього прочитаного ви можете призначити onClickобробник кнопці двома способами.

Використання android:onClickатрибута XML, де ви просто використовуєте ім'я публічного методу з підписом void name(View v)або за допомогою setOnClickListenerметоду, де ви передаєте об'єкт, що реалізує OnClickListenerінтерфейс. Останній часто вимагає анонімного класу, який особисто мені не подобається (особистий смак) або визначення внутрішнього класу, який реалізує OnClickListener.

Використовуючи атрибут XML, вам просто потрібно визначити метод замість класу, тому мені було цікаво, чи можна це зробити за допомогою коду, а не в макеті XML.


4
Я читаю вашу проблему і думаю, що ви застрягли там же, як і я. Я натрапив на дуже гарне відео, яке дуже допомогло мені вирішити мою проблему. Знайдіть відео за наступним посиланням: youtube.com/watch?v=MtmHURWKCmg&feature=youtu.be Сподіваюся, це допоможе і вам :)
користувач2166292

9
Для тих, хто хоче заощадити час на перегляді відео, розміщеного в коментарі вище, він просто демонструє, як дві кнопки можуть мати однаковий метод для свого onClickатрибута у файлі макета. Це робиться завдяки параметру View v. Ви просто перевіряєте if (v == findViewById(R.id.button1)) тощо.
CodyBugstein

13
@Imray Я думаю, що це краще використовувати v.getId() == R.id.button1, оскільки вам не потрібно знаходити фактичний контроль і робити порівняння. І ви можете використовувати switchзамість великої кількості ifs.
Самі Кухмонен

3
Цей підручник вам дуже допоможе. натисніть тут
c49

Використання xml android: onClick викликає збій.

Відповіді:


604

Ні, це неможливо через код. 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()лише в поточній діяльності. Це важливо пам’ятати, якщо ви використовуєте фрагменти, оскільки навіть якщо ви додаєте XML вище за допомогою фрагмента, Android не буде шукати onClickметод у .javaфайлі фрагмента, який використовується для додавання XML.

Ще одна важлива річ, яку я помітив. Ви згадали, що не віддаєте перевагу анонімним методам . Ви хотіли сказати, що вам не подобаються анонімні заняття .


4
Я не гуру Java, але так, я мав на увазі анонімний клас. Дякуємо за Ваш відповідь. Дуже ясно.
емітракс

118
Зауважте, що якщо ви використовуєте XML onclick, вам слід ввести метод onclick ( myFancyMethod()) у поточну активність. Це важливо, якщо ви використовуєте фрагменти, оскільки програмний спосіб налаштування слухачів onclick, ймовірно, матиме метод обробки кліків у фрагменті onCreateView () ... там, де його не було б знайдено, якщо посилатися з XML.
Peter Ajtai

12
Так, метод повинен бути загальнодоступним.
Октавіан А. Дам’ян

12
Цікавим є те, що виконання цього коду дозволяє захистити доступ до методу, зробивши метод приватним, тоді як, виконуючи це, xml-шлях призводить до викриття методу.
bgse

5
Той факт, що функція (у XML-підході) повинна бути в "Активність", важлива не тільки при розгляді фрагментів, але і на власному перегляді (містить кнопку). Якщо у вас є спеціальний перегляд, який ви повторно використовуєте в кількох заходах, але ви хочете використовувати один і той же метод onClick для всіх випадків, метод XML не є найзручнішим. Вам потрібно буде помістити цей метод onClickMethod (з тим самим тілом) у кожній діяльності, яка використовує ваш власний вид.
Bartek Lipinski

87

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

public void myFancyMethod(View v) {}

При спробі отримати доступ до нього з xml, слід скористатися

android:onClick="myFancyMethod"/>

Сподіваюся, що хтось допомагає.


74

android:onClick призначений для рівня 4 API далі, тому якщо ви орієнтуєтесь на <1.6, ви не можете його використовувати.


33

Перевірте, чи забули ви розмістити метод загальнодоступним!


1
чому це обов’язково оприлюднювати?
eRaisedToX

3
@eRaisedToX Я думаю, що це досить зрозуміло: якщо він не є загальнодоступним, його не можна викликати з Android.
m0skit0

28

Визначення android:onClickрезультатів атрибута, Buttonнаприклад, setOnClickListenerвнутрішній виклик . Отже, різниці абсолютно немає.

Щоб мати чітке розуміння, давайте подивимося, як onClickатрибут XML обробляється рамкою.

Якщо файл макета завищений, всі представлені в ньому представлення представлені миттєво. У цьому конкретному випадку 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 у фрагменті, зворотний виклик повинен бути визначений у розділі "Активність" : getContext().getClass().getMethod()виклик обмежує пошук методу поточним контекстом, який є "Активність" у разі "фрагмента". Отже, шукається метод у класі діяльності, а не у класі Fragment.
  • Метод зворотного виклику повинен приймати параметр View : оскільки Java Class getMethodшукає метод, який приймає View.classяк параметр.

1
Для мене це було відсутнім - Java використовує Reflection, щоб знайти обробник кліків, починаючи з getContext (). Для мене було трохи таємничо, як натискання поширюється від фрагмента до діяльності.
Ендрю Квіссер

15

Тут є дуже вдалі відповіді, але я хочу додати один рядок:

У системі android:onclickXML Android використовує java-відображення поза сценою для вирішення цього питання.

І як пояснено тут, роздуми завжди сповільнюють продуктивність. (особливо про Дальвік В.М.). Реєстрація onClickListener- кращий спосіб.


5
На скільки це може сповільнити додаток? :) Півмісяця; навіть не? У порівнянні з фактично роздутим макетом це схоже на перо і кит
Конрад Моравський

14

Зауважте, що якщо ви хочете використовувати функцію onClick XML, відповідний метод повинен мати один параметр, тип якого повинен відповідати об’єкту XML.

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

Якщо ви намагаєтесь додати цю функцію до пункту меню , у файлі XML він буде мати такий самий синтаксис, але ваш метод буде оголошено як:...MyFancyMethod(MenuItem mi) {...


6

Ще одним способом встановити ваших слухачів клацань буде використання 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"/>

Ява частина

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

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

Порція XML

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

Ява частина

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

Ось переваги використання атрибута XML над анонімним класом Java:

  • У класі Anonymous Java нам завжди потрібно вказати ідентифікатор для наших елементів, але з атрибутом XML ідентифікатор може бути опущений.
  • З класом Anonymous Java нам доводиться активно шукати елемент всередині перегляду (частина findViewById), але з атрибутом XML Android це робить для нас.
  • Для анонімного Java-класу потрібно як мінімум 5 рядків коду, як ми бачимо, але з атрибутом XML достатньо 3 рядків коду.
  • З класом Anonymous Java ми повинні назвати наш метод "onClick", але за допомогою атрибута XML ми можемо додати будь-яке ім'я, яке ми хочемо, що суттєво допоможе в читанні нашого коду.
  • Google додав атрибут XML “onClick” під час випуску API рівня 4, це означає, що це трохи сучасніший синтаксис, а сучасний синтаксис майже завжди кращий.

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

  • Якщо ми працюємо з фрагментами. атрибут onClick може бути доданий лише до активності, тому, якщо у нас є фрагмент, нам доведеться використовувати анонімний клас.
  • Якщо ми хотіли б перенести слухач onClick в окремий клас (можливо, якщо він дуже складний і / або ми хотіли б повторно використовувати його в різних частинах нашого додатку), тоді ми не хотіли б використовувати атрибут xml або.

І зауважте, що функція, яка викликається атрибутами XML, завжди має бути загальнодоступною, і якщо вони будуть оголошені як приватні курки, це призведе до виключення.
Бестін Джон

5

З 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 .


5

Використовуючи атрибут XML, вам просто потрібно визначити метод замість класу, тому мені було цікаво, чи можна це зробити за допомогою коду, а не в макеті XML.

Так, ви можете зробити fragmentабо activityреалізуватиView.OnClickListener

і коли ви ініціалізуєте свої нові об’єкти перегляду в коді, ви можете просто зробити mView.setOnClickListener(this);

і це автоматично встановлює всі об'єкти перегляду в коді для використання onClick(View v)методу, який має ваш fragmentабо activityін.

щоб розрізнити, який погляд викликав onClickметод, можна використовувати оператор переключення на v.getId()метод.

Ця відповідь відрізняється від тієї, яка говорить "Ні, це неможливо через код"


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

3

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

Я декларував свій метод приватним, і це спричинило помилку, просто оголосивши його як громадський працює чудово.


виявляється, що змінні, оголошені в класі хостингової діяльності, не можуть бути використані в області оголошеного зворотного виклику; Activity Bundle Activity.mBundle викине IllegalStateException / NullPointerException, якщо використовується в myFancyMethod ().
Квазаур

2

Будьте уважні, хоча android:onClickXML, здається, є зручним способом обробки кліків, setOnClickListenerреалізація робить щось додаткове, ніж додавання onClickListener. Дійсно, це поставило властивість перегляду clickableсправжньою.

Хоча це може не бути проблемою для більшості реалізацій Android, згідно з конструктором телефону, кнопка завжди за замовчуванням для натискання = істинно, але інші конструктори в деяких моделях телефону можуть мати типовий спосіб натискання = хибний у представленнях, що не переглядаються кнопками.

Отже, встановити XML недостатньо, вам доведеться весь час думати, щоб додати android:clickable="true"не кнопку, і якщо у вас є пристрій, де за замовчуванням можна натискати = вірно, і ви навіть забудете один раз поставити цей атрибут XML, ви не помітите проблема під час виконання, але ви отримаєте відгуки про ринок, коли він опиниться в руках ваших клієнтів!

Крім того, ми ніколи не можемо бути впевнені в тому, як Proguard приховає і перейменує атрибути XML та метод класу, тому не на 100% безпечний, що у них ніколи не буде помилки одного дня.

Тож якщо ви ніколи не хочете мати проблем і ніколи не замислюєтесь про це, краще використовувати setOnClickListenerбібліотеки типу ButterKnife з анотацією@OnClick(R.id.button)


onClickАтрибут XML також встановлює , clickable = trueтому що він викликає setOnClickListenerна Viewвнутрішньо
Флоріан Walther

1

Припустимо, ви хочете додати подію кліку, як це 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) {
}

0

Я написав цей код у 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) {
}

Це можливо фрагментарно.
користувач2786249

0

Найкращий спосіб зробити це за допомогою наступного коду:

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

Я не бачу, як це відповідає на питання - запитувач хоче уникати створення анонімного класу, а натомість використовувати метод класу.
ajshort

0

Щоб полегшити своє життя та уникнути анонімного класу в setOnClicklistener (), реалізуйте інтерфейс View.OnClicklistener, як показано нижче:

YourClass публічного класу розширює реалізацію CommonActivity View.OnClickListener, ...

це дозволяє уникнути:

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        yourMethod(v);
    }
});

і прямує до:

@Override
public void onClick(View v) {
  switch (v.getId()) {
    case R.id.your_view:
      yourMethod();
      break;
  }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.