Czym dokładnie różni się atrybut android: onClick XML od setOnClickListener?

Z tego, co czytałem, można przypisać onClick obsługę przycisku na dwa sposoby.

Używając atrybutu android:onClick XML, w którym po prostu używasz nazwy publicznej metody z podpisem void name(View v) lub używając metody setOnClickListener, w której przekazujesz obiekt implementujący interfejs OnClickListener. Ta ostatnia często wymaga anonimowej klasy, której osobiście nie lubię (osobisty gust) lub zdefiniowania wewnętrznej klasy, która implementuje OnClickListener.

Używając atrybutu XML wystarczy zdefiniować metoda zamiast klasy więc byłem zastanawiasz się, czy to samo można zrobić za pomocą kodu, a nie w układzie XML.

Author: Octavian Damiean, 2010-11-11

16 answers

Nie, to nie jest możliwe za pomocą kodu. Android po prostu implementuje OnClickListener dla ciebie, gdy zdefiniujesz atrybut android:onClick="someMethod".

Te dwa fragmenty kodu są sobie równe, zaimplementowane na dwa różne sposoby.

Implementacja Kodu

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
}

Powyżej znajduje się implementacja kodu OnClickListener. I to jest implementacja XML.

Implementacja 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 -->

W tle Android nie robi nic innego jak kod Javy, wywołując metodę w przypadku kliknięcia.

Zauważ, że z powyższym XML, Android będzie szukał metody onClick myFancyMethod() tylko w bieżącej aktywności. Jest to ważne, aby pamiętać, jeśli używasz fragmentów, ponieważ nawet jeśli dodasz powyższy XML za pomocą fragmentu, Android nie będzie szukał metody onClick w pliku .java fragmentu użytego do dodania XML.

Kolejna ważna rzecz, którą zauważyłem. Wspomniałeś, że nie preferujesz metod anonimowych . Chciałeś powiedzieć, że nie lubisz anonimowych klasy .

 540
Author: Octavian Damiean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-19 09:22:45

Kiedy zobaczyłem najlepszą odpowiedź, uświadomiłem sobie, że moim problemem nie było umieszczenie parametru (Widok v) na metodzie fancy:

public void myFancyMethod(View v) {}

Podczas próby uzyskania dostępu z xml, należy użyć

android:onClick="myFancyMethod"/>
Mam nadzieję, że to komuś pomoże.
 75
Author: jp093121,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-22 01:45:17

android:onClick jest dla poziomu API 4, więc jeśli kierujesz

 68
Author: James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-24 10:50:08

Sprawdź, czy zapomniałeś upublicznić metodę!

 28
Author: Ruivo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-23 19:06:39

Podanie atrybutu android:onClick powoduje wewnętrzne wywołanie instancji Button. Stąd nie ma absolutnie żadnej różnicy.

Aby mieć jasne zrozumienie, zobaczmy, jak atrybut XML onClick jest obsługiwany przez framework.

Gdy plik układu jest napompowany, wszystkie widoki w nim określone są tworzone. W tym konkretnym przypadku instancja Button jest tworzona przy użyciu public Button (Context context, AttributeSet attrs, int defStyle) konstruktor. Wszystkie atrybuty znacznika XML są odczytywane z pakietu zasobów i przekazywane jako AttributeSet do konstruktora.

Button klasa jest dziedziczona z View klasy, co skutkuje wywołaniem konstruktora View, który zajmuje się ustawianiem obsługi wywołania kliknięcia za pomocą setOnClickListener.

Atrybut onClick zdefiniowany w attrs.xml , odnosi się w widoku.java as R.styleable.View_onClick.

Oto kod View.java to wykonuje większość pracy za Ciebie, dzwoniąc setOnClickListener sam.

 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;

Jak widać, setOnClickListener jest wywoływany do rejestracji wywołania zwrotnego, jako robimy to w naszym kodzie. Jedyną różnicą jest to, że używa Java Reflection do wywołania metody callback zdefiniowanej w naszej aktywności.

