如何为RecyclerView创建上下文菜单

Bin*_*abu 89 android contextmenu android-recyclerview

如何实现上下文菜单RecyclerView?显然调用registerForContextMenu(recyclerView)不起作用.我是从片段中调用它的.有没有人有成功实现这个?

小智 90

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

这就是我做的

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

registerForContextMenu(mRecyclerView);
Run Code Online (Sandbox Code Playgroud)

然后在Adapter add中

private int position;

public int getPosition() {
    return position;
}

public void setPosition(int position) {
    this.position = position;
}
Run Code Online (Sandbox Code Playgroud)

使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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

然后onViewRecycled删除监听器,以便没有引用问题.(可能不是必需的).

@Override
public void onViewRecycled(ViewHolder holder) {
    holder.itemView.setOnLongClickListener(null);
    super.onViewRecycled(holder);
}
Run Code Online (Sandbox Code Playgroud)

最后在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);
}
Run Code Online (Sandbox Code Playgroud)

  • 使用viewHolder.getAdapterPosition()而不是getPosition() (9认同)
  • 对于那些得到getAdapter()错误的人,我通过保存对RecyclerView的引用解决了它,然后使用它:((BackupRestoreListAdapter)recyclerView.getAdapter()).getPosition(); (2认同)
  • R.id.ctx_menu_remove_backup 到底在哪里? (2认同)

Pra*_*kar 85

你不能直接实现这样的方法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);


    }
Run Code Online (Sandbox Code Playgroud)

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

@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"); 

}
Run Code Online (Sandbox Code Playgroud)

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

  • 好的,然后我们实现一个`onContextItemSelected`on活动/片段级别.`getTitle`有效,`getItemId`有效,但`getMenuInfo`为空.那么,如何获得`ViewHolder`? (7认同)
  • 经过一些实验,我无法使其工作,`getMenuInfo()`在`onContextItemSelected()`中返回null.也许那些确实让它工作的人碰巧在层次结构中的某个视图(`RecyclerView`或`Fragment`)中有一个`onCreateContextMenu()`方法?这可以工作,但然后带我们到这个问题的其他答案. (7认同)
  • 你可以使用`menu.add(this.getAdapterPosition(),v.getId(),0,"Call");`然后在你的回调方法测试`item.getGroupId()`来获取位置 (4认同)
  • Prabhakbar你没有提到如何获得点击的项目既不是视图? (3认同)

Ren*_*ato 26

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

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

@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
}
Run Code Online (Sandbox Code Playgroud)

最后,在您的ViewHolder中:

class MyViewHolder extends RecyclerView.View.ViewHolder {
    ...
    private void onLongClick() {
        itemView.showContextMenu();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这适合我.但是没有必要在MyViewHolder中使用onLongClick(),它足以在构造函数中设置itemView.setLongClickable(true),当OnLongClickListener未注册时,将出现上下文菜单. (4认同)
  • 重要的是:在将RecyclerView扩展到ContextMenuRecyclerView时,不要忘记添加IDE建议的构造函数.具体来说,如果您没有实现采用Context和AttributeSet的双参数构造函数,Android将无法使您的布局XML膨胀. (2认同)

小智 21

尝试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;
                }
            });
        }
    });
Run Code Online (Sandbox Code Playgroud)

您可以将其用于将数据设置为ViewHolder项目

  • 对于现在正在查看此内容的任何人,这仅适用于API 23及更高版本。 (2认同)

Val*_*kov 15

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;
        }
    };
}
Run Code Online (Sandbox Code Playgroud)


jos*_*112 11

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

  • RecyclerContextMenuInfo并且RecyclerViewContextMenuInfo是同一个班级.选择一个名字并坚持下去.
  • ViewHolder必须执行View.OnLongClickListener,并记住调用setOnLongClickListener()构造函数内的项目.
  • onLongClick()听众中,getView().showContextMenu()是完全错误的.你必须打电话给showContextMenuForChild()ContextMenuRecyclerView,否则ContextMenuInfo你进去onCreateContextMenu(),onContextItemSelected()将是空的.

我编辑的代码如下:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在你的片段中:

@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);
}
Run Code Online (Sandbox Code Playgroud)

在您的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

此外,请确保您更换RecyclerViewContextMenuRecyclerView在布局!


Moh*_*adL 11

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

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

在适配器类中:

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

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

public interface onLongItemClickListener {
    void ItemLongClicked(View v, int position);
}
Run Code Online (Sandbox Code Playgroud)

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;
            }
        });
Run Code Online (Sandbox Code Playgroud)

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

private int mCurrentItemPosition;
Run Code Online (Sandbox Code Playgroud)

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

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

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

其次,创建您的菜单

在 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>
Run Code Online (Sandbox Code Playgroud)

在 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;
}
Run Code Online (Sandbox Code Playgroud)

三、回到你的Adapter对象

  1. 注册您的上下文菜单。
  2. 显示上下文菜单。

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

希望我不会忘记任何事情

菜单文档中的更多信息


小智 9

这是使用 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    
            }       
    }       

} 
Run Code Online (Sandbox Code Playgroud)

  • 将侦听器放置在 onBindViewHolder 中是一种不好的做法。您应该将它们放在 onCreateViewHolder 中并调用holder.getAbsoluteAdapterPosition() (3认同)
  • 这是最自然、最可控的方式 (2认同)