Android: jak obsługiwać kliknięcie przycisku


97

Mając solidne doświadczenie w obszarach innych niż Java i non-Android, uczę się Androida.

Mam wiele zamieszania w różnych obszarach, jednym z nich jest obsługa kliknięć przycisków. Są na to co najmniej 4 sposoby (!!!), są one pokrótce wymienione tutaj

dla spójności wymienię je:

  1. Miej członka View.OnClickListenerklasy w działaniu i przypisz go do instancji, która będzie obsługiwać onClicklogikę w onCreatemetodzie działania.

  2. Utwórz „onClickListener” w metodzie działania „onCreate” i przypisz go do przycisku za pomocą setOnClickListener

  3. Zaimplementuj „onClickListener” w samym działaniu i przypisz „this” jako odbiornik przycisku. W przypadku, gdy aktywność ma kilka przycisków, identyfikator przycisku należy przeanalizować w celu wykonania procedury obsługi „onClick” dla odpowiedniego przycisku

  4. Miej metodę publiczną na działaniu, które implementuje logikę `` onClick '' i przypisz ją do przycisku w deklaracji XML aktywności

Pytanie 1:

Czy to wszystkie metody, czy jest jakaś inna opcja? (Nie potrzebuję innego, po prostu ciekawy)

Dla mnie najbardziej intuicyjny byłby ten ostatni: wymaga wpisania najmniejszej ilości kodu i jest najbardziej czytelny (przynajmniej dla mnie).

Chociaż nie widzę szerokiego stosowania tego podejścia. Jakie są wady jej używania?

Pytanie 2:

Jakie są wady / zalety każdej z tych metod? Podziel się swoimi doświadczeniami lub dobrym linkiem.

Wszelkie uwagi są mile widziane!

PS Próbowałem znaleźć coś na ten temat w Google, ale jedyne co znalazłem to opis „jak” to zrobić, a nie dlaczego jest to dobre lub złe.

Odpowiedzi:


149

Pytanie 1: Niestety ten, o którym mówisz, że jest najbardziej intuicyjny, jest najmniej używany w systemie Android. Jak rozumiem, powinieneś oddzielić swój interfejs użytkownika (XML) i funkcje obliczeniowe (pliki klas Java). Ułatwia również debugowanie. W ten sposób o wiele łatwiej jest czytać i myśleć o Android imo.

Pytanie 2: Myślę, że dwa najczęściej używane to nr 2 i nr 3. Jako przykładu użyję przycisku clickButton.

2

ma postać anonimowej klasy.

Button clickButton = (Button) findViewById(R.id.clickButton);
clickButton.setOnClickListener( new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                ***Do what you want with the click here***
            }
        });

Jest to moje ulubione, ponieważ ma metodę onClick tuż obok miejsca, w którym zmienna przycisku została ustawiona za pomocą findViewById. Wydaje się bardzo schludne i uporządkowane, że wszystko, co dotyczy tego widoku przycisku ClickButton, znajduje się tutaj.

Wadą, którą komentuje mój współpracownik, jest to, że wyobraź sobie, że masz wiele widoków, które wymagają słuchacza onclick. Możesz zobaczyć, że Twój onCreate będzie bardzo długi. Dlatego lubi używać:

3

Powiedzmy, że masz, 5 przycisków kliknięć:

Upewnij się, że Twoja aktywność / fragment implementuje OnClickListener

// in OnCreate

Button mClickButton1 = (Button)findViewById(R.id.clickButton1);
mClickButton1.setOnClickListener(this);
Button mClickButton2 = (Button)findViewById(R.id.clickButton2);
mClickButton2.setOnClickListener(this);
Button mClickButton3 = (Button)findViewById(R.id.clickButton3);
mClickButton3.setOnClickListener(this);
Button mClickButton4 = (Button)findViewById(R.id.clickButton4);
mClickButton4.setOnClickListener(this);
Button mClickButton5 = (Button)findViewById(R.id.clickButton5);
mClickButton5.setOnClickListener(this);


// somewhere else in your code

public void onClick(View v) {
    switch (v.getId()) {
        case  R.id.clickButton1: {
            // do something for button 1 click
            break;
        }

        case R.id.clickButton2: {
            // do something for button 2 click
            break;
        }

        //.... etc
    }
}

W ten sposób, jak wyjaśnia mój współpracownik, jest ładniejszy w jego oczach, ponieważ wszystkie obliczenia onClick są obsługiwane w jednym miejscu i nie zajmują miejsca w metodzie onCreate. Ale wadą, którą widzę, jest to, że:

  1. same poglądy,
  2. a każdy inny obiekt, który może znajdować się w onCreate używany przez metodę onClick, będzie musiał zostać przekształcony w pole.

Daj mi znać, jeśli chcesz uzyskać więcej informacji. Nie odpowiedziałem w pełni na twoje pytanie, ponieważ jest to dość długie pytanie. A jeśli znajdę jakieś strony, poszerzę swoją odpowiedź, teraz tylko daję trochę doświadczenia.


