Как создать интерфейс между фрагментом и адаптером?

У меня есть фрагмент с ListView, скажем MyListFragment и пользовательский CursorAdapter. Я устанавливаю onClickListener в этом адаптере для кнопки в строке списка.

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
    }
}

Мне нужно уведомить фрагмент при нажатии кнопки. Как вызвать этот интерфейс?

Помогите, пожалуйста.

Ответ 1

Создайте новый конструктор и переменную экземпляра:

AdapterInterface buttonListener;

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

При создании адаптера переменной экземпляра будет присвоена правильная ссылка для удержания.

Чтобы вызвать фрагмент из щелчка:

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

При создании Adapter вам также необходимо передать свой фрагмент в адаптер. Например

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

так как this будет ссылаться на ваш фрагмент, который теперь является AdapterInterface.

Имейте в виду, что при ориентации изменений Фрагмента он скорее всего будет воссоздан. Если ваш адаптер не воссоздан, он потенциально может ссылаться на несуществующий объект, вызывая ошибки.

Ответ 2

Использование Eventbus:

Примеры:

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

или

https://github.com/greenrobot/EventBus

Использование интерфейсов:

Я понимаю текущий ответ, но мне нужен более ясный пример. Вот пример того, что я использовал с Adapter (RecyclerView.Adapter) и Fragment.

Создать Callback Интерфейс:

public interface AdapterCallback {
    void onMethodCallback();
}

Передача Callback/Fragment:

Это будет реализовывать interface, который у нас есть в нашем Adapter. В этом примере он будет вызываться, когда пользователь нажимает на элемент в RecyclerView.

В вашем 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
    }
}

Используйте Callback в своем адаптере:

В Fragment мы инициировали наш Adapter и передали это как аргумент конструктору. Это вызовет наш interface для нашего метода обратного вызова. Вы можете видеть, что мы используем наш метод обратного вызова для пользовательских кликов.

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

или используйте Fragment в вашем адаптере:

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

Ответ 3

Следуйте двум шагам ниже для получения обратного вызова от Adapter в Fragment (or Activity)

Сначала: В 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);
            }
        });
        ...
    }
    ...
}

Второе: В вашем Fragment (or Activity) существует 2 способа реализации метода обратного вызова

Путь 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();
             }
         });
         ...
     }
 }

Путь 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();     
    }          
}

Ответ 4

Это очень похоже на способ взаимодействия и фрагмента. В конструкторе вашего адаптера передайте ссылку своего фрагмента, отбросьте его на свой интерфейс и просто вызовите метод yourReference.buttonPressed() для вашего метода onClick.