Android 如何为 RecyclerView 创建上下文菜单

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26466877/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 11:32:40  来源:igfitidea点击:

How to create context menu for RecyclerView

androidcontextmenuandroid-recyclerview

提问by Binoy Babu

How do I implement context menu for RecyclerView?Apparently calling registerForContextMenu(recyclerView)doesn't work. I'm calling it from a fragment. Did anybody have any success implementing this?

我如何实现上下文菜单RecyclerView?显然调用registerForContextMenu(recyclerView)不起作用。我从一个片段调用它。有人成功实施了吗?

采纳答案by Prabhakar

You can't directly implement these method like onClickListener, OnContextMenuListeneretc. because RecycleViewextends android.view.ViewGroup. So we cant directly use these method. We can implement these methods in ViewHolderadapter class. We can use context menu in RecycleView like this way:

你不能直接实现这样的方法onClickListenerOnContextMenuListener因为等RecycleView延伸android.view.ViewGroup。所以我们不能直接使用这些方法。我们可以在ViewHolder适配器类中实现这些方法。我们可以像这样在 RecycleView 中使用上下文菜单:

public static class ViewHolder extends RecyclerView.ViewHolder implements OnCreateContextMenuListener {

    TextView tvTitle;
    ImageView ivImage;

    public ViewHolder(View v) {
        super(v);
        tvTitle =(TextView)v.findViewById(R.id.item_title);
        v.setOnCreateContextMenuListener(this);


    }

Now we follow the same procedure while implements the context menu.

现在我们在实现上下文菜单时遵循相同的过程。

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {

    menu.setHeaderTitle("Select The Action");    
    menu.add(0, v.getId(), 0, "Call");//groupId, itemId, order, title   
    menu.add(0, v.getId(), 0, "SMS"); 

}

If you get any difficulties ask in comment.

如果您遇到任何困难,请在评论中提出。

回答by Hardik Shah

Thanks for the info and comments. I was able to achieve ContextMenufor items in Recyclerview.

感谢您的信息和评论。我是能够实现ContextMenu对物品Recyclerview

Here is what I did

这是我所做的

in Fragment's onViewCreatedmethod or Activity's onCreatemethod:

在 Fragment 的onViewCreated方法或 Activity 的onCreate方法中:

registerForContextMenu(mRecyclerView);

Then in Adapter add

然后在适配器中添加

private int position;

public int getPosition() {
    return position;
}

public void setPosition(int position) {
    this.position = position;
}

make the ViewHolderclass implement OnCreateContextMenuListener

使ViewHolder类实现OnCreateContextMenuListener

public static class ViewHolder extends RecyclerView.ViewHolder 
        implements View.OnCreateContextMenuListener {

    public ImageView icon;

    public TextView fileName;
    public ImageButton menuButton;


    public ViewHolder(View v) {
        super(v);
        icon = (ImageView)v.findViewById(R.id.file_icon);
        fileName = (TextView)v.findViewById(R.id.file_name);
        menuButton = (ImageButton)v.findViewById(R.id.menu_button);
        v.setOnCreateContextMenuListener(this);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, 
        ContextMenu.ContextMenuInfo menuInfo) {
        //menuInfo is null
        menu.add(Menu.NONE, R.id.ctx_menu_remove_backup, 
            Menu.NONE, R.string.remove_backup);
        menu.add(Menu.NONE, R.id.ctx_menu_restore_backup,
            Menu.NONE, R.string.restore_backup);
    }
}

onBindViewHoldermethod add OnLongClickListeneron the holder.itemView to capture the position before the context menu is loaded:

onBindViewHolder方法OnLongClickListener在 holder.itemView 上添加以在加载上下文菜单之前捕获位置:

holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        setPosition(holder.getPosition());
        return false;
    }
});

Then in onViewRecycledremove the Listener so that there are no reference issues. (may not be required).

然后在onViewRecycled删除 Listener 以便没有引用问题。(可能不需要)。

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

Finally in the Fragment/Activity override the onContextItemSelectedas under:

最后在 Fragment/Activity 覆盖onContextItemSelected如下:

