如何在 Fragment 和适配器之间创建接口?

Posted

技术标签:

【中文标题】如何在 Fragment 和适配器之间创建接口?【英文标题】:How to create interface between Fragment and adapter? 【发布时间】:2013-03-04 20:58:40 【问题描述】:

我有带有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

请记住,在 Fragment 的方向发生变化时,它很可能会被重新创建。如果您的适配器未重新创建,它可能会保留对不存在对象的引用,从而导致错误。

【讨论】:

第一部分的代码应该放在哪里?适配器接口按钮监听器;我尝试将它粘贴在我的适配器中,但这引发了错误。谢谢 太棒了! +1 用于构造函数中的 AdapterInterface buttonListener。谢谢。【参考方案2】:

使用事件总线:

例子:

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

这将实现我们在Adapter 中的interface。在这个例子中,当用户点击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
                
            
        );
    

【讨论】:

我为 FragmentActivity 尝试了以下操作.. 但我得到了 NPE:pastebin.com/wc2ByiFz - 如果我不使用回调并且只记录值一切都很好。 @TheDevMan NPE 在哪里?我在我的应用程序中使用这种接口方法。 是的,请阅读我的示例。你必须在你的片段中implements AdapterCallback @Skizo 谢谢!我认为这是可靠的先到先得哈。我很高兴我的回答有所帮助。 EventBus 需要死掉。【参考方案3】:

按照以下 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)中,实现回调方法有两种方式

方式 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();     
              

【讨论】:

在方式 1 中,如果片段被销毁并且 onClickAtOkButton() 从某个线程调用,则使用匿名类实现。内存泄漏的可能性有多大【参考方案4】:

这与 Activity 和 Fragment 的通信方式非常相似。在适配器的构造函数中,传递片段的引用,将其转换为接口,然后在 onClick 方法上调用 yourReference.buttonPressed()。

【讨论】:

【参考方案5】:

NPE 的解决方案是首先在你的 Fragment 中像这样制作承包商

public MyFragment MyFragment()
        return this;
    

然后初始化你的监听器就是这样的适配器

Lisener lisener = new MyFragment();

【讨论】:

【参考方案6】:

制作一个这样的构造函数:

  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 

      
     
 

【讨论】:

以上是关于如何在 Fragment 和适配器之间创建接口?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用接口在片段和活动之间进行通信?

Activity Fragment 之间通信

如何在Activity和Fragment之间进行回调?

Fragment的ListView项接口定义中的按钮?

数据变化时刷新 Fragment 内容的方法(如调用 onCreateView)

Activity和Fragment之间的数据传递