Кнопка ручка нажмите внутри строки в RecyclerView
Я использую следующий код для обработки нажатий подряд. (источник)
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) {
}
}
это работает, однако, если я хочу, чтобы сказать кнопку удаления на каждой строке. Я не уверен, как это реализовать с этим.
я прикрепил прослушиватель OnClick к кнопке удаления, которая работает (удаляет строку), но она также запускает onclick на полной строке.
может ли кто-нибудь помочь мне в том, как избежать полного щелчка строки, если одна кнопка нажатый.
спасибо.
6 ответов:
вот как я обрабатываю несколько событий onClick внутри recyclerView:
Edit: обновлено для включения обратных вызовов (как упоминалось в других комментариях). Я использовал
WeakReference
наViewHolder
для устранения потенциальной утечки памяти.определить интерфейс :
public interface ClickListener { void onPositionClicked(int position); void onLongClicked(int position); }
адаптер :
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { private final ClickListener listener; private final List<MyItems> itemsList; public MyAdapter(List<MyItems> itemsList, ClickListener listener) { this.listener = listener; this.itemsList = itemsList; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.my_row_layout), parent, false), listener); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { // bind layout and data etc.. } @Override public int getItemCount() { return itemsList.size(); } public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { private ImageView iconImageView; private TextView iconTextView; private WeakReference<ClickListener> listenerRef; public MyViewHolder(final View itemView, ClickListener listener) { super(itemView); listenerRef = new WeakReference<>(listener); iconImageView = (ImageView) itemView.findViewById(R.id.myRecyclerImageView); iconTextView = (TextView) itemView.findViewById(R.id.myRecyclerTextView); itemView.setOnClickListener(this); iconTextView.setOnClickListener(this); iconImageView.setOnLongClickListener(this); } // onClick Listener for view @Override public void onClick(View v) { if (v.getId() == iconTextView.getId()) { Toast.makeText(v.getContext(), "ITEM PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show(); } listenerRef.get().onPositionClicked(getAdapterPosition()); } //onLongClickListener for view @Override public boolean onLongClick(View v) { final AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext()); builder.setTitle("Hello Dialog") .setMessage("LONG CLICK DIALOG WINDOW FOR ICON " + String.valueOf(getAdapterPosition())) .setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); builder.create().show(); listenerRef.get().onLongClicked(getAdapterPosition()); return true; } } }
затем в вашей деятельности / фрагмент-все, что вы можете реализовать :
Clicklistener
- или анонимный класс, если вы хотите, как так :MyAdapter adapter = new MyAdapter(myItems, new ClickListener() { @Override public void onPositionClicked(int position) { // callback performed on click } @Override public void onLongClicked(int position) { // callback performed on click } });
чтобы получить, какой элемент был нажат, вы соответствуете идентификатору представления, т. е. v. getId() == whateverItem.getId()
надеюсь, что это помогает!
Я нахожу, что обычно:
- мне нужно использовать несколько слушателей, потому что у меня есть несколько кнопок.
- Я хочу, чтобы моя логика была в действии, а не в адаптере или viewholder.
поэтому ответ @ mark-keen работает хорошо, но наличие интерфейса обеспечивает большую гибкость:
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()); } }); } }
где onClickListener определяется в вашем адаптере:
public MyAdapterListener onClickListener; public interface MyAdapterListener { void iconTextViewOnClick(View v, int position); void iconImageViewOnClick(View v, int position); }
и, вероятно, установить через ваш конструктор:
public MyAdapter(ArrayList<MyListItems> newRows, MyAdapterListener listener) { rows = newRows; onClickListener = listener; }
затем вы можете обрабатывать события в своей деятельности или везде, где используется ваш 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);
Я хотел решение, которое не создавало никаких дополнительно объекты (т. е. слушатели), которые должны были бы быть мусором, собранным позже, и не требовали вложения держателя представления внутри класса адаптера.
на
ViewHolder
классprivate 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()); } }
указывает на примечание:
ClickHandler
интерфейс определен, но не инициализирован здесь, поэтому вonClick
метод, который он когда-то был инициализирован.The
ClickHandler
интерфейс выглядит это:private interface ClickHandler { void onMyButtonClicked(final int position); }
в адаптере установите экземпляр 'ClickHandler' в конструкторе и переопределите
onBindViewHolder
, чтобы инициализировать 'clickHandler' на держателе вида: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; }
примечание: Я знаю, что viewHolder.clickHandler потенциально получает множество раз с одним и тем же значением, но это дешевле, чем проверка на null и ветвление, и нет никакой стоимости памяти, просто дополнительная инструкция.
наконец, когда вы создаете адаптер, вы вынуждены передать
ClickHandler
экземпляр конструктору, как так: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 } });
отметим, что
adapter
является переменной-членом здесь, а не локальной переменной
просто хотел добавить еще одно решение, если у вас уже есть Recycler touch listener и вы хотите обрабатывать все события касания в нем, а не иметь дело с событием касания кнопки отдельно в держателе вида. Ключевая вещь, которую делает эта адаптированная версия класса, - это возврат представления кнопки в обратном вызове onItemClick () при его нажатии, в отличие от контейнера элементов. Затем вы можете проверить, что представление является кнопкой, и выполнить другое действие. Обратите внимание, что долгое нажатие на кнопку интерпретируется как долгое нажатие на весь ряд еще.
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) { } }
затем используя его из activity / 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) { } }); }
вы можете проверить, есть ли у вас какие-либо подобные записи, если вы получаете коллекцию с размером 0, запустите новый запрос для сохранения.
или
более профессиональный и более быстрый путь. создайте облачный триггер (перед сохранением)
проверьте этот ответ https://stackoverflow.com/a/35194514/1388852