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