Làm cách nào để triển khai menu ngữ cảnh cho RecyclerView?
Rõ ràng gọi registerForContextMenu(recyclerView)
không hoạt động. Tôi đang gọi nó từ một mảnh vỡ. Có ai đã thành công khi thực hiện điều này?
Làm cách nào để triển khai menu ngữ cảnh cho RecyclerView?
Rõ ràng gọi registerForContextMenu(recyclerView)
không hoạt động. Tôi đang gọi nó từ một mảnh vỡ. Có ai đã thành công khi thực hiện điều này?
Câu trả lời:
Bạn không thể trực tiếp triển khai các phương pháp này như onClickListener , OnContextMenuListener , v.v. vì RecycleView mở rộng android.view.ViewGroup . Vì vậy, chúng tôi không thể trực tiếp sử dụng phương pháp này. Chúng ta có thể triển khai các phương thức này trong lớp bộ điều hợp ViewHolder . Chúng ta có thể sử dụng menu ngữ cảnh trong RecycleView như sau:
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);
}
Bây giờ chúng ta làm theo quy trình tương tự trong khi triển khai menu ngữ cảnh.
@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");
}
Nếu bạn gặp bất kỳ khó khăn nào, hãy hỏi trong bình luận.
onContextItemSelected
hoạt động / phân mảnh. getTitle
hoạt động, getItemId
hoạt động, nhưng getMenuInfo
phân phối rỗng. Vì vậy, làm thế nào để có được ahold của ViewHolder
?
getMenuInfo()
trả về null in onContextItemSelected()
. Có thể những người đã làm cho nó hoạt động tình cờ có một onCreateContextMenu()
phương pháp trong một cái nhìn để nâng cao hơn nữa hệ thống phân cấp ( RecyclerView
hoặc Fragment
)? Điều đó có thể hoạt động nhưng sau đó đưa chúng ta đến các câu trả lời khác cho câu hỏi này.
menu.add(this.getAdapterPosition(), v.getId(), 0, "Call");
và sau đó trong thử nghiệm phương pháp gọi lại của bạn cho item.getGroupId()
để có được những vị trí
Cảm ơn vì thông tin và nhận xét. Tôi đã có thể đạt được ContextMenu
cho các mục trong Recyclerview
.
Đây là những gì tôi đã làm
trong onViewCreated
phương thức của Fragment hoặc onCreate
phương thức của Activity :
registerForContextMenu(mRecyclerView);
Sau đó, trong Bộ điều hợp thêm
private int position;
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
làm cho ViewHolder
lớp thực hiệnOnCreateContextMenuListener
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
thêm phương thức OnLongClickListener
vào hold.itemView để nắm bắt vị trí trước khi tải menu ngữ cảnh:
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
setPosition(holder.getPosition());
return false;
}
});
Sau đó, onViewRecycled
xóa Trình nghe để không có vấn đề tham chiếu. (có thể không bắt buộc).
@Override
public void onViewRecycled(ViewHolder holder) {
holder.itemView.setOnLongClickListener(null);
super.onViewRecycled(holder);
}
Cuối cùng trong Fragment / Activity ghi đè onContextItemSelected
như sau:
@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);
}
Câu trả lời hiện tại là không chính xác. Đây là một triển khai hoạt động:
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;
}
}
Trong Phân đoạn (hoặc Hoạt động) của bạn:
@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
}
Và cuối cùng, trong ViewHolder của bạn:
class MyViewHolder extends RecyclerView.View.ViewHolder {
...
private void onLongClick() {
itemView.showContextMenu();
}
}
getChildPosition()
không được dùng nữa. Tôi đã sử dụng getChildAdapterPosition()
thay thế.
getChildPosition()
không được dùng nữa trong com.android.support:recyclerview-v7:22.0.0
.
Hãy thử điều này cho một View
mục trong 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;
}
});
}
});
Bạn có thể sử dụng nó với cài đặt dữ liệu cho một ViewHolder
mục
Câu trả lời của Prabhakar là đúng, nhưng anh ta không giải thích cách lấy dữ liệu, liên quan đến mục được nhấn, khi một mục menu ngữ cảnh được chọn. Chúng ta có thể sử dụng onContextItemSelected
callback, nhưng ContextMenuInfo
không khả dụng ( null
) trong trường hợp này (nếu getContextMenuInfo()
phương thức không được ghi đè cho dạng xem được nhấn). Vì vậy, giải pháp đơn giản nhất là thêm OnMenuItemClickListener
trực tiếp vào 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;
}
};
}
RecyclerView
chỉ để ghi đè getContextMenuInfo
, v.v., ngay cả khi nó không hoàn toàn hiệu quả bằng việc để phân đoạn / hoạt động xử lý các nhấp chuột. Người nghe sẽ có quyền truy cập vào dữ liệu trong trình lưu trữ, vì vậy bạn không cần vị trí. Và về mặt lý thuyết, bạn vẫn có thể lưu vào bộ nhớ cache vị trí khi ràng buộc trong bộ điều hợp của mình và sử dụng ủy quyền để gọi ra chủ sở hữu của bạn nếu bạn phải, mặc dù việc sử dụng Context
từ một trong các chế độ xem bị ràng buộc đôi khi có thể là đủ.
Câu trả lời của @ Renaud phù hợp với tôi nhưng trước tiên yêu cầu sửa một số mã. Nó giống như anh ấy đã đăng các đoạn mã từ một số lần lặp lại khác nhau của mã của mình. Những thay đổi cần được thực hiện là:
RecyclerContextMenuInfo
và RecyclerViewContextMenuInfo
là cùng một lớp. Chọn một cái tên và gắn bó với nó.ViewHolder
phải thực hiện View.OnLongClickListener
, và ghi nhớ để gọi setOnLongClickListener()
vào mục trong constructor.onLongClick()
người nghe, getView().showContextMenu()
là hoàn toàn sai lầm. Bạn phải gọi showContextMenuForChild()
trong của bạn ContextMenuRecyclerView
, nếu không ContextMenuInfo
bạn nhận được onCreateContextMenu()
và onContextItemSelected()
sẽ vô hiệu.Mã đã chỉnh sửa của tôi bên dưới:
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;
}
}
Trong phân đoạn của bạn:
@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);
}
Trong ViewHolder của bạn:
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;
}
}
Ngoài ra, hãy đảm bảo rằng bạn thay thế RecyclerView
bằng ContextMenuRecyclerView
trong bố cục của mình!
recyclerView.showContextMenuForChild(itemView);
bằng itemView.showContextMenu()
.
Trong lớp Bộ điều hợp:
/**
* Custom on long click item listener.
*/
onLongItemClickListener mOnLongItemClickListener;
public void setOnLongItemClickListener(onLongItemClickListener onLongItemClickListener) {
mOnLongItemClickListener = onLongItemClickListener;
}
public interface onLongItemClickListener {
void ItemLongClicked(View v, int position);
}
Trong onBindViewHolder
hook trình nghe tùy chỉnh:
// 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;
}
});
Trong MainActivity (Hoạt động / Phân mảnh), hãy tạo một trường:
private int mCurrentItemPosition;
Trong đối tượng Bộ điều hợp của bạn, hãy đặt trình nghe tùy chỉnh:
mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() {
@Override
public void ItemLongClicked(View v, int position) {
mCurrentItemPosition = position;
}
});
Bây giờ bạn có một vị trí tuyệt vời cho bất kỳ mục nào bạn đã nhấp vào nó từ lâu 😋
Trong menu res ->
Tạo một tệp chứa mục menu của bạn 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>
Trong MainActivity:
Triển khai cả onCreateContextMenu
và 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;
}
hiển thị menu ngữ cảnh.
registerForContextMenu(mRecyclerView);
mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() {
@Override
public void ItemLongClicked(View v, int position) {
mCurrentItemPosition = position;
v.showContextMenu();
}
});
Hy vọng tôi không quên bất cứ điều gì 🤔
Thông tin thêm tại Tài liệu menu
Đây là một cách đơn giản hơn để làm điều đó với Kotlin đã phù hợp với tôi. Thách thức chính là tìm ra vị trí của mục được ấn. Bên trong bộ điều hợp của mình, bạn có thể đặt đoạn mã này và nó sẽ có thể nắm bắt vị trí của mục mà menu ngữ cảnh được hiển thị; đó là tất cả.
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
}
}
}
Tôi đã kết hợp giải pháp của mình với giải pháp từ @Hardik Shah:
Trong hoạt động tôi có:
@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);
}
}
Trong Bộ điều hợp tôi có:
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);
}
Trong phân đoạn tôi có:
@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);
}
}
Tôi có thể đến muộn bữa tiệc nhưng tôi có một giải pháp làm việc . Tôi đã đưa ra một ý chính cho nó.
Thêm trình đơn ngữ cảnh vào RecyclerView
ActivityName.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
//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;
}
}
Xin chào các bạn đã ra mắt một giải pháp thay thế phù hợp với tôi. Tôi chỉ cần đăng ký itemView của mình với registerContextMenu y là ViewHolder Constructor, cũng đặt onLongClikcListener cho cùng một View. Trong triển khai onLongClick (View v), tôi chỉ cần lấy vị trí đã nhấp bằng getLayoutPosition () và lưu trong một biến thể hiện (tôi đã tạo một lớp để đại diện cho dữ liệu này, giống như ContextMenuInfo dự kiến sẽ hoạt động), nhưng quan trọng hơn là làm chắc chắn rằng bạn trả về false trong phương thức này. Tất cả những gì bạn phải làm bây giờ là ở bạn trên onContextItemSelected (mục MenuItem), đọc dữ liệu mà bạn lưu trữ trong biến thể hiện của mình và nếu nó hợp lệ, bạn tiến hành các hành động của mình. Đây là một đoạn mã.
public MyViewHolder(View itemView){
super(itemView);
registerForContextMenu(itemView);
itemView.setOnLongClickListener(this);
}
Tôi làm cho ViewHolder triển khai OnLongClickListener, nhưng bạn có thể thực hiện theo bất kỳ cách nào bạn muốn.
@Override
public boolean onLongClick(View v){
mCurrentLongItem = new ListItemInfo(v.getId(), getLayoutPosition());
return false; // REMEMBER TO RETURN FALSE.
}
Bạn cũng có thể đặt điều này trong bộ điều hợp hoặc cho một Chế độ xem khác mà bạn có trong ViewHolder (tức là một TextView). Điều quan trọng là triển khai onLongClik ().
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.client_edit_context_menu:
if(mCurrentLongItem != null){
int position = mCurrentLongItem.position;
//TAKE SOME ACTIONS.
mCurrentLongItem = null;
}
return true;
}
return super.onContextItemSelected(item);
}
Phần tốt nhất là bạn vẫn có thể xử lý sự kiện LongClick trả về true trong trường hợp bạn muốn và conextMenu sẽ không hiển thị.
Phương thức này hoạt động vì registerForContextView làm cho View LongClickable và khi đến thời điểm xử lý ContextMenu, hệ thống sẽ gọi performanceLongClick, phương thức này đầu tiên sẽ gọi một triển khai onLongClick và nếu nó trả về false, thì nó sẽ gọi showContextMenu.
Tôi đã sử dụng giải pháp này đôi khi và nó hoạt động khá tốt cho tôi.
public class CUSTOMVIEWNAME extends RecyclerView {
public CUSTOMVIEWNAME(Context context) {
super(context);
}
public CUSTOMVIEWNAME (Context context, AttributeSet attrs) {
super(context, attrs);
}
public CUSTOMVIEWNAME (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private RecyclerContextMenuInfo mContextMenuInfo;
@Override
protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
return mContextMenuInfo;
}
@Override
public boolean showContextMenuForChild(View originalView) {
final int longPressPosition = getChildAdapterPosition(originalView);
if (longPressPosition >= 0) {
final long longPressId = getAdapter().getItemId(longPressPosition);
mContextMenuInfo = new RecyclerContextMenuInfo(longPressPosition, ` longPressId);
return super.showContextMenuForChild(originalView);
}
return false;
}
public class RecyclerContextMenuInfo implements ContextMenu.ContextMenuInfo {
public RecyclerContextMenuInfo(int position, long id) {
this.position = position;
this.id = id;
}
final public int position;
final public long id;
}
}
Bây giờ trong phân đoạn hoặc Hoạt động của bạn, hãy triển khai các phương pháp sau.
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
// Inflate Menu from xml resource
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.context_menu, menu);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
ContextMenuRecyclerView.RecyclerContextMenuInfo info = (ContextMenuRecyclerView.RecyclerContextMenuInfo) item.getMenuInfo();
Toast.makeText(InstanceOfContext , " User selected " + info.position, Toast.LENGTH_LONG).show();
return false;
}
Cuối cùng Đăng ký bối cảnhMenu trên chế độ xem lại
//for showing a popup on LongClick of items in recycler.
registerForContextMenu(recyclerView);
Cần làm việc!
Đây là cách bạn có thể triển khai menu ngữ cảnh cho RecyclerView và lấy vị trí của mục, mục menu ngữ cảnh đã được chọn:
public class YourAdapter extends RecyclerView.Adapter<YourAdapter.ViewHolder> {
...
@Override
public void onBindViewHolder(@NonNull final ViewHolder viewHolder, int position) {
...
viewHolder.itemView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
menu.add(0, R.id.mi_context_disable, 0, R.string.text_disable)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
// can do something with item at position given below,
// viewHolder is final
viewHolder.getAdapterPosition();
return true;
}
});
menu.add(0, R.id.mi_context_remove, 1, R.string.text_remove)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
// can do something with item at position given below,
// viewHolder is final
viewHolder.getAdapterPosition();
return true;
}
});
}
});
}
static class ViewHolder extends RecyclerView.ViewHolder {
private View itemView;
private ViewHolder(@NonNull View itemView) {
super(itemView);
this.itemView = itemView;
}
}
}
Một giải pháp cho những ai muốn lấy id item khi gọi ContextMenu
.
Nếu bạn có RecyclerView
với các mục như thế này (chứa có thể nhấp ImageView
):
thì bạn sẽ nhận được các cuộc gọi lại từ onClickListener
.
Bộ chuyển đổi
class YourAdapter(private val contextMenuCallback: ContextMenuCallback) :
RecyclerView.Adapter<YourAdapter.ViewHolder>() {
private var items: MutableList<Item> = mutableListOf()
...
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
val item = items[position] as Item
updateItem(viewHolder, item)
setOnClickListener(viewHolder.itemView, items[position].id, items[position].title)
}
private fun setOnClickListener(view: View, id: Int, title: String) {
// view.setOnClickListener { v -> }
// A click listener for ImageView `more`.
view.more.setOnClickListener {
// Here we pass item id, title, etc. to Fragment.
contextMenuCallback.onContextMenuClick(view, id, title)
}
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val titleTextView: TextView = itemView.title
}
class Item(
val id: Int,
val title: String
)
interface ContextMenuCallback {
fun onContextMenuClick(view: View, id: Int, title: String)
}
}
Miếng
class YourFragment : Fragment(), YourAdapter.ContextMenuCallback {
private var adapter: YourAdapter? = null
private var linearLayoutManager: LinearLayoutManager? = null
private var selectedItemId: Int = -1
private lateinit var selectedItemTitle: String
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = YourAdapter(this)
view.recycler_view.apply {
layoutManager = linearLayoutManager
adapter = this@YourFragment.adapter
setHasFixedSize(true)
}
registerForContextMenu(view.recycler_view)
}
override fun onCreateContextMenu(menu: ContextMenu?, v: View?,
menuInfo: ContextMenu.ContextMenuInfo?) {
activity?.menuInflater?.inflate(R.menu.menu_yours, menu)
}
override fun onContextItemSelected(item: MenuItem?): Boolean {
super.onContextItemSelected(item)
when (item?.itemId) {
R.id.action_your -> yourAction(selectedItemId, selectedItemTitle)
...
}
return true
}
override fun onContextMenuClick(view: View, id: Int, title: String) {
// Here we accept item id, title from adapter and show context menu.
selectedItemId = id
selectedItemTitle = title
view.showContextMenu()
}
}
Cảnh báo!
Nếu bạn sử dụng ViewPager
dựa trên một phân đoạn (tất cả các trang là danh sách tương tự), bạn sẽ gặp phải vấn đề. Khi bạn ghi đè onContextItemSelected
để hiểu mục menu nào đã được chọn, bạn sẽ nhận được id mục danh sách từ trang đầu tiên! Để khắc phục sự cố này, hãy xem Phân đoạn sai trong ViewPager nhận lệnh gọi onContextItemSelected .
Tôi đã gặp khó khăn về vấn đề này vì Android không xử lý điều này tốt cho tôi trong RecyclerView, vốn đang hoạt động rất tốt cho ListView.
Khó khăn nhất là phần ContextMenuInfo được nhúng bên trong một Chế độ xem, mà bạn không thể dễ dàng đính kèm ngoài việc ghi đè Chế độ xem.
Vì vậy, bạn sẽ cần một trình bao bọc giúp bạn cung cấp thông tin vị trí cho Hoạt động.
public class RecyclerContextMenuInfoWrapperView extends FrameLayout {
private RecyclerView.ViewHolder mHolder;
private final View mView;
public RecyclerContextMenuInfoWrapperView(View view) {
super(view.getContext());
setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
mView = view;
addView(mView);
}
public void setHolder(RecyclerView.ViewHolder holder) {
mHolder = holder;
}
@Override
protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
return new RecyclerContextMenuInfo(mHolder.getPosition(), mHolder.getItemId());
}
public static class RecyclerContextMenuInfo implements ContextMenu.ContextMenuInfo {
public RecyclerContextMenuInfo(int position, long id) {
this.position = position;
this.id = id;
}
final public int position;
final public long id;
}
}
Sau đó, trong RecyclerAdapter của bạn, khi bạn tạo ViewHolders, bạn cần đặt Wrapper làm chế độ xem gốc và đăng ký contextMenu trên mỗi chế độ xem.
public static class AdapterViewHolder extends RecyclerView.ViewHolder {
public AdapterViewHolder( View originalView) {
super(new RecyclerContextMenuInfoWrapperView(originalView);
((RecyclerContextMenuInfoWrapperView)itemView).setHolder(this);
yourActivity.registerForContextMenu(itemView);
itemView.setOnCreateContextMenuListener(yourListener);
}
}
Và cuối cùng, trong Hoạt động của bạn, bạn sẽ có thể làm những gì bạn thường làm:
@Override
public boolean onContextItemSelected(MenuItem item) {
int position = ((RecyclerContextMenuInfoWrapperView.RecyclerContextMenuInfo)item.getMenuInfo()).position;
// do whatever you need as now you have access to position and id and everything
Cách tốt nhất là sử dụng menu Ngữ cảnh với chế độ xem trình tái chế là nếu bạn tạo chế độ xem trình tái chế tùy chỉnh và ghi đè getContextMenuInfo()
phương thức và trả về bản sao của đối tượng thông tin menu Ngữ cảnh để bạn có thể tìm nạp các vị trí khi nó được tạo và khi menu được nhấp:
@Override
protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
return mContextMenuInfo;
}
Hãy xem ý chính mà tôi đã tạo:
Mở rộng một số câu trả lời ở trên một chút, nếu bạn muốn tránh xác định thủ công menu trong mã của mình trong Bộ điều hợp / ViewHolder thì bạn có thể sử dụng PopupMenu và tăng các tùy chọn menu từ tệp tài nguyên menu.xml tiêu chuẩn.
Ví dụ bên dưới cho thấy điều này bao gồm khả năng chuyển vào một trình lắng nghe mà bạn có thể triển khai trong Phân đoạn / Hoạt động của mình để phản hồi các lần nhấp vào menu ngữ cảnh.
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
private List<CustomObject> objects;
private OnItemSelectedListener listener;
private final boolean withContextMenu;
class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener, View.OnCreateContextMenuListener, PopupMenu.OnMenuItemClickListener {
@BindView(R.id.custom_name)
TextView name;
@BindView(R.id.custom_value)
TextView value;
ViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
view.setOnClickListener(this);
if (withContextMenu) {
view.setOnCreateContextMenuListener(this);
}
}
@Override
public void onClick(View v) {
int position = getAdapterPosition();
if (listener != null) {
listener.onCustomerSelected(objects.get(position));
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
PopupMenu popup = new PopupMenu(v.getContext(), v);
popup.getMenuInflater().inflate(R.menu.custom_menu, popup.getMenu());
popup.setOnMenuItemClickListener(this);
popup.show();
}
@Override
public boolean onMenuItemClick(MenuItem item) {
if (listener != null) {
CustomObject object = objects.get(getAdapterPosition());
listener.onCustomerMenuAction(object, item);
}
return false;
}
}
public CustomerAdapter(List<CustomObject> objects, OnItemSelectedListener listener, boolean withContextMenu) {
this.listener = listener;
this.objects = objects;
this.withContextMenu = withContextMenu;
}
public interface OnItemSelectedListener {
void onSelected(CustomObject object);
void onMenuAction(CustomObject object, MenuItem item);
}
@Override
public CustomerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.snippet_custom_object_line, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(CustomAdapter.ViewHolder holder, int position) {
CustomObject object = objects.get(position);
holder.name.setText(object.getName());
holder.value.setText(object.getValue());
}
@Override
public int getItemCount() {
return objects.size();
}
}
Ý chính đầy đủ tại đây https://gist.github.com/brettwold/45039b7f02ce752ae0d32522a8e2ad9c
Bạn có thể chuyển OnCreateContextMenuListener vào ViewHolder trên bind. Trình nghe này có thể tạo menu tùy chỉnh cho từng mục dữ liệu. Chỉ cần thêm setOnCreateContextMenuListener trong ViewHolder của bạn và gọi nó trong quá trình ràng buộc.
public static class ItemViewHolder extends RecyclerView.ViewHolder
{
public ItemViewHolder(View itemView) {
super(itemView);
}
void setOnCreateContextMenuListener(View.OnCreateContextMenuListener listener) {
itemView.setOnCreateContextMenuListener(listener);
}
}
Trong bộ chuyển đổi:
@Override
public void onBindViewHolder(ItemViewHolder viewHolder,
int position) {
final MyObject myObject = mData.get(position);
viewHolder.setOnCreateContextMenuListener(new OnCreateContextMenuListener(){
@Override
public void onCreateContextMenu(ContextMenu menu,
View v, ContextMenuInfo menuInfo) {
switch (myObject.getMenuVariant() {
case MNU_VARIANT_1:
menu.add(Menu.NONE, CTX_MNU_1,
Menu.NONE,R.string.ctx_menu_item_1);
menu.add(Menu.NONE, CTX_MNU_2,Menu.NONE, R.string.ctx_menu_item_2);
break;
case MNU_VARIANT_2:
menu.add(Menu.NONE, CTX_MNU_3,Menu.NONE, R.string.ctx_menu_item_3);
break;
default:
menu.add(Menu.NONE, CTX_MNU_4,
Menu.NONE, R.string.ctx_menu_item_4);
}
}
});
}
Trong trường hợp của tôi, tôi phải sử dụng dữ liệu từ phân đoạn của mình trong onContextItemSelected()
phương thức. Giải pháp mà tôi đã thực hiện là chuyển một phiên bản của phân đoạn vào bộ điều hợp của tôi và đăng ký mục xem trong trình giữ chế độ xem:
@Override
public void onBindViewHolder(final MyListAdapter.ViewHolder viewHolder, int position) {
final Object rowObject = myListItems.get(position);
// Do your data binding here
viewHolder.itemView.setTag(position);
fragment.registerForContextMenu(viewHolder.itemView);
}
Sau đó, onCreateContextMenu()
bạn có thể lưu chỉ mục vào một biến cục bộ:
selectedViewIndex = (int)v.getTag();
và lấy nó trong onContextItemSelected()
Lần đầu tiên tôi gặp sự cố này với các bộ điều hợp thông thường, tôi đã kết thúc việc tạo lớp con Chế độ xem tùy chỉnh của riêng mình và lưu trữ những thứ tôi cần trong đó. Tôi thực sự không thích giải pháp đó và đã dành rất nhiều thời gian để xem xét những ý tưởng tuyệt vời mà mọi người đã đề xuất, và quyết định rằng tôi không thích chúng hơn nữa. Vì vậy, tôi đã sắp xếp mọi thứ lại với nhau, lắc nó một lúc và cho ra một thứ mới mà tôi thích.
Chúng tôi bắt đầu với một vài lớp tiện ích. ContextMenuHandler là một giao diện cho bất kỳ đối tượng nào sẽ xử lý menu ngữ cảnh. Trong thực tế, đây sẽ là một lớp con của ViewHolder, nhưng về lý thuyết, nó có thể là bất cứ thứ gì
/**
* Interface for objects that wish to create and handle selections from a context
* menu associated with a view
*/
public interface ContextMenuHandler extends View.OnCreateContextMenuListener {
boolean onContextItemSelected(MenuItem item);
}
Tiếp theo là một Giao diện phải được thực hiện bởi bất kỳ Chế độ xem nào sẽ được sử dụng làm con ngay lập tức của Chế độ xem tái chế.
public interface ViewWithContextMenu {
public void setContextMenuHandler(FragmentWithContextMenu fragment, ContextMenuHandler handler);
public ContextMenuHandler getContextMenuHandler();
}
Tiếp theo, bất kỳ chế độ xem nào sẽ tạo menu ngữ cảnh dưới dạng phần tử con của RecylcerView phải triển khai ViewWIthContextMenu. Trong trường hợp của tôi, tôi chỉ cần một lớp con của LinearLayout.
public class LinearLayoutWithContextMenu extends LinearLayout implements ViewWithContextMenu {
public LinearLayoutWithContextMenu(Context context) {
super(context);
}
public LinearLayoutWithContextMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
private ContextMenuHandler handler;
@Override
public void setContextMenuHandler(FragmentWithContextMenu fragment, ContextMenuHandler handler) {
this.handler = handler;
setOnCreateContextMenuListener(fragment);
}
@Override
public ContextMenuHandler getContextMenuHandler() {
return handler;
}
}
Và cuối cùng, chúng ta cần một lớp Fragment được cải tiến để chặn các lệnh gọi menu ngữ cảnh và chuyển hướng chúng đến trình xử lý thích hợp.
public class FragmentWithContextMenu extends Fragment {
ContextMenuHandler handler = null;
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, view, menuInfo);
handler = null;
if (view instanceof ViewWithContextMenu) {
handler = ((ViewWithContextMenu)view).getContextMenuHandler();
if (handler != null) handler.onCreateContextMenu(menu, view, menuInfo);
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
if (handler != null) {
if (handler.onContextItemSelected(item)) return true;
}
return super.onContextItemSelected(item);
}
}
Với tất cả những điều này, việc thực hiện cuối cùng khá đơn giản. Phân đoạn chính phải phân lớp FragmentWithContextMenu. Nó thiết lập RecylerWindow chính bình thường và tự chuyển sang lớp con Bộ điều hợp. Lớp con Bộ điều hợp trông giống như
public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
private final FragmentWithContextMenu fragment;
Adapter(FragmentWithContextMenu fragment) {
this.fragment = fragment;
}
@Override
public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context)
.inflate(R.layout.child_view, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final Adapter.ViewHolder holder, int position) {
// Logic needed to bind holder to specific position
// ......
}
@Override
public int getItemCount() {
// Logic to return current item count
// ....
}
public class ViewHolder extends RecyclerView.ViewHolder implements ContextMenuHandler {
ViewHolder(View view) {
super(view);
((ViewWithContextMenu)view).setContextMenuHandler(fragment, this);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Do stuff to handle simple clicks on child views
// .......
}
});
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
// Logic to set up context menu goes here
// ....
}
@Override
public boolean onContextItemSelected(MenuItem item) {
// Logic to handle context menu item selections goes here
// ....
return true;
}
}
}
Đó là về nó. Tất cả dường như đang hoạt động. Nó đặt tất cả các lớp tiện ích trong một gói ngữ cảnh riêng biệt để tôi có thể đặt tên các lớp phù hợp với các lớp có phân lớp, nhưng tôi nghĩ sẽ khó hiểu hơn.
Được rồi, dựa trên câu trả lời của @ Flexo, tôi sẽ đặt mPosition để đặt hàng ...
protected class ExampleViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener {
int mPosition;
public KWViewHolder(View itemView) {
super(itemView);
itemView.setOnCreateContextMenuListener(this);
}
public void setPosition(int position) {
mPosition = position;
}
@Override
public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
contextMenu.setHeaderTitle(R.string.menu_title_context);
contextMenu.add(0, R.id.menu_delete, mPosition, R.string.delete);
}
}
thì trong onContextItemSelected tôi sử dụng
item.getOrder()
Và tất cả đều hoạt động tốt, tôi có được vị trí của mảng một cách dễ dàng