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
How to create context menu for 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:
你不能直接实现这样的方法onClickListener, OnContextMenuListener因为等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 ContextMenu
for items in Recyclerview
.
感谢您的信息和评论。我是能够实现ContextMenu
对物品Recyclerview
。
Here is what I did
这是我所做的
in Fragment's onViewCreated
method or Activity's onCreate
method:
在 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 ViewHolder
class 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);
}
}
onBindViewHolder
method add OnLongClickListener
on 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 onViewRecycled
remove 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 onContextItemSelected
as 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 View
item 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 ViewHolder
item
您可以将它与设置数据一起用于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 onContextItemSelected
callback, but ContextMenuInfo
is not available (null
) in this case (if getContextMenuInfo()
method is not overriden for a pressed view). So, the simplest solution is to add OnMenuItemClickListener
directly 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 的回答对我有用,但首先需要修复几个代码。这就像他从他的代码的几个不同迭代中发布了片段。需要进行的更改是:
RecyclerContextMenuInfo
andRecyclerViewContextMenuInfo
are the same class. Pick a name and stick with it.- The
ViewHolder
must implementView.OnLongClickListener
, and remember to callsetOnLongClickListener()
on the item in the constructor. - In the
onLongClick()
listener,getView().showContextMenu()
is completely wrong. You must callshowContextMenuForChild()
in yourContextMenuRecyclerView
, otherwise theContextMenuInfo
you get inonCreateContextMenu()
andonContextItemSelected()
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 RecyclerView
with ContextMenuRecyclerView
in your layout!
另外,请确保在布局中替换RecyclerView
为ContextMenuRecyclerView
!
回答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 onBindViewHolder
hook 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 onCreateContextMenu
and onContextItemSelected
:
在 MainActivity 中:同时实现onCreateContextMenu
和onContextItemSelected
:
@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对象
- Register your context menu.
show the context menu.
registerForContextMenu(mRecyclerView); mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() { @Override public void ItemLongClicked(View v, int position) { mCurrentItemPosition = position; v.showContextMenu(); } });
- 注册您的上下文菜单。
显示上下文菜单。
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
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;
}
}