RecyclerView添加ContextMenu的两种方案详解

RecyclerView+ContextMenu实现的技术难点主要是在RecyclerView中获取被点击item的position,本文一共给出了两种解决方案:

方案一:
从onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)方法传递进来的menuInfo中获取到相应的position信息。

方案二:
从RecyclerView的Adapter入手,在Adapter的ViewHolder中为每个itemView设置setOnLongClickListener监听,然后在长按监听回调中设置当前的position,为每个itemView设置setOnCreateContextMenuListener监听,通过上面记录的position来执行相应的动作。

一、方案一实现步骤:

RecyclerView+ContextMenu实现菜单项和ListView添加ContextMenu的步骤差不多,但要注意的是,从onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)方法传递进来的menuInfo为null,为什么为null值呢?因为RecyclerView并没有像ListView一样给我们重写View.getContextMenuInfo()这个方法,所以返回的是默认值null,(翻翻源码很容易就找到了),所以我们可以参考AbsListView类中对mContextMenuInfo对象的相关操作,重载RecyclerView类的showContextMenuForChild和getContextMenuInfo方法派生RecyclerViewWithContextMenu类。其中,在showContextMenuForChild方法中通过RecyclerView对应的LayoutManager方法获取到item的位置信息,并赋值给自定义的菜单附加信息类的对象。然后,在getContextMenuInfo方法中返回这个对象。这样上下文菜单被创建的时候就会在onCreateContextMenu方法的menuInfo对象中获得到传递的附加信息。

1.1 在RecyclerView中的的item布局设置longClickable属性

在RecyclerView中的的item布局设置longClickable属性,这样在长按item的时候才能弹出ContextMenu。注意:必须而且仅能在最外层布局中设置允许长按。

//代码设置
rl.setLongClickable(true);

//xml中设置
android:longClickable="true"

1.2 重载onCreateContextMenu

可以Activity中使用registerForContextMenu(view)注册需要上下文菜单的View,然后重载onCreateContextMenu方法,也可以在需要用到上下文菜单的View中设置setOnCreateContextMenuListener监听器来监听onCreateContextMenu事件的产生。这两种方法都可以实现,只要用其中一种即可。
方法一:registerForContextMenu(view)

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        registerForContextMenu(mRvUserList);
        ...
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        RecyclerViewWithContextMenu.RecyclerViewContextInfo contextMenuInfo = (RecyclerViewWithContextMenu.RecyclerViewContextInfo) menuInfo;
        if(contextMenuInfo!=null && contextMenuInfo.getPosition()>=0){
            UserAdapter adapter=(UserAdapter)mRvUserList.getAdapter();
            mSelectModelUser = adapter.getItem(contextMenuInfo.getPosition());
            menu.setHeaderTitle(mSelectModelUser.getUserName());
            CreateMenu(menu);
        }
    }

    public void CreateMenu(Menu menu)
    {
        int groupID = 0;
        int order = 0;
        int[] itemID = {1,2};

        for(int i=0;i<itemID.length;i++)
        {
            switch(itemID[i])
            {
                case 1:
                    menu.add(groupID, itemID[i], order, "编辑");
                    break;
                case 2:
                    menu.add(groupID, itemID[i], order, "删除");
                    break;
                default:
                    break;
            }
        }
    }

方法二:view…setOnCreateContextMenuListener

        mRvUserList.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
            @Override
            public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
                RecyclerViewWithContextMenu.RecyclerViewContextInfo contextMenuInfo = (RecyclerViewWithContextMenu.RecyclerViewContextInfo) menuInfo;
                if(contextMenuInfo!=null && contextMenuInfo.getPosition()>=0){
                    UserAdapter adapter=(UserAdapter)mRvUserList.getAdapter();
                    mSelectModelUser = adapter.getItem(contextMenuInfo.getPosition());
                    menu.setHeaderTitle(mSelectModelUser.getUserName());
                    CreateMenu(menu);
                }
            }
        });