1
W przypadku opcji 2 zechcesz to zrobić: clickButton.setOnClickListener (new View.OnClickListener () {@Override public void onClick (View v) {// TODO co chcesz zrobić}}); aby pomóc rozwiązać problem OnClickListener
ColossalChris

Opcja 3 jest prawdopodobnie najczystsza i najłatwiejsza do rozszerzenia za pomocą wzorca MVP.
Raffaeu

Opcja 2 nadal może przynieść onCreate()niezbyt długie wyniki. Przypisania nasłuchiwania kliknięć i klasy anonimowe można rozłożyć na oddzielną metodę pomocniczą, która jest wywoływana z onCreate().
Nick Alexeev

@Colossal: Nie musisz tego robić. Dodaj rozszerzenie do klasy działań, takie jak „implementuje View.OnClickListener”.
TomeeNS

11

# 1 Często używam ostatniego, gdy mam przyciski w układzie, które nie są generowane (ale oczywiście statyczne).

Jeśli używasz go w praktyce i aplikacji biznesowej, zwróć tutaj szczególną uwagę, ponieważ podczas korzystania z zaciemniacza źródła takiego jak ProGuard, będziesz musiał zaznaczyć te metody w swojej aktywności, aby nie były zaciemniane.

Aby zarchiwizować pewnego rodzaju zabezpieczenia w czasie kompilacji przy użyciu tego podejścia, spójrz na Androida Lint ( przykład ).


# 2 Wady i zalety wszystkich metod są prawie takie same, a lekcja powinna brzmieć:

Używaj tego, co jest najbardziej odpowiednie lub wydaje się najbardziej intuicyjne.

Jeśli musisz przypisać to samo OnClickListenerdo wielu instancji przycisku, zapisz go w zakresie klasy (# 1). Jeśli potrzebujesz prostego detektora dla przycisku, wykonaj anonimową implementację:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // Take action.
    }
});

Zwykle nie implementuję tego OnClickListenerw działaniu, co czasami robi się trochę zagmatwane (szczególnie, gdy implementujesz wiele innych programów obsługi zdarzeń i nikt nie wie, co thiswszystko robi).


Podążam za tym samym, ale nadal nie otrzymuję danych wyjściowych dla funkcji, mój kod i zapytanie jest tutaj: stackoverflow.com/questions/25107427/ ...
Rocket

8

Wolę opcję 4, ale ma to dla mnie intuicyjny sens, ponieważ wykonuję zdecydowanie za dużo pracy w Grails, Groovy i JavaFX. „Magiczne” połączenia między widokiem a kontrolerem są u wszystkich wspólne. Ważne jest, aby dobrze nazwać metodę:

W widoku dodaj metodę onClick do przycisku lub innego widżetu:

    android:clickable="true"
    android:onClick="onButtonClickCancel"

Następnie w klasie obsłuż metodę:

public void onButtonClickCancel(View view) {
    Toast.makeText(this, "Cancel pressed", Toast.LENGTH_LONG).show();
}

Ponownie, wyraźnie nazwij metodę, coś, co i tak powinieneś zrobić, a konserwacja stanie się drugą naturą.

Dużą zaletą jest to, że teraz możesz napisać testy jednostkowe dla metody. Opcja 1 może to zrobić, ale 2 i 3 są trudniejsze.


1
Zamierzam trochę pomyśleć i zasugerować piątą opcję (nie, bez Bruce'a Willisa :)), wariant opcji 2: użyj klasy Presenter w modelu Model-View-Presenter do obsługi kliknięć. ZNACZNIE ułatwia to automatyczne testowanie. Sprawdź ten link, aby uzyskać lepsze informacje: codelabs.developers.google.com/codelabs/android-testing/ ...
Steve Gelman

4

Najczęściej używanym sposobem jest anonimowa deklaracja

    Button send = (Button) findViewById(R.id.buttonSend);
    send.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // handle click
        }
    });

Możesz również utworzyć obiekt View.OnClickListener i ustawić go na przycisk później, ale nadal musisz na przykład zastąpić metodę onClick

View.OnClickListener listener = new View.OnClickListener(){
     @Override
        public void onClick(View v) {
            // handle click
        }
}   
Button send = (Button) findViewById(R.id.buttonSend);
send.setOnClickListener(listener);

Kiedy Twoja aktywność implementuje interfejs OnClickListener, musisz przesłonić metodę onClick (View v) na poziomie aktywności. Następnie możesz przypisać tę aktywność jako odbiornik do przycisku, ponieważ już implementuje interfejs i zastępuje metodę onClick ()

public class MyActivity extends Activity implements View.OnClickListener{


    @Override
    public void onClick(View v) {
        // handle click
    }


    @Override
    public void onCreate(Bundle b) {
        Button send = (Button) findViewById(R.id.buttonSend);
        send.setOnClickListener(this);
    }

}

