Ich verwende den folgenden Code für die Behandlung von Zeilenklicks. ( Quelle )
static class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
this.clickListener = clickListener;
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
@Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null) {
clickListener.onLongClick(child, recyclerView.getChildPosition(child));
}
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onClick(child, rv.getChildPosition(child));
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
}
Dies funktioniert jedoch, wenn ich in jeder Zeile eine Schaltfläche zum Löschen sagen möchte. Ich bin mir nicht sicher, wie ich das damit umsetzen soll.
Ich habe den OnClick-Listener an die Schaltfläche zum Löschen angehängt, die funktioniert (löscht die Zeile), aber auch den Onclick für die gesamte Zeile auslöst.
Kann mir jemand helfen, wie ich das Klicken in ganzen Zeilen vermeiden kann, wenn auf eine einzelne Schaltfläche geklickt wird?
Vielen Dank.
quelle
this
verweist auf sich selbst, den ViewHolder (in diesem Fall eine separate statische Klasse). Wenn Sie den Listener auf den Viewholder setzen, an den Ihre Daten gebunden sindonBindViewHolder()
, hat dies nichts mit dem Kontext im Adapter zu tun. Ich weiß nicht, welche Probleme Sie genau haben, aber diese Lösung funktioniert gut.android:background="?attr/selectableItemBackground"
in Ihrer XML für die Ansicht?Ich finde das typisch:
Die Antwort von @ mark-scharf funktioniert also gut, aber eine Benutzeroberfläche bietet mehr Flexibilität:
public static class MyViewHolder extends RecyclerView.ViewHolder { public ImageView iconImageView; public TextView iconTextView; public MyViewHolder(final View itemView) { super(itemView); iconImageView = (ImageView) itemView.findViewById(R.id.myRecyclerImageView); iconTextView = (TextView) itemView.findViewById(R.id.myRecyclerTextView); iconTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onClickListener.iconTextViewOnClick(v, getAdapterPosition()); } }); iconImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onClickListener.iconImageViewOnClick(v, getAdapterPosition()); } }); } }
Wo onClickListener in Ihrem Adapter definiert ist:
public MyAdapterListener onClickListener; public interface MyAdapterListener { void iconTextViewOnClick(View v, int position); void iconImageViewOnClick(View v, int position); }
Und wahrscheinlich durch Ihren Konstruktor eingestellt:
public MyAdapter(ArrayList<MyListItems> newRows, MyAdapterListener listener) { rows = newRows; onClickListener = listener; }
Dann können Sie die Ereignisse in Ihrer Aktivität oder überall dort behandeln, wo Ihre RecyclerView verwendet wird:
mAdapter = new MyAdapter(mRows, new MyAdapter.MyAdapterListener() { @Override public void iconTextViewOnClick(View v, int position) { Log.d(TAG, "iconTextViewOnClick at position "+position); } @Override public void iconImageViewOnClick(View v, int position) { Log.d(TAG, "iconImageViewOnClick at position "+position); } }); mRecycler.setAdapter(mAdapter);
quelle
onClickListener
jedoch zur statisch verschachtelten Viewholder-Klasse? Wenn mir nichts fehlt, kann ich nicht sehen, wie Sie es an Ihren ViewHolder weitergeben. Wenn Sie nur eine Schnittstellenmethode verwenden, können Sie auch einen Lambda-Ausdruck verwenden, der alles komprimiert.Ich wollte eine Lösung, die keine zusätzlichen Objekte (dh Listener) erstellt, die später als Müll gesammelt werden müssten, und die kein Verschachteln eines Ansichtshalters in einer Adapterklasse erforderte.
In der
ViewHolder
Klasseprivate static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { private final TextView ....// declare the fields in your view private ClickHandler ClickHandler; public MyHolder(final View itemView) { super(itemView); nameField = (TextView) itemView.findViewById(R.id.name); //find other fields here... Button myButton = (Button) itemView.findViewById(R.id.my_button); myButton.setOnClickListener(this); } ... @Override public void onClick(final View view) { if (clickHandler != null) { clickHandler.onMyButtonClicked(getAdapterPosition()); } }
Zu beachtende Punkte: Die
ClickHandler
Schnittstelle wird hier definiert, aber nicht initialisiert. Daher wird in deronClick
Methode nicht davon ausgegangen , dass sie jemals initialisiert wurde.Die
ClickHandler
Oberfläche sieht folgendermaßen aus:private interface ClickHandler { void onMyButtonClicked(final int position); }
Legen Sie im Adapter eine Instanz von 'ClickHandler' im Konstruktor fest und überschreiben Sie diese
onBindViewHolder
, um 'clickHandler' auf dem Ansichtsinhaber zu initialisieren:private class MyAdapter extends ...{ private final ClickHandler clickHandler; public MyAdapter(final ClickHandler clickHandler) { super(...); this.clickHandler = clickHandler; } @Override public void onBindViewHolder(final MyViewHolder viewHolder, final int position) { super.onBindViewHolder(viewHolder, position); viewHolder.clickHandler = this.clickHandler; }
Hinweis: Ich weiß, dass viewHolder.clickHandler möglicherweise mehrmals mit genau demselben Wert festgelegt wird. Dies ist jedoch billiger als die Überprüfung auf Null und Verzweigung, und es fallen keine Speicherkosten an, sondern nur eine zusätzliche Anweisung.
Wenn Sie den Adapter erstellen, müssen Sie eine
ClickHandler
Instanz wie folgt an den Konstruktor übergeben:adapter = new MyAdapter(new ClickHandler() { @Override public void onMyButtonClicked(final int position) { final MyModel model = adapter.getItem(position); //do something with the model where the button was clicked } });
Beachten Sie, dass dies
adapter
hier eine Mitgliedsvariable ist, keine lokale Variablequelle
Ich wollte nur eine weitere Lösung hinzufügen, wenn Sie bereits einen Recycler-Touch-Listener haben und alle darin enthaltenen Touch-Ereignisse behandeln möchten, anstatt das Touch-Ereignis der Schaltfläche separat im Ansichtshalter zu behandeln. Das Wichtigste, was diese angepasste Version der Klasse tut, ist, die Schaltflächenansicht im onItemClick () - Rückruf zurückzugeben, wenn sie getippt wird, im Gegensatz zum Elementcontainer. Sie können dann testen, ob die Ansicht eine Schaltfläche ist, und eine andere Aktion ausführen. Beachten Sie, dass ein langes Tippen auf die Schaltfläche als langes Tippen auf die gesamte Zeile interpretiert wird.
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener { public static interface OnItemClickListener { public void onItemClick(View view, int position); public void onItemLongClick(View view, int position); } private OnItemClickListener mListener; private GestureDetector mGestureDetector; public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) { mListener = listener; mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { // Important: x and y are translated coordinates here final ViewGroup childViewGroup = (ViewGroup) recyclerView.findChildViewUnder(e.getX(), e.getY()); if (childViewGroup != null && mListener != null) { final List<View> viewHierarchy = new ArrayList<View>(); // Important: x and y are raw screen coordinates here getViewHierarchyUnderChild(childViewGroup, e.getRawX(), e.getRawY(), viewHierarchy); View touchedView = childViewGroup; if (viewHierarchy.size() > 0) { touchedView = viewHierarchy.get(0); } mListener.onItemClick(touchedView, recyclerView.getChildPosition(childViewGroup)); return true; } return false; } @Override public void onLongPress(MotionEvent e) { View childView = recyclerView.findChildViewUnder(e.getX(), e.getY()); if(childView != null && mListener != null) { mListener.onItemLongClick(childView, recyclerView.getChildPosition(childView)); } } }); } public void getViewHierarchyUnderChild(ViewGroup root, float x, float y, List<View> viewHierarchy) { int[] location = new int[2]; final int childCount = root.getChildCount(); for (int i = 0; i < childCount; ++i) { final View child = root.getChildAt(i); child.getLocationOnScreen(location); final int childLeft = location[0], childRight = childLeft + child.getWidth(); final int childTop = location[1], childBottom = childTop + child.getHeight(); if (child.isShown() && x >= childLeft && x <= childRight && y >= childTop && y <= childBottom) { viewHierarchy.add(0, child); } if (child instanceof ViewGroup) { getViewHierarchyUnderChild((ViewGroup) child, x, y, viewHierarchy); } } } @Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { mGestureDetector.onTouchEvent(e); return false; } @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent){} @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } }
Dann benutze es aus Aktivität / Fragment:
recyclerView.addOnItemTouchListener(createItemClickListener(recyclerView)); public RecyclerItemClickListener createItemClickListener(final RecyclerView recyclerView) { return new RecyclerItemClickListener (context, recyclerView, new RecyclerItemClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { if (view instanceof AppCompatButton) { // ... tapped on the button, so go do something } else { // ... tapped on the item container (row), so do something different } } @Override public void onItemLongClick(View view, int position) { } }); }
quelle
Sie müssen true inside zurückgeben,
onInterceptTouchEvent()
wenn Sie das Klickereignis behandeln.quelle
Sie können zuerst überprüfen, ob Sie ähnliche Einträge haben. Wenn Sie eine Sammlung mit der Größe 0 erhalten, starten Sie eine neue Abfrage zum Speichern.
ODER
professioneller und schneller Weg. Erstellen Sie einen Cloud-Trigger (vor dem Speichern)
Überprüfen Sie diese Antwort https://stackoverflow.com/a/35194514/1388852
quelle
Fügen Sie einfach eine Überschreibungsmethode mit dem Namen getItemId ein. Klicken Sie mit der rechten Maustaste auf> generieren> Überschreibungsmethoden> getItemId. Fügen Sie diese Methode in die Adapterklasse ein
quelle