65

I have fragment with ListView, say MyListFragment, and custom CursorAdapter. I'm setting onClickListener in this adapter for the button in the list row.

public class MyListAdapter extends CursorAdapter {

    public interface AdapterInterface {
        public void buttonPressed();
    }

    ...

    @Override
    public void bindView(final View view, final Context context, final Cursor cursor) {
        ViewHolder holder = (ViewHolder) view.getTag();

        ...

        holder.button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // some action
                // need to notify MyListFragment
            }
        });
    }
}

public MyListFragment extends Fragment implements AdapterInterface {

    @Override
    public void buttonPressed() {
        // some action
    }
}

I need to notify fragment when the button is pressed. How to invoke this interface?

Help, please.

Sabre
  • 4,131
  • 5
  • 36
  • 57

6 Answers6

85

Make a new constructor and an instance variable:

AdapterInterface buttonListener;

public MyListAdapter (Context context, Cursor c, int flags, AdapterInterface buttonListener)
{
  super(context,c,flags);
  this.buttonListener = buttonListener;
}

When the Adapter is made, the instance variable will be given the proper reference to hold.

To call the Fragment from the click:

public void onClick(View v) {
   buttonListener.buttonPressed();
}

When making the Adapter, you will have to also pass your Fragment off to the Adapter. For example

MyListAdapter adapter = new MyListAdapter (getActivity(), myCursor, myFlags, this);

since this will refer to your Fragment, which is now an AdapterInterface.

Keep in mind that on orientation of the Fragment changes, it will most likely be recreated. If your Adapter isn't recreated, it can potentially keep a reference to a nonexistent object, causing errors.

A--C
  • 36,351
  • 10
  • 106
  • 92
  • 1
    Where do I put the code for the first part? AdapterInterface buttonListener; I tried sticking it in my adapter but that threw an error. Thanks – Adam Katz Jun 17 '16 at 11:29
  • 1
    Awesome!! +1 for **AdapterInterface buttonListener** in constructor. Thank you. – iUser Apr 08 '19 at 19:30
73

Using Eventbus:

Examples:

https://github.com/kaushikgopal/RxJava-Android-Samples/tree/master/app/src/main/java/com/morihacky/android/rxjava/rxbus

or

https://github.com/greenrobot/EventBus

Using Interfaces:

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 a Fragment.

Create Callback Interface:

public interface AdapterCallback {
    void onMethodCallback();
}

Passing in Callback/Fragment:

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.

In your Fragment:

public class MyFragment extends Fragment implements AdapterCallback {

    private MyAdapter mMyAdapter;

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

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        this.mMyAdapter = new MyAdapter(this); // this class implements callback
    }
}

Use the Callback in your Adapter:

In the Fragment, 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 mAdapterCallback;

    public MyAdapter(AdapterCallback callback) {
        this.mAdapterCallback = callback;
    }

    @Override
    public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, final int i) {
        // simple example, call interface here
        // not complete
        viewHolder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mAdapterCallback.onMethodCallback();
            }
        });
    }
}

