Metoda działania wywołania z adaptera

119

Czy można wywołać metodę zdefiniowaną w Activityfrom ListAdapter?

(Chcę zrobić wiersz Buttonw list'swierszu i po kliknięciu tego przycisku powinien wykonać metodę, która jest zdefiniowana w odpowiednim działaniu. Próbowałem ustawić onClickListenerw moim, ListAdapterale nie wiem, jak wywołać tę metodę, jaka jest jej ścieżka. ..)

kiedy użyłem Activity.this.method(), otrzymuję następujący błąd:

No enclosing instance of the type Activity is accessible in scope

Dowolny pomysł ?

user1602687
źródło
nie możesz nazwać Activity.this w jakiejś innej klasie, chyba że jest to klasa wewnętrzna tej aktywności. śledź rozwiązanie @Eldhose M Babu dla swojej sprawy
Archie.bpgc

Odpowiedzi:

306

Tak, możesz.

W adapterze Dodaj nowe pole:

private Context mContext;

W konstruktorze adaptera dodaj następujący kod:

public AdapterName(......, Context context) {
  //your code.
  this.mContext = context;
}

W getView (...) adaptera:

Button btn = (Button) convertView.findViewById(yourButtonId);
btn.setOnClickListener(new Button.OnClickListener() {
  @Override
  public void onClick(View v) {
    if (mContext instanceof YourActivityName) {
      ((YourActivityName)mContext).yourDesiredMethod();
    }
  }
});

zastąp własnymi nazwami klas, w których widzisz swój kod, aktywność itp.

Jeśli potrzebujesz użyć tego samego adaptera do więcej niż jednej czynności, to:

Utwórz interfejs

public interface IMethodCaller {
    void yourDesiredMethod();
}

Zaimplementuj ten interfejs w działaniach, których potrzebujesz, aby mieć tę funkcję wywoływania metody.

Następnie w adapterze getView () wywołaj:

Button btn = (Button) convertView.findViewById(yourButtonId);
btn.setOnClickListener(new Button.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (mContext instanceof IMethodCaller) {
            ((IMethodCaller) mContext).yourDesiredMethod();
        }
    }
});

Skończyłeś. Jeśli potrzebujesz użyć tego adaptera do działań, które nie wymagają tego mechanizmu wywołującego, kod nie zostanie wykonany (jeśli sprawdzenie nie powiedzie się).

Eldhose M Babu
źródło
29
Ponieważ jest to popularne pytanie, zdecydowanie radzę NIE UŻYWAĆ tego rozwiązania. W tym miejscu należy unikać rzutowania klas, ponieważ może to prowadzić do wyjątków w czasie wykonywania.
Igor Filippov
3
Eldhose bro, nie mam nic przeciwko tobie, ale odpowiedź poniżej to najlepsza praktyka. Możesz nawet czytać w komentarzach. Nie powinieneś rzutować mContext na swoje działanie, ponieważ unikasz ponownego użycia kodu. Teraz ten adapter może być używany tylko w działaniu, do którego rzutowano zmienną mContext, gdzie jeśli masz zdefiniowany detektor, możesz ponownie użyć tego samego adaptera w innym działaniu. Zaufaj mi, spędziłem wystarczająco dużo czasu w przemyśle i po prostu próbuję ci tutaj pomóc, proszę, nie bierz tego do siebie.
Varundroid
2
To jedno z najlepszych rozwiązań, jakie kiedykolwiek znalazłem w SO. Wielkie dzięki, panie Babu. Wywołanie w ten sposób metod działania daje całej aplikacji wzrost wydajności o 500% -> Nie muszę ponownie ładować bazy danych w adapterze, do którego muszę uzyskać dostęp. Nie obchodzą mnie „lata w branży”, szukam stabilnych i działających rozwiązań i jestem pewien, że nie znajdę lepszego sposobu na dostęp. Może mógłbym ustawić DB jako singleton, ale to nie jest sposób, w jaki chcę „usuwać strukturę” mojego projektu.
Martin Pfeffer
2
@RAM Jeśli potrzebujesz wywołać tę samą metodę w więcej niż jednym działaniu, musisz utworzyć interfejs o tej nazwie metody. Następnie zaimplementuj ten interfejs we wszystkich działaniach, których potrzebujesz, aby skorzystać z wywołania metody. Następnie w odbiorniku kliknięć sprawdź instancję interfejsu zamiast używać nazwy działania.
Eldhose M Babu
1
Doskonała odpowiedź. Kciuki w górę
Alok Rajasukumaran
126

Możesz to zrobić w ten sposób:

Zadeklaruj interfejs:

public interface MyInterface{
    public void foo();
}

Pozwól swojej aktywności to zrealizować:

public class MyActivity extends Activity implements MyInterface{
    public void foo(){
        //do stuff
    }
}

Następnie przekaż swoją aktywność do ListAdater:

public MyAdapter extends BaseAdater{
    private MyInterface listener;

    public MyAdapter(MyInterface listener){
        this.listener = listener;
    }
}

