Używam następującego kodu do obsługi kliknięć wierszy. ( źródło )
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) {
}
}
Działa to jednak, jeśli chcę powiedzieć przycisk usuwania w każdym wierszu. Nie jestem pewien, jak to zaimplementować.
Dołączyłem odbiornik OnClick, aby usunąć przycisk, który działa (usuwa wiersz), ale także uruchamia onclick w pełnym wierszu.
Czy ktoś może mi pomóc, jak uniknąć kliknięcia całego wiersza po kliknięciu jednego przycisku.
Dzięki.
źródło
this
odwołuje się do siebie, ViewHolder (który w tym przypadku jest oddzielną klasą statyczną) - ustawiasz nasłuchiwanie na Viewholder, w którym są powiązane Twoje daneonBindViewHolder()
, nie ma to nic wspólnego z kontekstem adaptera. Nie wiem, jakie dokładnie masz problemy, ale to rozwiązanie działa dobrze.android:background="?attr/selectableItemBackground"
w swoim xml dla widoku?Uważam, że zazwyczaj:
Odpowiedź @ mark-keen działa dobrze, ale posiadanie interfejsu zapewnia większą elastyczność:
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()); } }); } }
Gdzie onClickListener jest zdefiniowany w adapterze:
public MyAdapterListener onClickListener; public interface MyAdapterListener { void iconTextViewOnClick(View v, int position); void iconImageViewOnClick(View v, int position); }
I prawdopodobnie ustawione przez twojego konstruktora:
public MyAdapter(ArrayList<MyListItems> newRows, MyAdapterListener listener) { rows = newRows; onClickListener = listener; }
Następnie możesz obsłużyć zdarzenia w swojej Aktywności lub wszędzie tam, gdzie używany jest Twój RecyclerView:
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);
źródło
onClickListener
do statycznej zagnieżdżonej klasy Viewholder? Chyba że brakuje mi czegoś, nie widzę, jak przekazujesz to do swojego ViewHolder. Jeśli używasz tylko jednej metody interfejsu, możesz użyć wyrażenia Lambda, które wszystko skondensuje.Zależało mi na rozwiązaniu, które nie tworzy żadnych dodatkowych obiektów (tj. Odbiorników), które musiałyby być później zbierane jako śmieci, i nie wymagałoby zagnieżdżania uchwytu widoku w klasie adaptera.
W
ViewHolder
klasieprivate 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()); } }
Warto zwrócić uwagę:
ClickHandler
interfejs jest tutaj zdefiniowany, ale nie zainicjowany, więc wonClick
metodzie nie ma założenia , że został kiedykolwiek zainicjowany.ClickHandler
Interfejs wygląda tak:private interface ClickHandler { void onMyButtonClicked(final int position); }
W adapterze ustaw instancję 'ClickHandler' w konstruktorze i
onBindViewHolder
nadpisz, aby zainicjować `clickHandler 'na uchwycie widoku: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; }
Uwaga: wiem, że viewHolder.clickHandler może być ustawiany wiele razy z dokładnie tą samą wartością, ale jest to tańsze niż sprawdzanie wartości null i rozgałęzień, a nie ma kosztu pamięci, tylko dodatkowa instrukcja.
Na koniec, kiedy tworzysz adapter, jesteś zmuszony przekazać
ClickHandler
instancję do konstruktora, tak jak to: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 } });
Zauważ, że
adapter
jest to zmienna składowa, a nie zmienna lokalnaźródło
Chciałem tylko dodać kolejne rozwiązanie, jeśli masz już odbiornik dotyku recyklera i chcesz obsługiwać wszystkie zdarzenia dotykowe w nim, zamiast zajmować się zdarzeniem dotknięcia przycisku oddzielnie w uchwycie widoku. Kluczową rzeczą, jaką robi ta dostosowana wersja klasy, jest zwrócenie widoku przycisku w wywołaniu zwrotnym onItemClick (), gdy jest on dotknięty, w przeciwieństwie do kontenera elementu. Następnie możesz sprawdzić, czy widok jest przyciskiem i wykonać inną akcję. Uwaga, długie naciśnięcie przycisku jest interpretowane jako długie dotknięcie całego wiersza.
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) { } }
Następnie używając go z aktywności / fragmentu:
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) { } }); }
źródło
onInterceptTouchEvent()
Podczas obsługi zdarzenia kliknięcia musisz zwrócić wartość true w środku .źródło
Możesz najpierw sprawdzić, czy masz podobne wpisy, jeśli otrzymasz kolekcję o rozmiarze 0, rozpocznij nowe zapytanie, aby zapisać.
LUB
bardziej profesjonalny i szybszy sposób. utwórz wyzwalacz w chmurze (przed zapisaniem)
sprawdź tę odpowiedź https://stackoverflow.com/a/35194514/1388852
źródło
Po prostu umieść metodę nadpisywania o nazwie getItemId Pobierz ją prawym przyciskiem myszy> generuj> zastępuj metody> getItemId Umieść tę metodę w klasie Adapter
źródło