(imho) Czwarte podejście używane, gdy wiele przycisków ma tę samą procedurę obsługi i możesz zadeklarować jedną metodę w klasie aktywności i przypisać tę metodę do wielu przycisków w układzie XML, również możesz utworzyć jedną metodę dla jednego przycisku, ale w tym przypadku ja wolą deklarować procedury obsługi wewnątrz klasy aktywności.


1

Opcja 1 i 2 polega na użyciu klasy wewnętrznej, która spowoduje bałagan w kodzie. Opcja 2 jest trochę niechlujna, ponieważ dla każdego przycisku będzie jeden odbiornik. Jeśli masz małą liczbę przycisków, to jest w porządku. W przypadku opcji 4 myślę, że będzie to trudniejsze do debugowania, ponieważ będziesz musiał cofnąć się i czwarty kod XML i Java. Osobiście używam opcji 3, gdy muszę obsługiwać wiele kliknięć przycisków.


1

Moja próbka, przetestowana w Android Studio 2.1

Zdefiniuj przycisk w układzie XML

<Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Wykrywanie pulsacji Java

Button clickButton = (Button) findViewById(R.id.btn1);
if (clickButton != null) {
    clickButton.setOnClickListener( new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            /***Do what you want with the click here***/
        }
    });
}

1

Aby ułatwić sprawę, asp Pytanie 2 stwierdzone, możesz użyć metody lambda takiej jak ta, aby zaoszczędzić pamięć zmiennej i uniknąć poruszania się w górę iw dół w klasie widoku

//method 1
findViewById(R.id.buttonSend).setOnClickListener(v -> {
          // handle click
});

ale jeśli chcesz od razu zastosować zdarzenie click do swojego przycisku w metodzie.

możesz skorzystać z pytania 3 autorstwa @D. Odpowiedź tran. Ale nie zapomnij zaimplementować swojej klasy widoku z View.OnClickListener.

Innymi słowy, aby poprawnie użyć pytania nr 3


1
Należy to uznać za nowoczesną odpowiedź w połączeniu z odniesieniami do metod IMO. Większość innych odpowiedzi nie wyjaśnia faktu, że są to stare kody sprzed Java8 na Androida.
Ryan The Leach

0

Pytanie nr 1 - to jedyny sposób obsługi kliknięć widoku.

Pytanie 2 -
Opcja nr 1 / Opcja nr 4 - Nie ma dużej różnicy między opcją nr 1 a opcją nr 4. Jedyną różnicą, jaką widzę, jest to, że w jednym przypadku działanie polega na implementacji OnClickListener, podczas gdy w drugim przypadku byłaby anonimowa implementacja.

Opcja nr 2 - W tej metodzie zostanie wygenerowana klasa anonimowa. Ta metoda jest nieco uciążliwa, ponieważ musiałbyś to zrobić wiele razy, jeśli masz wiele przycisków. W przypadku klas anonimowych musisz uważać na obsługę wycieków pamięci.

Opcja nr 3 - Chociaż jest to łatwy sposób. Zazwyczaj programiści starają się nie używać żadnej metody, dopóki jej nie napiszą, dlatego ta metoda nie jest szeroko stosowana. Można zobaczyć, że większość ludzi korzysta z opcji nr 4. Ponieważ jest czystszy pod względem kodu.


Cześć Gaurav, dziękuję za odpowiedź. Ale czy możesz wyjaśnić, co masz na myśli: w przypadku klas anonimowych musisz uważać na wycieki pamięci. Jak dochodzi do wycieków pamięci?
Budda,

Musisz tylko mieć świadomość, że: jeśli utworzysz anonimową klasę wewnątrz metody, która może być wywoływana wiele razy w czasie istnienia Twojej aplikacji, nie zostanie utworzonych kilka wystąpień jednej klasy, ale kilka klas, w tym ich wystąpienia. Możesz tego uniknąć, używając zwykłych klas wewnętrznych i tworząc instancje detektorów jako pól instancji. Spróbuj zredukować różne klasy słuchaczy, uświadamiając stan słuchacza za pomocą argumentów konstruktora. Zwykła klasa wewnętrzna zapewnia korzyści wynikające z niestandardowych konstruktorów i innych metod.
Risadinha

0

Dostępne są również opcje w postaci różnych bibliotek, które mogą sprawić, że proces ten będzie bardzo znany osobom, które używały innych frameworków MVVM.

https://developer.android.com/topic/libraries/data-binding/

Pokazuje przykład oficjalnej biblioteki, która pozwala na wiązanie przycisków w następujący sposób:

<Button
    android:text="Start second activity"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="@{() -> presenter.showList()}"
/>

0

Krok 1: Utwórz plik XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btnClickEvent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me" />
</LinearLayout>

Krok 2: Utwórz MainActivity:

package com.scancode.acutesoft.telephonymanagerapp;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity implements View.OnClickListener {

    Button btnClickEvent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnClickEvent = (Button) findViewById(R.id.btnClickEvent);
        btnClickEvent.setOnClickListener(MainActivity.this);

    }

    @Override
    public void onClick(View v) {
        //Your Logic
    }
}

HappyCoding!

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.