Appeler la méthode d'activité à partir de l'adaptateur

119

Est-il possible d'appeler une méthode définie dans Activityfrom ListAdapter?

(Je veux faire Buttonen list'sligne et lorsque ce bouton est cliqué, il est conseillé d' effectuer la méthode, qui est définie dans l' activité correspondante. J'essayé de mettre onClickListenerdans mon ListAdaptermais je ne sais pas comment appeler cette méthode, ce qui est son chemin. ..)

quand j'ai utilisé, Activity.this.method()j'obtiens l'erreur suivante:

No enclosing instance of the type Activity is accessible in scope

Une idée ?

user1602687
la source
vous ne pouvez pas appeler activity.this dans une autre classe à moins qu'il ne s'agisse d'une classe interne à cette activité. Suivez @Eldhose M Babu solution pour votre cas
Archie.bpgc

Réponses:

306

Oui, vous pouvez.

Dans l'adaptateur, ajoutez un nouveau champ:

private Context mContext;

Dans le constructeur de l'adaptateur, ajoutez le code suivant:

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

Dans le getView (...) de l'adaptateur:

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

remplacez par vos propres noms de classe là où vous voyez votre code, votre activité, etc.

Si vous devez utiliser ce même adaptateur pour plusieurs activités, alors:

Créer une interface

public interface IMethodCaller {
    void yourDesiredMethod();
}

Implémentez cette interface dans les activités dont vous avez besoin pour avoir cette fonctionnalité d'appel de méthode.

Ensuite, dans Adapter getView (), appelez comme:

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

Vous avez terminé. Si vous devez utiliser cet adaptateur pour des activités qui ne nécessitent pas ce mécanisme d'appel, le code ne s'exécutera pas (si la vérification échoue).

Eldhose M Babu
la source
29
Comme il s'agit d'une question populaire, je suggère fortement de NE PAS UTILISER cette solution. Vous devez éviter le cast de classe ici, car cela pourrait entraîner des exceptions d'exécution.
Igor Filippov
3
Eldhose bro, je n'ai rien contre vous mais la réponse ci-dessous est la meilleure pratique. Vous pouvez même lire dans les commentaires. Vous ne devez pas convertir mContext dans votre activité car vous évitez la réutilisation du code. Désormais, cet adaptateur ne peut être utilisé que dans l'activité vers laquelle vous avez converti votre variable mContext, où si vous avez défini un écouteur, vous pouvez réutiliser le même adaptateur dans une autre activité. Croyez-moi, j'ai passé suffisamment de temps dans l'industrie et j'essaie simplement de vous aider ici, ne le prenez pas personnellement.
Varundroid
2
Cette solution est l'une des meilleures que j'ai jamais trouvées sur SO. Merci beaucoup, M. Babu. Appeler les méthodes de l'activité de cette manière donne à l'ensemble de l'application une augmentation de 500% des performances -> Je n'ai pas besoin de recharger la base de données dans l'adaptateur, auquel j'ai besoin d'accéder. Je me fiche des "années dans l'industrie", je recherche des solutions stables et fonctionnelles et je suis sûr que je ne trouverai pas de meilleur moyen d'y accéder. Peut-être que je pourrais configurer la base de données en tant que singleton, mais ce n'est pas comme ça que je veux "dé-structurer" mon projet.
Martin Pfeffer
2
@RAM si vous devez appeler la même méthode dans plusieurs activités, vous devez créer une interface avec ce nom de méthode. Ensuite, implémentez cette interface dans toutes les activités dont vous avez besoin que l'appel de méthode soit utilisé. Ensuite, dans l'écouteur de clic, vérifiez l'instance de votre interface plutôt que d'utiliser le nom de l'activité.
Eldhose M Babu
1
Superbe réponse. Thumbs up
Alok Rajasukumaran
126

Vous pouvez le faire de cette façon:

Déclarer l'interface:

public interface MyInterface{
    public void foo();
}