@Override
public boolean onContextItemSelected(MenuItem item) {
    int position = -1;
    try {
        position = ((BackupRestoreListAdapter)getAdapter()).getPosition();
    } catch (Exception e) {
        Log.d(TAG, e.getLocalizedMessage(), e);
        return super.onContextItemSelected(item);
    }
    switch (item.getItemId()) {
        case R.id.ctx_menu_remove_backup:
            // do your stuff
            break;
        case R.id.ctx_menu_restore_backup:
            // do your stuff
            break;
    }
    return super.onContextItemSelected(item);
}

回答by Renaud Cerrato

The current answer is not correct. Here's a working implementation:

目前的答案是不正确的。这是一个有效的实现:

public class ContextMenuRecyclerView extends RecyclerView {

  private RecyclerViewContextMenuInfo mContextMenuInfo;

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

  @Override
  public boolean showContextMenuForChild(View originalView) {
    final int longPressPosition = getChildPosition(originalView);
    if (longPressPosition >= 0) {
        final long longPressId = getAdapter().getItemId(longPressPosition);
        mContextMenuInfo = new RecyclerViewContextMenuInfo(longPressPosition, longPressId);
        return super.showContextMenuForChild(originalView);
    }
    return false;
  }

  public static class RecyclerViewContextMenuInfo implements ContextMenu.ContextMenuInfo {

    public RecyclerViewContextMenuInfo(int position, long id) {
        this.position = position;
        this.id = id;
    }

    final public int position;
    final public long id;
  }
}

In your Fragment (or Activity):

在您的片段(或活动)中:

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    mRecyclerView = view.findViewById(R.id.recyclerview);
    registerForContextMenu(mRecyclerView);
}

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    // inflate menu
    MenuInflater inflater = getActivity().getMenuInflater();
    inflater.inflate(R.menu.my_context_menu, menu);
}

@Override
public boolean onContextItemSelected(MenuItem item) {
    return super.onContextItemSelected(item);
    RecyclerViewContextMenuInfo info = (RecyclerViewContextMenuInfo) item.getMenuInfo();
    // handle menu item here
}

And finally, in your ViewHolder:

最后,在您的 ViewHolder 中:

class MyViewHolder extends RecyclerView.View.ViewHolder {
    ...
    private void onLongClick() {
        itemView.showContextMenu();
    }
}

回答by Sergey Bondarenko

Try this for a Viewitem in recycleView

View在 recycleView 中尝试这个项目

.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
        @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
            menu.add("delete").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
                @Override
                public boolean onMenuItemClick(MenuItem item) {

                    //do what u want
                    return true;
                }
            });
        }
    });

You can use it with setting data to a ViewHolderitem

您可以将它与设置数据一起用于ViewHolder项目

回答by Valeriy Katkov

Prabhakar answeris correct, but he didn't explain how to get a data, related to the pressed item, when a context menu item is selected. We can use onContextItemSelectedcallback, but ContextMenuInfois not available (null) in this case (if getContextMenuInfo()method is not overriden for a pressed view). So, the simplest solution is to add OnMenuItemClickListenerdirectly to the MenuItem.

Prabhakar 的回答是正确的,但他没有解释如何在选择上下文菜单项时获取与按下的项相关的数据。我们可以使用onContextItemSelected回调,但在这种情况下ContextMenuInfo不可用 ( null)(如果getContextMenuInfo()方法没有被按下的视图覆盖)。因此,最简单的解决方案是OnMenuItemClickListener直接添加到MenuItem.

private class ViewHolder extends RecyclerView.ViewHolder {
    private final TextView mTitleTextView;
    private MyItemData mData;

    public ViewHolder(View view) {
        super(view);

        mTitleTextView = (TextView)view.findViewById(R.id.title);

        view.setOnCreateContextMenuListener(mOnCreateContextMenuListener);
    }

    public void bind(@NonNull MyItemData data) {
         mData = data;

         String title = mData.getTitle();
         mTitleTextView.setText(title);
    }