1.3 重载onContextItemSelected方法

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case 1:
                showMsg("编辑");
                break;
            case 2:
                showMsg("删除");
                break;
            default:
                break;
        }
        return super.onContextItemSelected(item);
    }

1.4 RecyclerViewWithContextMenu

public class RecyclerViewWithContextMenu extends RecyclerView {

    private RecyclerViewContextInfo mContextInfo = new RecyclerViewContextInfo();

    public RecyclerViewWithContextMenu(Context context) {
        super(context);
    }

    public RecyclerViewWithContextMenu(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public RecyclerViewWithContextMenu(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean showContextMenuForChild(View originalView) {
        getPositionByChild(originalView);
        return super.showContextMenuForChild(originalView);
    }

    @Override
    public boolean showContextMenuForChild(View originalView, float x, float y) {
        getPositionByChild(originalView);
        return super.showContextMenuForChild(originalView, x, y);
    }

    /**
     * 记录当前RecyclerView中Item上下文菜单的Position
     * @param originalView originalView
     */
    private void getPositionByChild(View originalView){
        LayoutManager layoutManager =getLayoutManager();
        if(layoutManager!=null){
            int position=layoutManager.getPosition(originalView);
            mContextInfo.setPosition(position);
        }
    }

    @Override
    protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
        return mContextInfo;
    }

    public class RecyclerViewContextInfo implements ContextMenu.ContextMenuInfo {
        private int mPosition = -1;
        public int getPosition(){
            return this.mPosition;
        }
        public int setPosition(int position){
            return this.mPosition=position;
        }
    }

}

1.5 UserAdapter

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

    private Context mContext;
    private List<ModelUser> mList;


    public UserAdapter(List<ModelUser> fruitList){this.mList=fruitList;}
    public void setAdapterList(List<ModelUser> fruitList){this.mList=fruitList;}

    @Override
    public UserAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if(mContext==null){
            mContext = parent.getContext();
        }
        View view= LayoutInflater.from(mContext).inflate(R.layout.item_user_list_layout,parent,false);
        final ViewHolder holder=new ViewHolder(view);
        return holder;
    }

    @Override
    public void onViewRecycled(ViewHolder holder) {
        super.onViewRecycled(holder);
    }

    @Override
    public void onBindViewHolder(final UserAdapter.ViewHolder holder, int position) {
        ModelUser user = mList.get(position);
        holder.mUserName.setText(user.getUserName());
        holder.mUserDesc.setText(user.getCreateDate().toString());
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder{
        CardView mCardView;
        ImageView mImageView;
        TextView mUserName;
        TextView mUserDesc;

        public ViewHolder(View view){
            super(view);
            mCardView = (CardView) view;
            mImageView= (ImageView) view.findViewById(R.id.iv_user_image);
            mUserName = (TextView) view.findViewById(R.id.tv_user_name);
            mUserDesc = (TextView) view.findViewById(R.id.tv_user_desc);
        }
    }

    public ModelUser getItem(int position) {
        if (mList != null && (mList.size() >= position)) {
            return mList.get(position);
        }
        return null;
    }

}

显示的效果:
在这里插入图片描述


二、方案二实现步骤:

方案二要比方案一步骤简单不少,不需要设置longClickable属性,不需要registerForContextMenu(view),在Activity中只需要重载onContextItemSelected(MenuItem item)就可以了,剩下的都在Adapter中实现了。

2.1 在Activity中重载onContextItemSelected

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case 1:
                showMsg("编辑");
                break;
            case 2:
                showMsg("删除");
                break;
            default:
                break;
        }
        return super.onContextItemSelected(item);
    }

2.2 在 adapter 中创建 position 变量,并设置 set/get 方法来操作当前 item 的 position

   private int position;
    public int getContextMenuPosition() { return position; }
    public void setContextMenuPosition(int position) { this.position = position; }