Oto powód problemów wymienionych w innych odpowiedziach:

  • metoda Callback powinna być Publiczna : ponieważ Java Class getMethod jest używany, wyszukiwane są tylko funkcje z public access specifier. W przeciwnym razie bądź gotowy do obsługi wyjątku IllegalAccessException.
  • podczas używania przycisku z onClick we fragmencie, callback powinien być zdefiniowany w Działalność : getContext().getClass().getMethod() call ogranicza wyszukiwanie metody do bieżącego kontekstu, czyli Activity w przypadku fragmentu. Stąd metoda jest przeszukiwana wewnątrz klasy Activity, a nie Klasy Fragment.
  • metoda Callback powinna przyjmować parametr View : ponieważ Java Class getMethod wyszukuje metodę, która przyjmuje View.class jako parametr.
 21
Author: Manish Mulimani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-18 13:07:21

Zauważ, że jeśli chcesz użyć funkcji onclick XML, odpowiednia metoda powinna mieć jeden parametr, którego typ powinien pasować do obiektu XML.

Na przykład, przycisk zostanie połączony z Twoją metodą poprzez łańcuch nazw: android:onClick="MyFancyMethod" ale deklaracja metody powinna pokazywać: ...MyFancyMethod(View v) {...

Jeśli próbujesz dodać tę funkcję do , będzie ona miała dokładnie tę samą składnię w pliku XML, ale twoja metoda zostanie zadeklarowana jako: ...MyFancyMethod(MenuItem mi) {...

 13
Author: Antoine Lizée,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-07 19:06:17

Są tu bardzo dobre odpowiedzi, ale chcę dodać jedną linijkę:

W android:onclick W XML, Android używa java reflection za sceną, aby sobie z tym poradzić.

Ijak wyjaśniono tutaj, odbicie zawsze spowalnia wydajność. (szczególnie na DHALVIK VM). Rejestracja onClickListener jest lepszym sposobem.

 11
Author: Krupal Shah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:59

W Javie 8 prawdopodobnie możesz użyć method Reference , aby osiągnąć to, czego chcesz.

Załóżmy, że jest to Twój onClick Obsługa zdarzenia dla przycisku.

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

Następnie przekazujesz referencję metody instancji onMyButtonClicked w wywołaniu setOnClickListener() w ten sposób.

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

To pozwoli Ci uniknąć jawnie definiowania anonimowej klasy przez siebie. Muszę jednak podkreślić, że odniesienie do metody Java 8 jest tak naprawdę tylko cukrem składniowym. Faktycznie tworzy instancję Klasa anonimowa dla ciebie (podobnie jak wyrażenie lambda) stąd podobna ostrożność jak w przypadku obsługi zdarzeń w stylu lambda-expression została zastosowana, gdy przychodzisz do niezastrzeżenia obsługi zdarzeń. Ten artykuł wyjaśnia to bardzo ładnie.

PS. Dla tych, którzy są ciekawi, jak naprawdę mogę korzystać z funkcji języka Java 8 W Androidzie, jest to dzięki uprzejmości retrolambda biblioteka.

 4
Author: onelaview,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-01 13:31:29

Innym sposobem ustawienia słuchaczy on click byłoby użycie XML. Wystarczy dodać atrybut android:onClick do tagu.

Dobrą praktyką jest używanie atrybutu XML "onClick" nad anonimową klasą Javy w miarę możliwości.

Po pierwsze, spójrzmy na różnicę w kodzie:

Atrybut XML / atrybut onClick

Porcja XML

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

Część Java

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

Anonimowa Klasa Java / setOnClickListener

Porcja XML

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

Część Java

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

Oto korzyści płynące z używania atrybutu XML nad anonimową klasą Java:

  • Z anonimową klasą Java zawsze musimy podać id dla naszego elementów, ale z atrybutem XML id można pominąć.
  • Z anonimową klasą Java musimy aktywnie szukać elementu wewnątrz widoku (część findViewById), ale z atrybutem XML Android robi to dla my.
  • Anonymous Java class wymaga co najmniej 5 linijek kodu, jak możemy zobacz, ale z atrybutem XML wystarczą 3 linijki kodu.
  • Z anonimową klasą Java musimy nazwać naszą metodę " onClick", ale za pomocą atrybutu XML możemy dodać dowolną nazwę, która będzie znacznie pomóc w czytelności naszego kodu.
  • atrybut XML "onClick" został dodany przez Google na poziomie API 4 Wydania, co oznacza, że jest to nieco nowocześniejsza składnia i nowoczesne składnia jest prawie zawsze lepsza.

Oczywiście nie zawsze jest możliwe użycie atrybutu Xml, oto powody, dla których nie wybralibyśmy go:

    Jeśli pracujemy z fragmentami. atrybut onClick można dodać tylko do działania, więc jeśli mamy fragment, będziemy musieli użyć anonimowe zajęcia.
  • jeśli chcemy przenieść słuchacza onClick do osobnej klasy (może jeśli jest to bardzo skomplikowane i / lub chcielibyśmy go ponownie wykorzystać w różne części naszej aplikacji), wtedy nie chcemy używać atrybut xml.
 4
Author: Bestin John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-11 08:36:23

Używając atrybutu XML wystarczy zdefiniować metodę zamiast klasy więc zastanawiałem się czy to samo można zrobić za pomocą kodu a nie w układ XML.

Tak, możesz wykonać fragment LUB activity zaimplementować View.OnClickListener

A po zainicjowaniu nowych obiektów widoku w kodzie możesz po prostu wykonać mView.setOnClickListener(this);

I automatycznie ustawia wszystkie obiekty widoku w kodzie tak, aby używały metody onClick(View v), którą ma twoja fragment LUB activity itd.

Aby odróżnić który Widok wywołał metodę onClick, możesz użyć instrukcji switch w metodzie v.getId().

Ta odpowiedź różni się od tej, która mówi "Nie, które nie jest możliwe za pomocą kodu"

 4
Author: CQM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-30 16:29:41

Wspierając odpowiedź Ruivo, tak, musisz zadeklarować metodę jako "publiczną", aby móc używać w Androidzie XML onclick - rozwijam kierowanie aplikacji z poziomu API 8 (minSdk...) do 16 (ok...).

Deklarowałem moją metodę jako prywatną i powodowała błąd, po prostu deklarowałem ją jako publiczną.

 3
Author: Waqas Hasan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-04 13:51:03
   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
Author: jeet parmar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-15 14:22:47

Załóżmy, że chcesz dodać zdarzenie kliknij Tak 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"/>

W pliku java musisz napisać metodę podobną do tej metody.

public void register(View view) {
}
 0
Author: nazrul islam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-10 09:10:52

Zapisuję ten kod w pliku 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"/>

I napisać ten kod fragmentem...

public void register(View view) {
}
 0
Author: user2786249,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-07 13:11:54

Najlepszym sposobem jest użycie następującego kodu:

 Button button = (Button)findViewById(R.id.btn_register);
 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //do your fancy method
            }
        });
 0
