129

Is it possible to call method that is defined in Activity from ListAdapter?

(I want to make a Button in list's row and when this button is clicked, it should perform the method, that is defined in corresponding Activity. I tried to set onClickListener in my ListAdapter but I don't know how to call this method, what's its path...)

when I used Activity.this.method() I get the following error:

No enclosing instance of the type Activity is accessible in scope

Any Idea ?

anand krish
  • 4,281
  • 4
  • 44
  • 47
user1602687
  • 1,477
  • 3
  • 14
  • 13
  • you cannot call activity.this in some other class unless it is a inner class to that activity. follow @Eldhose M Babu solution for your case – Archie.bpgc Aug 27 '12 at 13:03

9 Answers9

326

Yes you can.

In the adapter Add a new Field :

private Context mContext;

In the adapter Constructor add the following code :

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

In the getView(...) of 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();
    }
  }
});

replace with your own class names where you see your code, your activity etc.

If you need to use this same adapter for more than one activity then :

Create an Interface

public interface IMethodCaller {
    void yourDesiredMethod();
}

Implement this interface in activities you require to have this method calling functionality.

Then in Adapter getView(), call like:

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

You are done. If you need to use this adapter for activities which does not require this calling mechanism, the code will not execute (If check fails).

Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Eldhose M Babu
  • 14,382
  • 8
  • 39
  • 44
  • I have similar problem. in my case, it is Fragment what should i use instead of `((YourActivityName)mContext).yourDesiredMethod();` – Jeeten Parmar Oct 15 '14 at 09:10
  • 30
    As this is a popular question, I would strongly suggest NOT TO USE this solution. You should avoid class casting here, because this might lead to runtime exceptions. –  Nov 14 '14 at 10:18
  • 3
    Eldhose bro, I have nothing against you but answer below is best practice. You can even read in comments. You should not cast mContext to your Activity as you are avoiding reuse of code. Now this adapter can only be used inside the Activity which you have casted your mContext variable to, where if you have a listener defined then you can reuse the same Adapter in another Activity. Trust me i have spent enough time in industry and I am just trying to help you here, please don't take it personally. – Varundroid Dec 13 '14 at 13:30
  • 2
    This solution is one of the best I've ever found on SO. So thanks a lot Mr. Babu. Calling the Activity's methods by this, gives the whole app a 500% kicking performance boost -> I don't need to reload the database in the adapter, which I need to access. I don't care about "years in industry", I'm looking for stable and working solutions and I'm quite sure, I will not find a better way to get access. Maybe I could set up the DB as a singleton, but this is not the way I want to "un-structure" my project. – Martin Pfeffer Mar 08 '15 at 06:54
  • if in am using same adapter in one or more activity then ? – Ram Jul 08 '15 at 08:56
  • 2
    @RAM if you need to call the same method in more than one activity, you need to create an interface with that method name. Then implement that interface in what all activities you need the method call to be availed. Then in the click listener, check instance of your interface rather than using activity name. – Eldhose M Babu Jul 08 '15 at 13:14
  • You can't believe How Thankful I am to your for this Answer You are Amazing Had the other problem but though this I solved mine (Y) – Asad Mehmood Aug 15 '15 at 06:50
  • java.lang.ClassCastException: android.app.Application cannot be cast to <> – Shahzain ali Sep 13 '17 at 06:37
  • 1
    This solution is an anti-pattern, you should't tightly couple an adapter with activity. – Haroun Hajem Mar 26 '19 at 13:56
141

You can do it this way:

Declare interface:

public interface MyInterface{
    public void foo();
}

Let your Activity imlement it:

    public class MyActivity extends Activity implements MyInterface{
        public void foo(){
            //do stuff
        }
        
        public onCreate(){
            //your code
            MyAdapter adapter = new MyAdapter(this); //this will work as your 
                                                     //MyInterface listener
        }
    }

Then pass your activity to ListAdater:

public MyAdapter extends BaseAdater{
    private MyInterface listener;

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

And somewhere in adapter, when you need to call that Activity method:

listener.foo();
Pritam Pawade
  • 637
  • 7
  • 21
78

Original:

I understand the current answer but needed a more clear example. Here is an example of what I used with an Adapter(RecyclerView.Adapter) and an Activity.

In your Activity:

This will implement the interface that we have in our Adapter. In this example, it will be called when the user clicks on an item in the 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);
    }
}

In your Adapter:

In the Activity, we initiated our Adapter and passed this as an argument to the constructer. This will initiate our interface for our callback method. You can see that we use our callback method for user clicks.

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();
    }
}
Community
  • 1
  • 1
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
18

Basic and simple.

In your adapter simply use this.

((YourParentClass) context).functionToRun();

Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Ck Maurya
  • 2,225
  • 2
  • 17
  • 26
  • 4
    not a good practice because you are restricting your class to work only with YourParentClass type instead of any class, but works if you dont care about reusing it, if you also rename a class the code breaks... – Gustavo Baiocchi Costa Jun 13 '17 at 17:04
  • ((YourParentClass) parent.getContext()).functionToRun(); also works like this – user3088071 Mar 27 '23 at 15:54
10

For Kotlin:

In your adapter, simply call

(context as Your_Activity_Name).yourMethod()
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Numair
  • 1,062
  • 1
  • 20
  • 41
7

One more way is::

Write a method in your adapter lets say public void callBack(){}.

Now while creating an object for adapter in activity override this method. Override method will be called when you call the method in adapter.

Myadapter adapter = new Myadapter() {
  @Override
  public void callBack() {
    // dosomething
  }
};
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Prashanth Debbadwar
  • 1,047
  • 18
  • 33
2

In Kotlin there is now a cleaner way by using lambda functions, no need for 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) {

    }
}
blade
  • 804
  • 1
  • 9
  • 18
1

For kotlin you could do something like :

if(context is MainActivity){ context.functionToCall(values) }

Amal Sunil
  • 133
  • 2
0
if (parent.getContext() instanceof yourActivity) {
  //execute code
}

this condition will enable you to execute something if the Activity which has the GroupView that requesting views from the getView() method of your adapter is yourActivity

NOTE : parent is that GroupView

Jared Burrows
  • 54,294
  • 25
  • 151
  • 185