Laissez votre activité l'imlement:

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

Puis transmettez votre activité à ListAdater:

public MyAdapter extends BaseAdater{
    private MyInterface listener;

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

Et quelque part dans l'adaptateur, lorsque vous devez appeler cette méthode Activity:

listener.foo();
Igor Filippov
la source
5
J'ai d'abord pensé à la méthode ci-dessus comme vous le feriez avec des fragments mais c'est la meilleure solution!
brux
1
quel est mon InterfaceListener? comment l'initialiser? public MyAdapter (MyInterface listener) {this.listener = listener; }
Gilberto Ibarra
6
Comment passez-vous l'interface à l'adaptateur (à partir de l'activité)
Brandon
1
@Brandon, vous pouvez lui transmettre son paramètre de constructeur dans l'adaptateur.
Igor Filippov
5
@Brandon, ajoutez simplement «this» pour passer l'interface à l'adaptateur myAdapter = new MyAdapter (this);
ajinkya
67

Original:

Je comprends la réponse actuelle mais j'avais besoin d'un exemple plus clair. Voici un exemple de ce que j'ai utilisé avec un Adapter(RecyclerView.Adapter) et un Activity.

Dans votre activité:

Cela implémentera le interfaceque nous avons dans notre Adapter. Dans cet exemple, il sera appelé lorsque l'utilisateur cliquera sur un élément dans le 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);
    }
}

Dans votre adaptateur:

Dans le Activity, nous avons lancé notre Adapteret l' avons passé en argument au constructeur. Cela lancera interfacenotre méthode de rappel. Vous pouvez voir que nous utilisons notre méthode de rappel pour les clics des utilisateurs.

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
la source
6
merci pour cela, c'est exactement ce que je cherchais. voté
Nactus
cela a fonctionné pour moi! imo, le code de travail le plus complet à ce jour!
publicknowledge
2
Merci pour cette solution
Steve
4
cette réponse devrait avoir plus de votes positifs, c'est une solution propre et parfaite.
Opiatefuchs
Salut @Jared - Pouvez-vous corriger le même échantillon avec celui EventBusque vous avez suggéré?
Tohid le
15

Basique et simple.

Dans votre adaptateur, utilisez simplement ceci.

((YourParentClass) context).functionToRun();

Ck Maurya
la source
4
Ce n'est pas une bonne pratique car vous limitez votre classe pour qu'elle fonctionne uniquement avec le type YourParentClass au lieu de n'importe quelle classe, mais fonctionne si vous ne vous souciez pas de la réutiliser, si vous renommez également une classe, le code se brise ...
Gustavo Baiocchi Costa
7

Une autre façon est:

Écrivez une méthode dans votre adaptateur, disons public void callBack () {}.

Maintenant, lors de la création d'un objet pour l'adaptateur en activité, remplacez cette méthode. La méthode de remplacement sera appelée lorsque vous appelez la méthode dans l'adaptateur.

Myadapter adapter = new Myadapter() {
  @Override
  public void callBack() {
    // dosomething
  }
};
Prashanth Debbadwar
la source
5

Pour Kotlin:

Dans votre adaptateur, appelez simplement

(context as Your_Activity_Name).yourMethod()
Numair
la source
0
if (parent.getContext() instanceof yourActivity) {
  //execute code
}

cette condition vous permettra d'exécuter quelque chose si l'activité qui a les GroupViewvues qui demandent de la getView()méthode de votre adapterestyourActivity

REMARQUE: parentest-ce que GroupView

Abd Salam Baker
la source
ce GroupView demande des vues à l'adaptateur lorsqu'il appelle la getView()méthode ..... donc nous obtenons le contexte de ce GroupView, et l'examinons à travers la condition ci
Abd Salam Baker
0

Dans Kotlin, il existe maintenant un moyen plus propre en utilisant des fonctions lambda, pas besoin d'interfaces:

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

    }
}
lame
la source