2.3 在 ViewHolder 中 实现 OnCreateContextMenuListener 接口

    public  class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener{
        CardView mCardView;
        ImageView mImageView;
        TextView mUserName;
        TextView mUserDesc;

        public ViewHolder(View view){
            super(view);
            mCardView = (CardView) view;
            mImageView= (ImageView) view.findViewById(R.id.iv_user_image);
            mUserName = (TextView) view.findViewById(R.id.tv_user_name);
            mUserDesc = (TextView) view.findViewById(R.id.tv_user_desc);
            view.setOnCreateContextMenuListener(this);

        }

        @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
            //注意传入的menuInfo为null
            ModelUser mSelectModelUser = mList.get(getContextMenuPosition());
            Log.i("UserAdapter", "onCreateContextMenu: "+getContextMenuPosition());
            menu.setHeaderTitle(mSelectModelUser.getUserName());
            ((UserActivity)mContext).CreateMenu(menu);
        }
    }

2.4 在 onBindViewHolder 方法中添加 OnLongClickListener 监听,保存当前 item 的 position。

    @Override
    public void onBindViewHolder(final UserAdapter.ViewHolder holder, int position) {
        ModelUser user = mList.get(position);
        holder.mUserName.setText(user.getUserName());
        holder.mUserDesc.setText(user.getCreateDate().toString());
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                setContextMenuPosition(holder.getLayoutPosition());
                return false;
            }
        });
    }

2.5 在 onViewRecycled 方法中移除 OnLongClickListener 监听

    @Override
    public void onViewRecycled(ViewHolder holder) {
        holder.itemView.setOnLongClickListener(null);
        super.onViewRecycled(holder);
    }

完整的UserAdapter如下:

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

    private Context mContext;
    private List<ModelUser> mList;

    private int position;
    public int getContextMenuPosition() { return position; }
    public void setContextMenuPosition(int position) { this.position = position; }


    public UserAdapter(List<ModelUser> fruitList){this.mList=fruitList;}
    public void setAdapterList(List<ModelUser> fruitList){this.mList=fruitList;}

    @Override
    public UserAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if(mContext==null){
            mContext = parent.getContext();
        }
        View view= LayoutInflater.from(mContext).inflate(R.layout.item_user_list_layout,parent,false);
        final ViewHolder holder=new ViewHolder(view);
        return holder;
    }

    @Override
    public void onViewRecycled(ViewHolder holder) {
        holder.itemView.setOnLongClickListener(null);
        super.onViewRecycled(holder);
    }

    @Override
    public void onBindViewHolder(final UserAdapter.ViewHolder holder, int position) {
        ModelUser user = mList.get(position);
        holder.mUserName.setText(user.getUserName());
        holder.mUserDesc.setText(user.getCreateDate().toString());
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                setContextMenuPosition(holder.getLayoutPosition());
                return false;
            }
        });
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }

    public  class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener{
        CardView mCardView;
        ImageView mImageView;
        TextView mUserName;
        TextView mUserDesc;

        public ViewHolder(View view){
            super(view);
            mCardView = (CardView) view;
            mImageView= (ImageView) view.findViewById(R.id.iv_user_image);
            mUserName = (TextView) view.findViewById(R.id.tv_user_name);
            mUserDesc = (TextView) view.findViewById(R.id.tv_user_desc);
            view.setOnCreateContextMenuListener(this);

        }

        @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
            //注意传入的menuInfo为null
            ModelUser mSelectModelUser = mList.get(getContextMenuPosition());
            Log.i("UserAdapter", "onCreateContextMenu: "+getContextMenuPosition());
            menu.setHeaderTitle(mSelectModelUser.getUserName());
            ((UserActivity)mContext).CreateMenu(menu);
        }
    }
}

显示的效果:
在这里插入图片描述


版权声明:本文为MakerCloud原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。