I gdzieś w adapterze, kiedy musisz wywołać tę metodę Activity:

listener.foo();
Igor Filippov
źródło
5
Na początku pomyślałem o powyższej metodzie, tak jak o fragmentach, ale to jest najlepsze rozwiązanie!
brux
1
co to jest mój InterfaceListener? jak można to zainicjować? public MyAdapter (odbiornik MyInterface) {this.listener = listener; }
Gilberto Ibarra
6
Jak jednak przekazać interfejs do adaptera (z ćwiczenia)
Brandon
1
@Brandon możesz przekazać go jako parametr konstruktora w adapterze.
Igor Filippov
5
@Brandon, po prostu dodaj „this”, aby przekazać interfejs do adaptera myAdapter = new MyAdapter (this);
ajinkya
67

Oryginalny:

Rozumiem obecną odpowiedź, ale potrzebowałem wyraźniejszego przykładu. Oto przykład tego, co użyłem z Adapter(RecyclerView.Adapter) i plikiem Activity.

W Twojej aktywności:

Spowoduje to wdrożenie tego interface, co mamy w naszym Adapter. W tym przykładzie zostanie wywołana, gdy użytkownik kliknie element w RecyclerView.

public class MyActivity extends Activity implements AdapterCallback {

    private MyAdapter myAdapter;

    @Override
    public void onMethodCallback() {
       // do something
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        myAdapter = new MyAdapter(this);
    }
}

W Twoim adapterze:

W Activity, zainicjowaliśmy nasz Adapteri przekazaliśmy to jako argument konstruktorowi. Spowoduje to zainicjowanie naszej interfacemetody wywołania zwrotnego. Możesz zobaczyć, że używamy naszej metody wywołania zwrotnego dla kliknięć użytkowników.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private AdapterCallback adapterCallback;

    public MyAdapter(Context context) {
        try {
            adapterCallback = ((AdapterCallback) context);
        } catch (ClassCastException e) {
            throw new ClassCastException("Activity must implement AdapterCallback.", e);
        }
    }

    @Override
    public void onBindViewHolder(MyAdapter.ViewHolder viewHolder, int position) {
        // simple example, call interface here
        // not complete
        viewHolder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    adapterCallback.onMethodCallback();
                } catch (ClassCastException e) {
                   // do something
                }
            }
        });
    }

    public static interface AdapterCallback {
        void onMethodCallback();
    }
}
Jared Burrows
źródło
6
dzięki za to, właśnie tego szukałem. głosowanie w górę
Nactus
to działało dla mnie! imo, jak dotąd najbardziej kompletny działający kod!
publicknowledge
2
Dzięki za to rozwiązanie
Steve
4
ta odpowiedź powinna mieć więcej głosów pozytywnych, to czyste i doskonałe rozwiązanie.
Opiatefuchs
Cześć @Jared - Czy możesz poprawić tę samą próbkę, EventBusjak sugerowałeś?
Tohid
15

Podstawowe i proste.

W swoim adapterze po prostu użyj tego.

((YourParentClass) context).functionToRun();

Ck Maurya
źródło
4
niezbyt dobra praktyka, ponieważ ograniczasz swoją klasę do pracy tylko z typem YourParentClass zamiast z jakąkolwiek klasą, ale działa, jeśli nie zależy ci na ponownym użyciu, jeśli zmienisz również nazwę klasy, kod się zepsuje ...
Gustavo Baiocchi Costa
7

Jeszcze jeden sposób:

Napisz metodę w swoim adapterze, powiedzmy public void callBack () {}.

Teraz podczas tworzenia obiektu dla adaptera w aktywności należy zastąpić tę metodę. Metoda Override zostanie wywołana po wywołaniu metody w adapterze.

Myadapter adapter = new Myadapter() {
  @Override
  public void callBack() {
    // dosomething
  }
};
Prashanth Debbadwar
źródło
5

Kotlin:

Wystarczy zadzwonić w adapterze

(context as Your_Activity_Name).yourMethod()
Numair
źródło
0
if (parent.getContext() instanceof yourActivity) {
  //execute code
}

ten warunek umożliwi ci wykonanie czegoś, jeśli działanie, które ma GroupViewżądające widoki z getView()twojej metody, adaptertoyourActivity

UWAGA: parentczy to GroupView

Abd Salam Baker
źródło
ten GroupView żąda widoków od adaptera, gdy wywołuje getView()metodę ..... więc uzyskujemy kontekst tego GroupView i badamy go za pomocą powyższego warunku
Abd Salam Baker
0

W Kotlinie jest teraz bardziej przejrzysty sposób dzięki wykorzystaniu funkcji lambda, bez konieczności stosowania interfejsów:

class MyAdapter(val adapterOnClick: (Any) -> Unit) {
    fun setItem(item: Any) {
        myButton.setOnClickListener { adapterOnClick(item) }
    }
}

class MyActivity {
    override fun onCreate(savedInstanceState: Bundle?) {
        var myAdapter = MyAdapter { item -> doOnClick(item) }
    }


    fun doOnClick(item: Any) {

    }
}
nóż
źródło