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.
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 .
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.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
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ę!
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ątkuIllegalAccessException
. -
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 przyjmujeView.class
jako parametr.
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) {...
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.
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.
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.
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"
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ą.
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;
}}
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) {
}
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) {
}
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
}
});
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)
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