    private final View.OnCreateContextMenuListener mOnCreateContextMenuListener = new View.OnCreateContextMenuListener() {
        @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
            if (mData!= null) {
                MenuItem myActionItem = menu.add("My Context Action");
                myActionItem.setOnMenuItemClickListener(mOnMyActionClickListener);
            }
        }
    };

    private final MenuItem.OnMenuItemClickListener mOnMyActionClickListener = new MenuItem.OnMenuItemClickListener() {
        @Override
        public boolean onMenuItemClick(MenuItem item) {
            //todo: process item click, mData is available here!!!
            return true;
        }
    };
}

回答by josh2112

@Renaud's answer worked for me but required several code fixes first. It's like he posted snippets from several different iterations of his code. The changes that need to be made are:

@Renaud 的回答对我有用,但首先需要修复几个代码。这就像他从他的代码的几个不同迭代中发布了片段。需要进行的更改是:

  • RecyclerContextMenuInfoand RecyclerViewContextMenuInfoare the same class. Pick a name and stick with it.
  • The ViewHoldermust implement View.OnLongClickListener, and remember to call setOnLongClickListener()on the item in the constructor.
  • In the onLongClick()listener, getView().showContextMenu()is completely wrong. You must call showContextMenuForChild()in your ContextMenuRecyclerView, otherwise the ContextMenuInfoyou get in onCreateContextMenu()and onContextItemSelected()will be null.
  • RecyclerContextMenuInfo并且RecyclerViewContextMenuInfo是同一个类。选择一个名字并坚持下去。
  • ViewHolder必须执行View.OnLongClickListener,并记住调用setOnLongClickListener()构造函数内的项目。
  • onLongClick()听者中,getView().showContextMenu()是完全错误的。你必须叫showContextMenuForChild()ContextMenuRecyclerView,否则ContextMenuInfo你在onCreateContextMenu()onContextItemSelected()将为空。

My edited code below:

我编辑的代码如下:

ContextMenuRecyclerView:

ContextMenuRecyclerView:

public class ContextMenuRecyclerView extends RecyclerView {

    private RecyclerViewContextMenuInfo mContextMenuInfo;

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

    @Override
    public boolean showContextMenuForChild(View originalView) {
        final int longPressPosition = getChildPosition(originalView);
        if (longPressPosition >= 0) {
            final long longPressId = getAdapter().getItemId(longPressPosition);
                mContextMenuInfo = new RecyclerViewContextMenuInfo(longPressPosition, longPressId);
            return super.showContextMenuForChild(originalView);
        }
        return false;
    }

    public static class RecyclerViewContextMenuInfo implements ContextMenu.ContextMenuInfo {

        public RecyclerViewContextMenuInfo(int position, long id) {
            this.position = position;
            this.id = id;
        }

        final public int position;
        final public long id;
    }
}

In your fragment:

在你的片段中:

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    mRecyclerView = view.findViewById(R.id.recyclerview);
    registerForContextMenu(mRecyclerView);
}

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    // inflate menu here
    // If you want the position of the item for which we're creating the context menu (perhaps to add a header or something):
    int itemIndex = ((ContextMenuRecyclerView.RecyclerViewContextMenuInfo) menuInfo).position;
}

@Override
public boolean onContextItemSelected(MenuItem item) {
    ContextMenuRecyclerView.RecyclerViewContextMenuInfo info = (ContextMenuRecyclerView.RecyclerViewContextMenuInfo) item.getMenuInfo();
    // handle menu here - get item index or ID from info
    return super.onContextItemSelected(item);
}

In your ViewHolder:

在您的 ViewHolder 中:

class MyViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener {

    public MyViewHolder( View itemView ) {
        super( itemView );
        itemView.setOnLongClickListener( this );
    }

    @Override public boolean onLongClick() {
        recyclerView.showContextMenuForChild( v );
        return true;
    }
}

Also, make sure you replace RecyclerViewwith ContextMenuRecyclerViewin your layout!

另外,请确保在布局中替换RecyclerViewContextMenuRecyclerView

回答by MohammadL

Here is a clean way to use menu context on RecyclerView items

这是在 RecyclerView 项目上使用菜单上下文的干净方法

First, you need an item position

首先,您需要一个项目位置

In Adapter class:

在适配器类中:

 /**
 * Custom on long click item listener.
 */
onLongItemClickListener mOnLongItemClickListener;

