ฉันจะใช้เมนูบริบทสำหรับการRecyclerView?
โทรแบบเห็นได้ชัดregisterForContextMenu(recyclerView)
ไม่ได้อย่างไร ฉันเรียกมันจากเศษเสี้ยว มีใครประสบความสำเร็จบ้างในการนำสิ่งนี้ไปใช้
ฉันจะใช้เมนูบริบทสำหรับการRecyclerView?
โทรแบบเห็นได้ชัดregisterForContextMenu(recyclerView)
ไม่ได้อย่างไร ฉันเรียกมันจากเศษเสี้ยว มีใครประสบความสำเร็จบ้างในการนำสิ่งนี้ไปใช้
คำตอบ:
คุณไม่สามารถใช้วิธีการเหล่านี้เช่น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);
}
ตอนนี้เราทำตามขั้นตอนเดียวกันในขณะที่ใช้เมนูบริบท
@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");
}
หากคุณมีปัญหาใด ๆ ถามในความคิดเห็น
onContextItemSelected
กิจกรรม / ส่วนย่อย getTitle
ทำงานได้getItemId
ผล แต่getMenuInfo
ให้ค่าว่าง ดังนั้นวิธีการรับของViewHolder
?
getMenuInfo()
onContextItemSelected()
บางทีคนที่ทำให้มันใช้งานได้อาจมีonCreateContextMenu()
วิธีการในมุมมองที่ไกลขึ้นตามลำดับชั้น ( RecyclerView
หรือFragment
) ซึ่งสามารถทำงานได้ แต่จะนำเราไปสู่คำตอบอื่น ๆ ของคำถามนี้
menu.add(this.getAdapterPosition(), v.getId(), 0, "Call");
แล้วในการทดสอบวิธีการโทรกลับitem.getGroupId()
เพื่อรับตำแหน่ง
ขอบคุณสำหรับข้อมูลและความคิดเห็น ฉันสามารถบรรลุContextMenu
รายการในRecyclerview
.
นี่คือสิ่งที่ฉันทำ
ในonViewCreated
วิธีการของ Fragment หรือonCreate
วิธีการของกิจกรรม:
registerForContextMenu(mRecyclerView);
จากนั้นในอะแดปเตอร์เพิ่ม
private int position;
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
ทำให้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
วิธีการเพิ่มOnLongClickListener
บน holder.itemView เพื่อจับตำแหน่งก่อนโหลดเมนูบริบท:
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
setPosition(holder.getPosition());
return false;
}
});
จากนั้นให้onViewRecycled
นำ Listener ออกเพื่อไม่ให้มีปัญหาในการอ้างอิง (อาจไม่จำเป็นต้องใช้)
@Override
public void onViewRecycled(ViewHolder holder) {
holder.itemView.setOnLongClickListener(null);
super.onViewRecycled(holder);
}
สุดท้ายใน 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);
}
คำตอบปัจจุบันไม่ถูกต้อง นี่คือการใช้งานที่ใช้งานได้:
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;
}
}
ในส่วนของคุณ (หรือกิจกรรม):
@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
}
และสุดท้ายใน ViewHolder ของคุณ:
class MyViewHolder extends RecyclerView.View.ViewHolder {
...
private void onLongClick() {
itemView.showContextMenu();
}
}
getChildPosition()
ตอนนี้เลิกใช้แล้ว ฉันใช้getChildAdapterPosition()
แทน
getChildPosition()
com.android.support:recyclerview-v7:22.0.0
ลองทำ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;
}
});
}
});
คุณสามารถใช้กับการตั้งค่าข้อมูลเป็นViewHolder
รายการ
คำตอบของ 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;
}
};
}
RecyclerView
เพียงเพื่อแทนที่getContextMenuInfo
ฯลฯ แม้ว่าจะไม่ค่อยมีประสิทธิภาพเท่ากับการปล่อยให้ส่วนย่อย / กิจกรรมจัดการกับการคลิกก็ตาม ผู้ฟังจะสามารถเข้าถึงข้อมูลในตัวยึดได้ดังนั้นคุณไม่ควรต้องการตำแหน่ง และในทางทฤษฎีคุณสามารถแคชตำแหน่งเมื่อมีการเชื่อมโยงในอะแดปเตอร์ของคุณได้และใช้การมอบหมายเพื่อเรียกออกจากผู้ถือของคุณหากคุณต้องการแม้ว่าการใช้Context
มุมมองจากมุมมองที่ถูกผูกไว้ในบางครั้งก็เพียงพอแล้ว
คำตอบของ @ มองซิเออร์ได้ผลสำหรับฉัน แต่ต้องแก้ไขโค้ดหลายอย่างก่อน เหมือนกับว่าเขาโพสต์ตัวอย่างโค้ดจากการทำโค้ดซ้ำหลาย ๆ ครั้ง การเปลี่ยนแปลงที่ต้องทำ ได้แก่
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;
}
}
ในส่วนของคุณ:
@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);
}
ใน 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;
}
}
นอกจากนี้อย่าลืมแทนที่RecyclerView
ด้วยContextMenuRecyclerView
ในเลย์เอาต์ของคุณ!
recyclerView.showContextMenuForChild(itemView);
itemView.showContextMenu()
ในคลาสอะแดปเตอร์:
/**
* Custom on long click item listener.
*/
onLongItemClickListener mOnLongItemClickListener;
public void setOnLongItemClickListener(onLongItemClickListener onLongItemClickListener) {
mOnLongItemClickListener = onLongItemClickListener;
}
public interface onLongItemClickListener {
void ItemLongClicked(View v, int position);
}
ในการ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;
}
});
ใน MainActivity (Activity / Fragment) ให้สร้างฟิลด์:
private int mCurrentItemPosition;
ในวัตถุอะแดปเตอร์ของคุณตั้งค่า Listener แบบกำหนดเอง:
mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() {
@Override
public void ItemLongClicked(View v, int position) {
mCurrentItemPosition = position;
}
});
ตอนนี้คุณมีตำแหน่งที่อร่อยสำหรับรายการใด ๆ ที่คุณคลิกมานานแล้ว😋
ใน res -> เมนู
สร้างไฟล์ต่อรายการเมนูของคุณ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>
ใน 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;
}
แสดงเมนูบริบท
registerForContextMenu(mRecyclerView);
mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() {
@Override
public void ItemLongClicked(View v, int position) {
mCurrentItemPosition = position;
v.showContextMenu();
}
});
หวังว่าฉันจะไม่ลืมอะไร🤔
ข้อมูลเพิ่มเติมที่เอกสารเมนู
นี่เป็นวิธีที่ง่ายกว่าในการใช้ 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
}
}
}
ฉันอาจจะไปงานปาร์ตี้สาย แต่ฉันมีวิธีแก้ปัญหาวิธีการทำงานฉันได้ทำส่วนสำคัญสำหรับมัน
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;
}
}
ฉันได้รวมโซลูชันของฉันเข้ากับโซลูชันจาก @Hardik Shah:
ในกิจกรรมฉันมี:
@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);
}
}
ในอะแดปเตอร์ฉันมี:
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);
}
ในส่วนฉันมี:
@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);
}
}
วิธีแก้ปัญหาสำหรับผู้ที่ต้องการรับ item idContextMenu
เมื่อโทร
หากคุณมีRecyclerView
รายการที่มีลักษณะนี้ (มีรายการที่คลิกได้ImageView
):
จากนั้นคุณควรได้รับการติดต่อกลับจาก onClickListener
แล้วคุณควรจะได้รับการเรียกกลับจาก
อะแดปเตอร์
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)
}
}
ส่วนย่อย
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()
}
}
คำเตือน!
หากคุณใช้ส่วนที่ViewPager
อิงตามส่วนเดียว (ทุกหน้าเป็นรายการที่คล้ายกัน) คุณจะประสบปัญหา เมื่อคุณลบล้างonContextItemSelected
เพื่อทำความเข้าใจว่ารายการเมนูใดถูกเลือกคุณจะได้รับรหัสรายการจากหน้าแรก! ที่จะเอาชนะปัญหานี้ดูส่วนผิดใน ViewPager ได้รับโทรศัพท์
สวัสดีพวกผมมาพร้อมกับทางเลือกหนึ่งที่เหมาะกับฉัน ฉันเพิ่งลงทะเบียน itemView ของฉันด้วย registerContextMenu และ ViewHolder Constructor ยังตั้งค่า onLongClikcListener เป็น View เดียวกัน ในการใช้งาน onLongClick (View v) ฉันได้รับตำแหน่งที่คลิกด้วย getLayoutPosition () และบันทึกในตัวแปรอินสแตนซ์ (ฉันสร้างคลาสเพื่อแสดงข้อมูลนี้เช่นเดียวกับที่คาดว่า ContextMenuInfo จะทำงาน) แต่สิ่งที่สำคัญกว่าคือการทำให้ แน่ใจว่าคุณส่งคืนเท็จในวิธีนี้ สิ่งที่คุณต้องทำตอนนี้คือคุณใน onContextItemSelected (รายการ MenuItem) อ่านข้อมูลที่คุณเก็บไว้ในตัวแปรอินสแตนซ์ของคุณและหากถูกต้องคุณจะดำเนินการต่อ นี่คือตัวอย่างข้อมูล
public MyViewHolder(View itemView){
super(itemView);
registerForContextMenu(itemView);
itemView.setOnLongClickListener(this);
}
ฉันทำให้ ViewHolder ใช้ OnLongClickListener แต่คุณสามารถทำได้ในแบบที่คุณต้องการ
@Override
public boolean onLongClick(View v){
mCurrentLongItem = new ListItemInfo(v.getId(), getLayoutPosition());
return false; // REMEMBER TO RETURN FALSE.
}
คุณยังสามารถตั้งค่านี้ในอะแดปเตอร์หรือเป็นมุมมองอื่นที่คุณมีใน ViewHolder (เช่น TextView) สิ่งสำคัญคือการใช้งาน 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);
}
ส่วนที่ดีที่สุดคือคุณยังสามารถประมวลผลเหตุการณ์ LongClick ที่ส่งคืนจริงในกรณีที่คุณต้องการและ conextMenu จะไม่ปรากฏขึ้น
วิธีนี้ใช้ได้ผลเนื่องจาก registerForContextView ทำให้ View LongClickable และเมื่อถึงเวลาประมวลผล ContextMenu ระบบจะเรียกใช้ performLongClick ซึ่งเรียกการใช้งาน onLongClick เป็นครั้งแรกและหากส่งคืนเป็นเท็จระบบจะเรียกใช้ showContextMenu
ฉันใช้วิธีแก้ปัญหานี้มาระยะหนึ่งแล้วและได้ผลดีสำหรับฉัน
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;
}
}
ตอนนี้ในส่วนหรือกิจกรรมของคุณใช้วิธีการต่อไปนี้
@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;
}
สุดท้ายลงทะเบียนสำหรับ contextMenu ในมุมมองรีไซเคิล
//for showing a popup on LongClick of items in recycler.
registerForContextMenu(recyclerView);
ที่น่าทำงาน!
นี่คือวิธีที่คุณสามารถใช้เมนูบริบทสำหรับ RecyclerView และรับตำแหน่งของรายการซึ่งรายการเมนูบริบทถูกเลือก:
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;
}
}
}
ฉันดิ้นรนกับเรื่องนี้เพราะ Android ไม่ได้จัดการสิ่งนี้ให้ฉันใน RecyclerView ซึ่งทำงานได้ดีมากสำหรับ ListView
ชิ้นส่วนที่ยากที่สุดคือชิ้นส่วน ContextMenuInfo ถูกฝังอยู่ใน View ซึ่งคุณไม่สามารถแนบได้อย่างง่ายดายนอกจากการแทนที่ View
ดังนั้นคุณจะต้องมีกระดาษห่อหุ้มที่ช่วยส่งข้อมูลตำแหน่งไปยังกิจกรรม
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;
}
}
จากนั้นใน RecyclerAdapter ของคุณเมื่อคุณสร้าง ViewHolders คุณต้องตั้งค่า Wrapper เป็นมุมมองรูทและลงทะเบียน contextMenu ในแต่ละมุมมอง
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);
}
}
และสุดท้ายในกิจกรรมของคุณคุณจะสามารถทำสิ่งที่คุณมักจะทำ:
@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
วิธีที่ดีที่สุดคือการใช้เมนูบริบทที่มีมุมมองผู้รีไซเคิลคือถ้าคุณสร้างมุมมองผู้รีไซเคิลแบบกำหนดเองและแทนที่getContextMenuInfo()
วิธีการและส่งคืนอินสแตนซ์ของวัตถุข้อมูลเมนูบริบทของคุณเองเพื่อให้คุณสามารถดึงตำแหน่งเมื่อสร้างขึ้นและเมื่อคลิกเมนู:
@Override
protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
return mContextMenuInfo;
}
ดูส่วนสำคัญที่ฉันสร้างขึ้นนี้:
ขยายคำตอบด้านบนเล็กน้อยหากคุณต้องการหลีกเลี่ยงการกำหนดเมนูด้วยตนเองในโค้ดของคุณใน Adapter / ViewHolder คุณสามารถใช้ PopupMenu และขยายตัวเลือกเมนูจากไฟล์ทรัพยากร menu.xml มาตรฐาน
ตัวอย่างด้านล่างแสดงสิ่งนี้รวมถึงความสามารถในการส่งผ่าน Listener ที่คุณสามารถนำไปใช้ใน Fragment / Activity เพื่อตอบสนองต่อการคลิกเมนูบริบท
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();
}
}
ข้อมูลสำคัญทั้งหมดที่นี่ https://gist.github.com/brettwold/45039b7f02ce752ae0d32522a8e2ad9c
คุณสามารถส่ง OnCreateContextMenuListener ไปยัง ViewHolder เมื่อผูก Listener นี้สามารถสร้างเมนูแบบกำหนดเองสำหรับข้อมูลแต่ละรายการ เพียงเพิ่ม setOnCreateContextMenuListener ใน ViewHolder ของคุณและเรียกใช้ในระหว่างการผูก
public static class ItemViewHolder extends RecyclerView.ViewHolder
{
public ItemViewHolder(View itemView) {
super(itemView);
}
void setOnCreateContextMenuListener(View.OnCreateContextMenuListener listener) {
itemView.setOnCreateContextMenuListener(listener);
}
}
ในอะแดปเตอร์:
@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);
}
}
});
}
ในกรณีของฉันฉันต้องใช้ข้อมูลจากส่วนของฉันในonContextItemSelected()
วิธีการ วิธีแก้ปัญหาที่ฉันลงเอยด้วยการส่งอินสแตนซ์ของชิ้นส่วนไปยังอะแดปเตอร์ของฉันและลงทะเบียนรายการมุมมองในที่ยึดมุมมอง:
@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);
}
จากนั้นonCreateContextMenu()
คุณสามารถบันทึกดัชนีเป็นตัวแปรท้องถิ่น:
selectedViewIndex = (int)v.getTag();
และดึงข้อมูลเข้ามา onContextItemSelected()
ครั้งแรกที่ฉันพบปัญหานี้กับอะแดปเตอร์ปกติฉันลงเอยด้วยการสร้างคลาสย่อย View ที่กำหนดเองของฉันเองและจัดเก็บสิ่งที่ฉันต้องการ ฉันไม่ชอบวิธีแก้ปัญหานั้นจริงๆและใช้เวลาส่วนใหญ่ในการดูความคิดที่ยอดเยี่ยมที่ผู้คนเสนอมาและตัดสินใจว่าฉันไม่ชอบพวกเขาไปมากกว่านี้แล้ว ฉันก็เลยรวบรวมทุกอย่างเข้าด้วยกันเขย่ามันสักพักแล้วก็ออกมาพร้อมกับสิ่งใหม่ที่ฉันชอบ
เราเริ่มต้นด้วยคลาสยูทิลิตี้สองสามคลาส ContextMenuHandler เป็นอินเทอร์เฟซสำหรับวัตถุใด ๆ ที่จะจัดการกับเมนูบริบท ในทางปฏิบัตินี่จะเป็นคลาสย่อยของ ViewHolder แต่ในทางทฤษฎีมันอาจเป็นอะไรก็ได้
/**
* 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);
}
ถัดไปคืออินเทอร์เฟซที่จะต้องใช้งานโดย View ใด ๆ ที่จะถูกใช้เป็นส่วนย่อยของ RecyclerView ในทันที
public interface ViewWithContextMenu {
public void setContextMenuHandler(FragmentWithContextMenu fragment, ContextMenuHandler handler);
public ContextMenuHandler getContextMenuHandler();
}
ถัดไปมุมมองใด ๆ ที่จะสร้างเมนูบริบทในฐานะลูกของ RecylcerView จะต้องใช้ ViewWIthContextMenu ในกรณีของฉันฉันต้องการคลาสย่อยของ 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;
}
}
และสุดท้ายเราจำเป็นต้องมีคลาส Fragment ที่ถูกปรับปรุงเพื่อสกัดกั้นการเรียกเมนูบริบทและเปลี่ยนเส้นทางไปยังตัวจัดการที่เหมาะสม
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);
}
}
ด้วยเหตุนี้การใช้งานขั้นสุดท้ายจึงค่อนข้างง่าย แฟรกเมนต์หลักต้องมีคลาสย่อย FragmentWithContextMenu ตั้งค่า RecylerWindow หลักตามปกติและส่งผ่านตัวเองไปยังคลาสย่อยของ Adapter คลาสย่อยของ Adapter มีลักษณะดังนี้
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;
}
}
}
เกี่ยวกับมัน. ดูเหมือนจะใช้งานได้ทั้งหมด มันใส่คลาสยูทิลิตี้ทั้งหมดไว้ในแพ็คเกจเมนูบริบทแยกต่างหากดังนั้นฉันสามารถตั้งชื่อคลาสที่ตรงกับคลาสที่มีคลาสย่อยได้ แต่ฉันคิดว่าน่าจะสับสนมากกว่านี้
ตกลงจากคำตอบของ @ Flexo ฉันจะใส่ mPosition เพื่อสั่ง ...
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);
}
}
จากนั้นในonContextItemSelectedฉันใช้
item.getOrder()
และทำงานได้ดีฉันได้ตำแหน่งอาร์เรย์อย่างง่ายดาย