Chiama il metodo Activity dall'adattatore


119

È possibile chiamare un metodo definito in Activityfrom ListAdapter?

(Voglio fare un Buttonin list'sfila e quando questo pulsante viene premuto, si deve eseguire il metodo, che è definito in corrispondenti attività. Ho cercato di serie onClickListenernel mio ListAdapter, ma non so come chiamare questo metodo, qual è il suo percorso. ..)

quando l'ho usato Activity.this.method()ottengo il seguente errore:

No enclosing instance of the type Activity is accessible in scope

Qualche idea ?


non puoi chiamare activity.this in qualche altra classe a meno che non sia una classe interna a quell'attività. segui la soluzione @Eldhose M Babu per il tuo caso
Archie.bpgc

Risposte:


306

Si, puoi.

Nell'adattatore Aggiungi un nuovo campo:

private Context mContext;

Nel costruttore dell'adattatore aggiungere il codice seguente:

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

Nel getView (...) di Adapter:

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

sostituisci con i tuoi nomi di classe in cui vedi il tuo codice, la tua attività ecc.

Se è necessario utilizzare lo stesso adattatore per più di un'attività, allora:

Crea un'interfaccia

public interface IMethodCaller {
    void yourDesiredMethod();
}

Implementa questa interfaccia nelle attività necessarie per avere questa funzionalità di chiamata del metodo.

Quindi in Adapter getView (), chiama come:

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

Hai fatto. Se è necessario utilizzare questo adattatore per attività che non richiedono questo meccanismo di chiamata, il codice non verrà eseguito (se il controllo non riesce).


29
Poiché si tratta di una domanda popolare, suggerisco caldamente di NON USARE questa soluzione. Dovresti evitare il casting di classe qui, perché questo potrebbe portare a eccezioni di runtime.
Igor Filippov

3
Eldhose fratello, non ho niente contro di te, ma la risposta di seguito è la migliore pratica. Puoi persino leggere nei commenti. Non dovresti eseguire il cast di mContext nella tua attività poiché stai evitando il riutilizzo del codice. Ora questo adattatore può essere utilizzato solo all'interno dell'attività in cui è stato eseguito il cast della variabile mContext, dove se è stato definito un listener, è possibile riutilizzare lo stesso adattatore in un'altra attività. Credimi, ho passato abbastanza tempo nell'industria e sto solo cercando di aiutarti qui, per favore non prenderla sul personale.
Varundroid

2
Questa soluzione è una delle migliori che abbia mai trovato su SO. Quindi grazie mille signor Babu. Chiamando i metodi dell'attività in questo modo, l'intera app aumenta del 500% le prestazioni -> Non ho bisogno di ricaricare il database nell'adattatore, a cui ho bisogno di accedere. Non mi interessano gli "anni nell'industria", cerco soluzioni stabili e funzionanti e sono abbastanza sicuro di non trovare un modo migliore per accedervi. Forse potrei impostare il DB come singleton, ma non è questo il modo in cui voglio "destrutturare" il mio progetto.
Martin Pfeffer

2
@RAM se è necessario chiamare lo stesso metodo in più di un'attività, è necessario creare un'interfaccia con quel nome di metodo. Quindi implementare quell'interfaccia in tutte le attività necessarie per utilizzare la chiamata al metodo. Quindi, nel listener di clic, controlla l'istanza della tua interfaccia anziché utilizzare il nome dell'attività.
Eldhose M Babu

1
Ottima risposta. Thumbs up
Alok Rajasukumaran

126

Puoi farlo in questo modo:

Dichiara l'interfaccia:

public interface MyInterface{
    public void foo();
}

Lascia che la tua attività lo realizzi:

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

Quindi passa la tua attività a ListAdater:

public MyAdapter extends BaseAdater{
    private MyInterface listener;

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

E da qualche parte nell'adattatore, quando è necessario chiamare quel metodo Activity:

listener.foo();

5
Ho pensato per la prima volta al metodo sopra come faresti con i frammenti, ma questa è la soluzione migliore!
brux

1
qual è il mio InterfaceListener? come posso inizializzarlo? public MyAdapter (MyInterface listener) {this.listener = listener; }
Gilberto Ibarra

6
Come si passa l'interfaccia all'adattatore però (dall'attività)
Brandon

1
@Brandon puoi passarlo è il parametro del costruttore nell'adattatore.
Igor Filippov

5
@Brandon, aggiungi semplicemente "this" per passare l'interfaccia all'adattatore myAdapter = new MyAdapter (this);
ajinkya

67

Originale:

Capisco la risposta attuale ma avevo bisogno di un esempio più chiaro. Ecco un esempio di ciò che ho usato con un Adapter(RecyclerView.Adapter) e un file Activity.

Nella tua attività:

Questo implementerà interfaceciò che abbiamo nel nostro Adapter. In questo esempio, verrà chiamato quando l'utente fa clic su un elemento nel file 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);
    }
}

Nel tuo adattatore:

In Activity, abbiamo avviato il nostro Adaptere lo abbiamo passato come argomento al costruttore. Questo avvierà il interfacenostro metodo di callback. Come puoi vedere, utilizziamo il nostro metodo di callback per i clic degli utenti.

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();
    }
}

6
grazie per questo, è esattamente quello che stavo cercando. votato
positivamente

questo ha funzionato per me! imo, il codice funzionante più completo finora!
publicknowledge

2
Grazie per questa soluzione
Steve

4
questa risposta dovrebbe avere più voti positivi, è una soluzione pulita e perfetta.
Opiatefuchs

Ciao @ Jared - Puoi correggere lo stesso campione con EventBusquello che hai suggerito?
Tohid,

15

Di base e semplice.

Nel tuo adattatore usa semplicemente questo.

((YourParentClass) context).functionToRun();


4
non è una buona pratica perché stai limitando la tua classe a lavorare solo con il tipo YourParentClass invece di qualsiasi classe, ma funziona se non ti interessa riutilizzarla, se rinomini anche una classe il codice si rompe ...
Gustavo Baiocchi Costa

7

Un altro modo è:

Scrivi un metodo nel tuo adattatore diciamo public void callBack () {}.

Ora, durante la creazione di un oggetto per l'adattatore in attività, sovrascrivere questo metodo. Il metodo Override verrà chiamato quando si chiama il metodo nell'adattatore.

Myadapter adapter = new Myadapter() {
  @Override
  public void callBack() {
    // dosomething
  }
};

5

Per Kotlin:

Nel tuo adattatore, chiama semplicemente

(context as Your_Activity_Name).yourMethod()

0
if (parent.getContext() instanceof yourActivity) {
  //execute code
}

questa condizione ti consentirà di eseguire qualcosa se l'Attività che ha le GroupViewviste richieste dal getView()tuo metodo adapterèyourActivity

NOTA: parentè quel GroupView


questo GroupView richiede viste dall'adattatore quando chiama il getView()metodo ..... quindi otteniamo il contesto di quel GroupView e lo esaminiamo attraverso la condizione sopra
Abd Salam Baker

0

In Kotlin ora esiste un modo più pulito utilizzando le funzioni lambda, senza bisogno di interfacce:

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) {

    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.