public void setOnLongItemClickListener(onLongItemClickListener onLongItemClickListener) {
    mOnLongItemClickListener = onLongItemClickListener;
}

public interface onLongItemClickListener {
    void ItemLongClicked(View v, int position);
}

In onBindViewHolderhook the custom listener:

onBindViewHolder挂钩自定义侦听器中:

        // Hook our custom on long click item listener to the item view.
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if (mOnLongItemClickListener != null) {
                    mOnLongItemClickListener.ItemLongClicked(v, position);
                }

                return true;
            }
        });

In MainActivity (Activity/Fragment) create a field:

在 MainActivity (Activity/Fragment) 中创建一个字段:

private int mCurrentItemPosition;

In your Adapter object set the custom listener:

在您的 Adapter 对象中设置自定义侦听器:

    mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() {
        @Override
        public void ItemLongClicked(View v, int position) {
            mCurrentItemPosition = position;
        }
    });

Now you have a yummy position for any item you long clicked on it

现在,您长按的任何项目都有一个美味的位置

Second, create your menu

其次,创建您的菜单

In res -> menuCreate a file contai your menu item context_menu_main.xml:

在 res -> menu创建一个包含你的菜单项的文件context_menu_main.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/delete" android:title="Delete"/>
<item android:id="@+id/share" android:title="Share"/>
</menu>

In MainActivity:Implement both onCreateContextMenuand onContextItemSelected:

在 MainActivity 中:同时实现onCreateContextMenuonContextItemSelected

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.context_menu_main, menu);
}

@Override
public boolean onContextItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.delete) {

    }

    if (id == R.id.share) {

    }

    return true;
}

Third, back to your Adapter object

三、回到你的Adapter对象

  1. Register your context menu.
  2. show the context menu.

    registerForContextMenu(mRecyclerView);
    mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() {
        @Override
        public void ItemLongClicked(View v, int position) {
            mCurrentItemPosition = position;
            v.showContextMenu();
        }
    });
    
  1. 注册您的上下文菜单。
  2. 显示上下文菜单。

    registerForContextMenu(mRecyclerView);
    mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() {
        @Override
        public void ItemLongClicked(View v, int position) {
            mCurrentItemPosition = position;
            v.showContextMenu();
        }
    });
    

Hope I don't forget anything

希望我不会忘记任何事情

More info at Menus Documentation

菜单文档中的更多信息

回答by Mickey Mouse

Here's a simpler way to do it with Kotlin that worked for me. The major challenge is figuring out the position of the item that was pressed. Inside your adapter, you can place this code snippet and it'll be able to capture the position of the item for whom the context menu is shown; that's all.

这是使用 Kotlin 的一种更简单的方法,它对我有用。主要的挑战是弄清楚被按下的项目的位置。在您的适配器中,您可以放置​​此代码片段,它将能够捕获显示上下文菜单的项目的位置;就这样。

override fun onBindViewHolder(holder: YourViewHolder, position: Int) {

...     

    holder.view.setOnCreateContextMenuListener { contextMenu, _, _ -> 
            contextMenu.add("Add").setOnMenuItemClickListener {
                    longToast("I'm pressed for the item at position => $position")
                    true    
            }       
    }       

} 

回答by Array

I have combined my solution with the solution from @Hardik Shah:

我将我的解决方案与@Hardik Shah 的解决方案结合起来:

In the activity I have:

在活动中,我有:

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    if (v.getId() == R.id.rvQuests) {
        getMenuInflater().inflate(R.menu.list_menu, menu);
    }
}

In the Adapter I have:

在适配器中,我有:

private MainActivity context;
private int position;

public int getPosition() {
    return position;
}

public void setPosition(int position) {
    this.position = position;
}

public QuestsAdapter(MainActivity context, List<Quest> objects) {
    this.context = context;
    this.quests.addAll(objects);
}

public class QuestViewHolder extends RecyclerView.ViewHolder {
    private QuestItemBinding questItemBinding;

    public QuestViewHolder(View v) {
        super(v);
        questItemBinding = DataBindingUtil.bind(v);
        v.setOnCreateContextMenuListener(context);
    }
}