Author: Katarina Dabo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-14 19:55:40

Bądź ostrożny, chociaż android:onClick XML wydaje się być wygodnym sposobem obsługi kliknięć, implementacja setOnClickListener robi coś więcej niż dodanie onClickListener. Rzeczywiście, ustawia właściwość view clickable na true.

Chociaż może to nie być problemem w większości implementacji Androida, zgodnie z konstruktorem telefonu, przycisk jest zawsze domyślny dla clickable = true, ale inne konstruktory w niektórych modelach telefonu mogą mieć domyślny clickable = false w widokach innych niż przyciski.

Więc ustawienie XML jest nie wystarczy, trzeba myśleć cały czas, aby dodać android:clickable="true" na non przycisk, a jeśli masz urządzenie, gdzie domyślnie jest clickable = true i zapomnisz nawet raz umieścić ten atrybut XML, nie zauważysz problemu w czasie wykonywania, ale otrzymasz informacje zwrotne na rynku, kiedy będzie w rękach klientów !

Ponadto nigdy nie możemy być pewni, jak proguard zaciemni i zmieni nazwę atrybutów XML i metody klas, tak aby nie były w 100% bezpieczne, że nigdy nie będą miały błędu dzień.

Więc jeśli nigdy nie chcesz mieć problemów i nigdy o tym nie myśleć, lepiej użyć {[1] } lub bibliotek takich jak ButterKnife z adnotacją @OnClick(R.id.button)

 0
Author: Livio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-25 15:19:18