or Use the Fragment in your Adapter:

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

    private AdapterCallback mAdapterCallback;

    public MyAdapter(Fragment fragment) {
        try {
            this.mAdapterCallback = ((AdapterCallback) fragment);
        } catch (ClassCastException e) {
            throw new ClassCastException("Fragment must implement AdapterCallback.");
        }
    }

    @Override
    public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, final int i) {
        // simple example, call interface here
        // not complete
        viewHolder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    mAdapterCallback.onMethodCallback();
                } catch (ClassCastException exception) {
                   // do something
                }
            }
        });
    }
}
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
  • I tried the following for FragementActivity.. but I am getting NPE: http://pastebin.com/wc2ByiFz - if i don't use the callback and just log value things are fine. – TheDevMan Mar 17 '15 at 11:35
  • @TheDevMan NPE where? I use this method of interfaces in app of my apps. – Jared Burrows Mar 17 '15 at 11:39
  • Hmm. Whenever I use the callback I get NPE but when I don't call and just have the log it is working.I have detail question in http://stackoverflow.com/questions/29092877/click-event-from-adapter-to-activity-or-fragment-in-android - Can you help fix this? I have been struggling to get this solved. – TheDevMan Mar 17 '15 at 11:42
  • Once again, NPE where? The log you showed is not from my example: `at com.ylg.link.CustomAdapter$1.onGroupExpand(CustomAdapter.java:149)`. Get what solved? I have not really used the ExpandableListView before. – Jared Burrows Mar 17 '15 at 11:45
  • Hmm where ever I am trying call the callback in OnClickListner. Similar to your answer. You are trying mAdapterCallback.onMethodCallback(); in OnClick right.. the place in my code. – TheDevMan Mar 17 '15 at 11:47
  • You are getting a NPE because you need to initialize it like I did in the constructor: `this.mAdapterCallback = ((AdapterCallback) fragment);` – Jared Burrows Mar 17 '15 at 11:48
  • I have done that now. Now I am getting the Fragment must implement adapterCallBack Exception – TheDevMan Mar 17 '15 at 11:57
  • 1
    Yes, please read my example. You must `implements AdapterCallback {` in your Fragment. – Jared Burrows Mar 17 '15 at 11:59
  • True.. hence I have update my question here please check; http://stackoverflow.com/questions/29092877/click-event-from-adapter-to-activity-or-fragment-in-android Thanks! – TheDevMan Mar 17 '15 at 12:11
  • any idea for the other way around? a interface implementation in the recylcer view holder perhaps? http://stackoverflow.com/questions/32849380/recyclerview-header-access-objects-from-the-header-in-the-activity-fragment – CularBytes Sep 30 '15 at 12:56
  • This answer should be the accepted one... @JaredBurrows Perfect explained now I know how to use interfaces and its uses ;) Thanks. – Skizo-ozᴉʞS ツ Nov 05 '15 at 14:34
  • 1
    @Skizo Thank you! I think it is relaly first come, first serve ha. I am glad my answer helped. – Jared Burrows Nov 06 '15 at 03:25
  • Even i m getting NPE on my interface object in getview(). I have declared it properly still showing null pointer exception. Can anyone ans this? – Azhar osws Jun 26 '16 at 03:31
  • @Azharosws Please read the top of the post. I would highly suggest using EventBus. – Jared Burrows Jun 26 '16 at 03:32
  • ok i will try to use that ..Thanks for suggestion. But i really want to know why it is giving null pointer error? Although i have declared all the matter properly. – Azhar osws Jun 26 '16 at 03:48
  • 6
    EventBus needs to die. – Swindler Mar 17 '17 at 18:38
38

Follow the 2 steps below for receive callback from Adapter in Fragment (or Activity)

First: In your Adapter

public class ListAdapter extends RecyclerView.Adapter < RecyclerListAdapter.ItemViewHolder > {
    ...
    private ListAdapterListener mListener;

    public interface ListAdapterListener { // create an interface
        void onClickAtOKButton(int position); // create callback function
    }

    public RecyclerListAdapter(Context mContext, ArrayList < Items > listItems, ListAdapterListener mListener) { // add the interface to your adapter constructor
        ...
        this.mListener = mListener; // receive mListener from Fragment (or Activity)
    }
    ...
    public void onBindViewHolder(final ItemViewHolder holder, final int position) {

        holder.btnOK.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // use callback function in the place you want
                mListener.onClickAtOKButton(position);
            }
        });
        ...
    }
    ...
}

Second: In your Fragment (or Activity), there are 2 ways for implement callback method

Way 1

 public MyListFragment extends Fragment {
     ...
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         ...
         ListAdapter adapter = new ListAdapter(getActivity(), listItems, new ListAdapter.ListAdapterListener() {
             @Override
             public void onClickAtOKButton(int position) {
                 Toast.makeText(getActivity(), "click ok button at" + position, Toast.LENGTH_SHORT).show();
             }
         });
         ...
     }
 }

Way 2

public MyListFragment extends Fragment implements ListAdapter.ListAdapterListener {
     ...
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        ListAdapter  adapter = new ListAdapter (getActivity(), listItems, this);
        ...   
    }

    @Override
    public void onClickAtOKButton(int position) {  
        Toast.makeText(getActivity(), "click ok button at" + position, Toast.LENGTH_SHORT).show();     
    }          
}
Linh
  • 57,942
  • 23
  • 262
  • 279
  • In way1, with anonymous class implementation if the fragment gets destroyed and the on onClickAtOkButton() is called from some thread. What are the chances of memory leaks – Manish Patiyal Jun 22 '17 at 13:06
1

This is very similar to the way an activity and a fragment should communicate. In the constructor of your adapter, pass a reference of your fragment, cast it to your interface and just call yourReference.buttonPressed() on your onClick method.

Quanturium
  • 5,698
  • 2
  • 30
  • 36
0

a solution for NPE is first to make conctractor in your Fragment like that

public MyFragment MyFragment(){
        return this;
    }

then initialize your listener is adapter like that

Lisener lisener = new MyFragment();
0

Make a constructor like that:

  public MyAdapter(Activity activity,AlertMessageBoxOk alertMessageBoxOk) {

  this.mActivity = activity;

  mAlertMessageBoxOk = alertMessageBoxOk;

}

call the interface from adapter use any event

mAlertMessageBoxOk.onOkClick(5);

after that implement AlertMessageBoxOk interface to your fragment like this,

class MyFragment extends Fragment implements AlertMessageBoxOk {

@Override
public void onOkClick(int resultCode) {

  if(resultCode==5){

  enter code here 

      }
    } 
 }