@Override
public void onBindViewHolder(final QuestViewHolder holder, int position) {
    Quest quest = quests.get(position);
    holder.questItemBinding.setQuest(quest);
    holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            setPosition(holder.getAdapterPosition());
            return false;
        }
    });
}

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

In fragment I have:

在片段中我有:

@Override
public boolean onContextItemSelected(MenuItem item) {
    int position = ((QuestsAdapter) questsList.getAdapter()).getPosition();
    switch (item.getItemId()) {
        case R.id.menu_delete:
            Quest quest = questsAdapter.getItem(position);
            App.getQuestManager().deleteQuest(quest);
            questsAdapter.remove(quest);
            checkEmptyList();
            return true;
        default:
            return super.onContextItemSelected(item);
    }
}

回答by Gaurav Sharma

I maybe late to the party but I have an working solution. I have made an gist for it.

我可能迟到了,但我有一个可行的解决方案。我已经为它制定了一个要点。

Add Context Menu to RecyclerView

将上下文菜单添加到 RecyclerView

ActivityName.java

活动名称.java

//Import Statements

public class ActivityName extends AppCompatActivity {
    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_birthdays);


        //Recycle View
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
        mLayoutManager = new LinearLayoutManager(getApplicationContext());
        mRecyclerView.setLayoutManager(mLayoutManager);
        mAdapter = new BirthdaysListAdapter(data, this);
        mRecyclerView.setAdapter(mAdapter);


    }

RecyclerAdapter.java

RecyclerAdapter.java

//Import Statements


public class BirthdaysListAdapter extends RecyclerView.Adapter<BirthdaysListAdapter.ViewHolder> {
    static Context ctx;

    private List<typeOfData> Data;


    public BirthdaysListAdapter(List<typeOfData> list, Context context) {
        Data = list;
        this.ctx = context;

    }

    BirthdaysListAdapter() {
    }

    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener {
        public TextView name;
        public TextView Birthday;
        public ImageView colorAlphabet;
        public TextView textInImg;


        public ViewHolder(View v) {
            super(v);
            name = (TextView) v.findViewById(R.id.name);
            Birthday = (TextView) v.findViewById(R.id.Birthday);
            colorAlphabet = (ImageView) v.findViewById(R.id.colorAlphabet);
            textInImg = (TextView) v.findViewById(R.id.textInImg);


            v.setOnCreateContextMenuListener(this); //REGISTER ONCREATE MENU LISTENER
        }

        @Override
        public void onCreateContextMenu(ContextMenu menu, View v                         //CREATE MENU BY THIS METHOD
                                        ContextMenu.ContextMenuInfo menuInfo) {
            new BirthdaysListAdapter().info = (AdapterView.AdapterContextMenuInfo) menuInfo;
            MenuItem Edit = menu.add(Menu.NONE, 1, 1, "Edit");
            MenuItem Delete = menu.add(Menu.NONE, 2, 2, "Delete");
            Edit.setOnMenuItemClickListener(onEditMenu);
            Delete.setOnMenuItemClickListener(onEditMenu);


        }
//ADD AN ONMENUITEM LISTENER TO EXECUTE COMMANDS ONCLICK OF CONTEXT MENU TASK
        private final MenuItem.OnMenuItemClickListener onEditMenu = new MenuItem.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {


                DBHandler dbHandler = new DBHandler(ctx);
                List<WishMen> data = dbHandler.getWishmen();

                switch (item.getItemId()) {
                    case 1:
                        //Do stuff
                        break;

                    case 2:
                       //Do stuff

                        break;
                }
                return true;
            }
        };


    }


    public List<ViewBirthdayModel> getData() {
        return Data;
    }


    @Override
    public long getItemId(int position) {

        return super.getItemId(position);
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_view_birthdays, parent, false);
        ViewHolder vh = new ViewHolder(view);
        return vh;
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        holder.name.setText(Data.get(position).getMan().getName());
        holder.Birthday.setText(Data.get(position).getMan().getBday());
        holder.colorAlphabet.setBackgroundColor(Color.parseColor(Data.get(position).getColor()));
        holder.textInImg.setText(String.valueOf(Data.get(position).getMan().getName().toUpperCase().charAt(0)));
           }


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

    private int position;

    public int getPosition() {

        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }

}