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

У меня есть фрагмент с 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
    }
}

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

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

6 ответов

Решение

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

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,

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

Использование 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
                }
            }
        });
    }
}

Выполните 2 шага ниже для получения обратного вызова от 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();     
    }          
}

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

Сделайте конструктор так:

  public MyAdapter(Activity activity,AlertMessageBoxOk alertMessageBoxOk) {

  this.mActivity = activity;

  mAlertMessageBoxOk = alertMessageBoxOk;

}

вызвать интерфейс из адаптера использовать любое событие

mAlertMessageBoxOk.onOkClick(5);

после этого реализуйте интерфейс AlertMessageBoxOk для своего фрагмента следующим образом:

class MyFragment extends Fragment implements AlertMessageBoxOk {

@Override
public void onOkClick(int resultCode) {

  if(resultCode==5){

  enter code here 

      }
    } 
 }

Решение для NPE - сначала сделать так, чтобы подрядчик в вашем Фрагменте

public MyFragment MyFragment(){
        return this;
    }

затем инициализируйте ваш слушатель адаптер так

Lisener lisener = new MyFragment();
Другие вопросы по тегам