Wie implementiere ich ein Kontextmenü für RecyclerView?
Anscheinend registerForContextMenu(recyclerView)
funktioniert das Aufrufen nicht. Ich nenne es aus einem Fragment. Hat jemand Erfolg damit gehabt?
Wie implementiere ich ein Kontextmenü für RecyclerView?
Anscheinend registerForContextMenu(recyclerView)
funktioniert das Aufrufen nicht. Ich nenne es aus einem Fragment. Hat jemand Erfolg damit gehabt?
Antworten:
Sie können nicht direkt diese Methode wie implementieren OnClickListener , OnContextMenuListener usw. weil RecycleView erstreckt android.view.ViewGroup . Wir können diese Methode also nicht direkt anwenden. Wir können diese Methoden in der ViewHolder- Adapterklasse implementieren . Wir können das Kontextmenü in RecycleView folgendermaßen verwenden:
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);
}
Jetzt gehen wir genauso vor, während wir das Kontextmenü implementieren.
@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");
}
Wenn Sie Schwierigkeiten haben, fragen Sie im Kommentar.
onContextItemSelected
Aktivität / Fragmentebene. getTitle
funktioniert, getItemId
funktioniert, getMenuInfo
liefert aber null. Also, wie bekommt man das in den Griff ViewHolder
?
getMenuInfo()
gibt null in zurück onContextItemSelected()
. Vielleicht haben die Leute, die es zum Laufen gebracht haben, eine onCreateContextMenu()
Methode im Blick weiter oben in der Hierarchie (das RecyclerView
oder Fragment
)? Das kann funktionieren, bringt uns dann aber zu anderen Antworten auf diese Frage.
menu.add(this.getAdapterPosition(), v.getId(), 0, "Call");
und dann in Ihrer item.getGroupId()
Danke für die Infos und Kommentare. Ich konnte ContextMenu
für Artikel in erreichen Recyclerview
.
Hier ist was ich getan habe
in der onViewCreated
Methode von Fragment oder der onCreate
Methode von Activity :
registerForContextMenu(mRecyclerView);
Dann im Adapter hinzufügen
private int position;
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
Lassen Sie die ViewHolder
Klasse implementierenOnCreateContextMenuListener
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
Methode Add OnLongClickListener
on the Holder.itemView, um die Position zu erfassen, bevor das Kontextmenü geladen wird:
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
setPosition(holder.getPosition());
return false;
}
});
Dann onViewRecycled
entfernen Sie den Hörer so , dass es keine Referenzfragen. (möglicherweise nicht erforderlich).
@Override
public void onViewRecycled(ViewHolder holder) {
holder.itemView.setOnLongClickListener(null);
super.onViewRecycled(holder);
}
Schließlich überschreiben Sie im Fragment / Aktivität das onContextItemSelected
wie unter:
@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);
}
Die aktuelle Antwort ist nicht korrekt. Hier ist eine funktionierende Implementierung:
public class ContextMenuRecyclerView extends RecyclerView {
private RecyclerViewContextMenuInfo mContextMenuInfo;
@Override
protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
return mContextMenuInfo;
}
@Override
public boolean showContextMenuForChild(View originalView) {
final int longPressPosition = getChildPosition(originalView);
if (longPressPosition >= 0) {
final long longPressId = getAdapter().getItemId(longPressPosition);
mContextMenuInfo = new RecyclerViewContextMenuInfo(longPressPosition, longPressId);
return super.showContextMenuForChild(originalView);
}
return false;
}
public static class RecyclerViewContextMenuInfo implements ContextMenu.ContextMenuInfo {
public RecyclerViewContextMenuInfo(int position, long id) {
this.position = position;
this.id = id;
}
final public int position;
final public long id;
}
}
In Ihrem Fragment (oder Ihrer Aktivität):
@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
}
Und schließlich in Ihrem ViewHolder:
class MyViewHolder extends RecyclerView.View.ViewHolder {
...
private void onLongClick() {
itemView.showContextMenu();
}
}
getChildPosition()
ist jetzt veraltet. Ich habe getChildAdapterPosition()
stattdessen verwendet.
getChildPosition()
ist veraltet in com.android.support:recyclerview-v7:22.0.0
.
Versuchen Sie dies für ein View
Element in 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;
}
});
}
});
Sie können es verwenden, um Daten für ein ViewHolder
Element festzulegen
Die Antwort von Prabhakar ist korrekt, aber er hat nicht erklärt, wie man Daten in Bezug auf das gedrückte Element erhält, wenn ein Kontextmenüelement ausgewählt wird. Wir können einen onContextItemSelected
Rückruf verwenden, sind jedoch in diesem Fall ContextMenuInfo
nicht verfügbar ( null
) (wenn die getContextMenuInfo()
Methode für eine gedrückte Ansicht nicht überschrieben wird). Die einfachste Lösung besteht also darin, OnMenuItemClickListener
direkt zum hinzuzufügen 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
nur zum Überschreiben getContextMenuInfo
usw. dienen sollen, auch wenn dies nicht ganz so effizient ist, wie das Fragment / die Aktivität Klicks verarbeiten zu lassen. Die Zuhörer haben Zugriff auf die Daten im Halter, sodass Sie keine Position benötigen sollten. Und theoretisch könnten Sie die Position beim Binden in Ihrem Adapter trotzdem zwischenspeichern und die Delegierung verwenden, um aus Ihrem Halter herauszurufen, wenn Sie müssen, obwohl die Verwendung der Context
aus einer der gebundenen Ansichten manchmal ausreichend sein kann.
@ Renauds Antwort funktionierte für mich, erforderte jedoch zuerst einige Code-Korrekturen. Es ist, als hätte er Ausschnitte aus verschiedenen Iterationen seines Codes gepostet. Die Änderungen, die vorgenommen werden müssen, sind:
RecyclerContextMenuInfo
und RecyclerViewContextMenuInfo
sind die gleiche Klasse. Wähle einen Namen und bleibe dabei.ViewHolder
muss implementiert werden View.OnLongClickListener
, und denken Sie daran, setOnLongClickListener()
das Element im Konstruktor aufzurufen .onLongClick()
Hörer getView().showContextMenu()
ist das völlig falsch. Sie müssen showContextMenuForChild()
Ihre anrufen ContextMenuRecyclerView
, sonst werden ContextMenuInfo
Sie einsteigen onCreateContextMenu()
und onContextItemSelected()
werden null sein.Mein bearbeiteter Code unten:
ContextMenuRecyclerView:
public class ContextMenuRecyclerView extends RecyclerView {
private RecyclerViewContextMenuInfo mContextMenuInfo;
@Override
protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
return mContextMenuInfo;
}
@Override
public boolean showContextMenuForChild(View originalView) {
final int longPressPosition = getChildPosition(originalView);
if (longPressPosition >= 0) {
final long longPressId = getAdapter().getItemId(longPressPosition);
mContextMenuInfo = new RecyclerViewContextMenuInfo(longPressPosition, longPressId);
return super.showContextMenuForChild(originalView);
}
return false;
}
public static class RecyclerViewContextMenuInfo implements ContextMenu.ContextMenuInfo {
public RecyclerViewContextMenuInfo(int position, long id) {
this.position = position;
this.id = id;
}
final public int position;
final public long id;
}
}
In Ihrem Fragment:
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mRecyclerView = view.findViewById(R.id.recyclerview);
registerForContextMenu(mRecyclerView);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
// inflate menu here
// If you want the position of the item for which we're creating the context menu (perhaps to add a header or something):
int itemIndex = ((ContextMenuRecyclerView.RecyclerViewContextMenuInfo) menuInfo).position;
}
@Override
public boolean onContextItemSelected(MenuItem item) {
ContextMenuRecyclerView.RecyclerViewContextMenuInfo info = (ContextMenuRecyclerView.RecyclerViewContextMenuInfo) item.getMenuInfo();
// handle menu here - get item index or ID from info
return super.onContextItemSelected(item);
}
In Ihrem ViewHolder:
class MyViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener {
public MyViewHolder( View itemView ) {
super( itemView );
itemView.setOnLongClickListener( this );
}
@Override public boolean onLongClick() {
recyclerView.showContextMenuForChild( v );
return true;
}
}
Also, stellen Sie sicher , dass Sie ersetzen RecyclerView
mit ContextMenuRecyclerView
im Layout!
recyclerView.showContextMenuForChild(itemView);
durch itemView.showContextMenu()
.
In der Adapterklasse:
/**
* Custom on long click item listener.
*/
onLongItemClickListener mOnLongItemClickListener;
public void setOnLongItemClickListener(onLongItemClickListener onLongItemClickListener) {
mOnLongItemClickListener = onLongItemClickListener;
}
public interface onLongItemClickListener {
void ItemLongClicked(View v, int position);
}
In onBindViewHolder
Hook der benutzerdefinierte Listener:
// 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;
}
});
Erstellen Sie in MainActivity (Aktivität / Fragment) ein Feld:
private int mCurrentItemPosition;
Legen Sie in Ihrem Adapterobjekt den benutzerdefinierten Listener fest:
mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() {
@Override
public void ItemLongClicked(View v, int position) {
mCurrentItemPosition = position;
}
});
Jetzt haben Sie eine leckere Position für jeden Gegenstand, auf den Sie lange geklickt haben 😋
In res -> menu
Erstellen Sie eine Datei mit Ihrem Menüpunkt context_menu_main.xml
:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/delete" android:title="Delete"/>
<item android:id="@+id/share" android:title="Share"/>
</menu>
In MainActivity:
Implementieren Sie beide onCreateContextMenu
und 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;
}
Zeigen Sie das Kontextmenü an.
registerForContextMenu(mRecyclerView);
mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() {
@Override
public void ItemLongClicked(View v, int position) {
mCurrentItemPosition = position;
v.showContextMenu();
}
});
Hoffe ich vergesse nichts 🤔
Weitere Infos unter Menüdokumentation
Hier ist eine einfachere Möglichkeit, dies mit Kotlin zu tun, die für mich funktioniert hat. Die größte Herausforderung besteht darin, die Position des Gegenstandes herauszufinden, der gedrückt wurde. In Ihrem Adapter können Sie dieses Code-Snippet platzieren und es kann die Position des Elements erfassen, für das das Kontextmenü angezeigt wird. das ist alles.
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
}
}
}
Ich habe meine Lösung mit der Lösung von @Hardik Shah kombiniert:
In der Aktivität habe ich:
@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);
}
}
Im Adapter habe ich:
private MainActivity context;
private int position;
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
public QuestsAdapter(MainActivity context, List<Quest> objects) {
this.context = context;
this.quests.addAll(objects);
}
public class QuestViewHolder extends RecyclerView.ViewHolder {
private QuestItemBinding questItemBinding;
public QuestViewHolder(View v) {
super(v);
questItemBinding = DataBindingUtil.bind(v);
v.setOnCreateContextMenuListener(context);
}
}
@Override
public void onBindViewHolder(final QuestViewHolder holder, int position) {
Quest quest = quests.get(position);
holder.questItemBinding.setQuest(quest);
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
setPosition(holder.getAdapterPosition());
return false;
}
});
}
@Override
public void onViewRecycled(QuestViewHolder holder) {
holder.itemView.setOnLongClickListener(null);
super.onViewRecycled(holder);
}
In Fragment habe ich:
@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);
}
}
Ich bin vielleicht zu spät zur Party, aber ich habe eine funktionierende Lösung . Ich habe einen Kern dafür gemacht.
Kontextmenü zu RecyclerView hinzufügen
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;
}
}
Hallo Leute, es ist eine Alternative herausgekommen, die für mich funktioniert. Ich registriere einfach meine itemView bei registerContextMenu im ViewHolder-Konstruktor und setze auch einen onLongClikcListener auf dieselbe Ansicht. In der Implementierung von onLongClick (View v) erhalte ich einfach die angeklickte Position mit getLayoutPosition () und speichere sie in einer Instanzvariablen (ich habe eine Klasse erstellt, um diese Daten darzustellen, genau wie ContextMenuInfo funktionieren soll), aber wichtiger ist es, dies zu tun Stellen Sie sicher, dass Sie bei dieser Methode false zurückgeben . Jetzt müssen Sie nur noch onContextItemSelected (MenuItem-Element) in sich aufnehmen, die Daten lesen, die Sie in Ihrer Instanzvariablen speichern, und wenn sie gültig sind, fahren Sie mit Ihren Aktionen fort. Hier ist es ein Ausschnitt.
public MyViewHolder(View itemView){
super(itemView);
registerForContextMenu(itemView);
itemView.setOnLongClickListener(this);
}
Ich mache ViewHolder implementiert OnLongClickListener, aber Sie können es auf jede Weise tun, die Sie bevorzugen.
@Override
public boolean onLongClick(View v){
mCurrentLongItem = new ListItemInfo(v.getId(), getLayoutPosition());
return false; // REMEMBER TO RETURN FALSE.
}
Sie können dies auch im Adapter oder in einer anderen Ansicht im ViewHolder (dh einer Textansicht) festlegen. Wichtig ist die Implementierung von 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);
}
Das Beste daran ist, dass Sie das LongClick-Ereignis, das in den gewünschten Fällen true zurückgibt, weiterhin verarbeiten können und das conextMenu nicht angezeigt wird.
Diese Methode funktioniert, weil registerForContextView die Ansicht LongClickable macht und wenn es Zeit ist, das ContextMenu zu verarbeiten, ruft das System performLongClick auf, das zuerst eine onLongClick-Implementierung aufruft, und wenn es false zurückgibt, ruft es showContextMenu auf.
Ich benutze diese Lösung seit einiger Zeit und habe ziemlich gut für mich funktioniert.
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;
}
}
Implementieren Sie nun in Ihrem Fragment oder Ihrer Aktivität die folgenden Methoden.
@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;
}
Registrieren Sie sich schließlich für das Kontextmenü in der Recycling-Ansicht
//for showing a popup on LongClick of items in recycler.
registerForContextMenu(recyclerView);
Das sollte funktionieren!
So können Sie das Kontextmenü für RecyclerView implementieren und die Position des Elements abrufen, für das das Kontextmenüelement ausgewählt wurde:
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;
}
}
}}
Eine Lösung für diejenigen, die beim Anruf die Artikel-ID erhalten möchten ContextMenu
.
Wenn Sie eine RecyclerView
mit Elementen wie diesen haben (mit anklickbaren ImageView
):
dann sollten Sie Rückrufe von erhalten onClickListener
.
Adapter
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)
}
}
Fragment
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()
}
}
Warnung!
Wenn Sie ein ViewPager
Fragment verwenden, das auf einem Fragment basiert (alle Seiten sind ähnliche Listen), tritt ein Problem auf. Wenn Sie überschreiben, um onContextItemSelected
zu verstehen, welcher Menüpunkt ausgewählt wurde, erhalten Sie auf der ersten Seite eine Listenelement-ID! Um dieses Problem zu beheben, lesen Sie Falsches Fragment in ViewPager empfängt den Aufruf onContextItemSelected .
Ich habe Probleme damit, weil Android dies in RecyclerView, das für ListView sehr gut funktioniert hat, für mich nicht gut handhabt.
Das schwierigste Teil ist, dass das ContextMenuInfo-Teil in eine Ansicht eingebettet ist, die Sie nur durch Überschreiben der Ansicht anhängen können.
Sie benötigen also einen Wrapper, mit dem Sie die Positionsinformationen an die Aktivität senden können.
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;
}
}}
Wenn Sie dann in Ihrem RecyclerAdapter ViewHolders erstellen, müssen Sie den Wrapper als Stammansicht festlegen und in jeder Ansicht das Kontextmenü registrieren.
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);
}
}}
Und schließlich können Sie in Ihrer Aktivität das tun, was Sie normalerweise tun:
@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
Am besten verwenden Sie das Kontextmenü mit der Recycler-Ansicht, wenn Sie eine benutzerdefinierte Recycler-Ansicht erstellen, die getContextMenuInfo()
Methode überschreiben und Ihre eigene Instanz des Kontextmenü-Infoobjekts zurückgeben, damit Sie Positionen abrufen können, wenn es erstellt wurde und wenn auf das Menü geklickt wird:
@Override
protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
return mContextMenuInfo;
}
Schauen Sie sich diesen Kern an, den ich erstellt habe:
Wenn Sie vermeiden möchten, das Menü in Ihrem Code im Adapter / ViewHolder manuell zu definieren, können Sie ein Popup-Menü verwenden und die Menüoptionen aus einer Standardressourcendatei menu.xml aufblasen.
Das folgende Beispiel zeigt dies, einschließlich der Möglichkeit, einen Listener zu übergeben, den Sie in Ihrem Fragment / Ihrer Aktivität implementieren können, um auf Kontextmenüklicks zu reagieren.
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();
}
}
Vollständiger Überblick hier https://gist.github.com/brettwold/45039b7f02ce752ae0d32522a8e2ad9c
Sie können OnCreateContextMenuListener beim Binden an ViewHolder übergeben. Dieser Listener kann für jedes Datenelement ein benutzerdefiniertes Menü erstellen. Fügen Sie einfach setOnCreateContextMenuListener in Ihren ViewHolder ein und rufen Sie es während der Bindung auf.
public static class ItemViewHolder extends RecyclerView.ViewHolder
{
public ItemViewHolder(View itemView) {
super(itemView);
}
void setOnCreateContextMenuListener(View.OnCreateContextMenuListener listener) {
itemView.setOnCreateContextMenuListener(listener);
}
}}
Im Adapter:
@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);
}
}
});
}
In meinem Fall musste ich Daten aus meinem Fragment in der onContextItemSelected()
Methode verwenden. Die Lösung bestand darin, eine Instanz des Fragments an meinen Adapter zu übergeben und das Ansichtselement im Ansichtshalter zu registrieren:
@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);
}
Dann onCreateContextMenu()
können Sie den Index in einer lokalen Variablen speichern:
selectedViewIndex = (int)v.getTag();
und abrufen in onContextItemSelected()
Als ich zum ersten Mal mit normalen Adaptern auf dieses Problem stieß, erstellte ich meine eigene benutzerdefinierte Ansichtsunterklasse und speicherte das benötigte Material darin. Ich mochte diese Lösung wirklich nicht und verbrachte viel Zeit damit, mir die großartigen Ideen anzusehen, die die Leute vorgeschlagen haben, und entschied, dass ich sie nicht besser mochte. Also habe ich alles zusammengefügt, eine Weile herumgeschüttelt und etwas Neues herausgebracht, das mir gefällt.
Wir beginnen mit ein paar Utility-Klassen. ContextMenuHandler ist eine Schnittstelle für jedes Objekt, das das Kontextmenü verarbeiten soll. In der Praxis wird dies eine ViewHolder-Unterklasse sein, aber theoretisch könnte es fast alles sein
/**
* 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);
}
Als nächstes folgt eine Schnittstelle, die von jeder Ansicht implementiert werden muss, die als unmittelbares untergeordnetes Element einer RecyclerView verwendet werden soll.
public interface ViewWithContextMenu {
public void setContextMenuHandler(FragmentWithContextMenu fragment, ContextMenuHandler handler);
public ContextMenuHandler getContextMenuHandler();
}
Als Nächstes muss jede Ansicht, die als untergeordnetes Element einer RecylcerView ein Kontextmenü erstellen soll, ViewWIthContextMenu implementieren. In meinem Fall brauchte ich nur eine Unterklasse von 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;
}
}
Und schließlich benötigen wir eine aufgemotzte Fragment-Klasse, um die Kontextmenü-Aufrufe abzufangen und an den entsprechenden Handler umzuleiten.
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);
}
}
Mit all dem ist die endgültige Implementierung ziemlich einfach. Das Hauptfragment muss FragmentWithContextMenu unterordnen. Es richtet das Haupt-RecylerWindow normal ein und übergibt sich selbst an die Adapter-Unterklasse. Die Adapter-Unterklasse sieht aus wie
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;
}
}
}
Das ist alles. Es scheint alles zu funktionieren. Alle Utility-Klassen wurden in ein separates Kontextmenü-Paket gestellt, sodass ich die Klassennamen hätte angeben können, die mit den dortigen Klassen übereinstimmen, aber ich dachte, das wäre verwirrender.
Ok, basierend auf der Antwort von @ Flexo werde ich mPosition bestellen ...
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);
}
}
dann verwende ich in onContextItemSelected
item.getOrder()
Und alles funktioniert gut Ich bekomme leicht die Position des Arrays