Кнопка "Ручка" внутри строки в 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 в полной строке.
Может кто-нибудь помочь мне в том, как избежать щелчка по полной строке, если нажата одна кнопка.
Спасибо.
Ответы
Ответ 1
так я обрабатываю несколько событий onClick внутри recyclerView:
Изменить: Обновлено для включения обратных вызовов (как указано в других комментариях). Я использовал 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
}
});
Чтобы получить, какой элемент был нажат, вы сопоставляете идентификатор вида i.e. v.getId() == whateverItem.getId()
Надеюсь, что этот подход поможет!
Ответ 2
Я нахожу, что обычно:
- Мне нужно использовать несколько прослушивателей, потому что у меня есть несколько кнопок.
- Я хочу, чтобы моя логика была в действии, а не в адаптере или зрителе.
Итак, замечание @mark-heen работает хорошо, но наличие интерфейса обеспечивает большую гибкость:
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);
Ответ 3
Мне нужно решение, которое не создавало никаких дополнительных объектов (т.е. слушателей), которые должны были быть собраны позже, и не требовали вложения в держатель вида внутри класса адаптера.
В классе 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
, который он когда-либо инициализировался, не было допущено.
Интерфейс 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 потенциально получает множество раз с одинаковым значением, но это дешевле, чем проверка на нуль и ветвление, и нет никакой стоимости памяти, просто дополнительная инструкция.
Наконец, при создании адаптера вы вынуждены передать экземпляр 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
является переменной-членом здесь, а не локальной переменной
Ответ 4
Просто захотелось добавить еще одно решение, если у вас уже есть прослушиватель прикосновений, связанных с переработчиком, и вы хотите обрабатывать все сенсорные события в нем, вместо того, чтобы иметь дело с событием касания кнопки отдельно в держателе вида. Ключевая вещь, которую эта адаптированная версия класса делает, - это вернуть представление кнопки в обратном вызове 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) {
}
}
Затем, используя его из действия/фрагмента:
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) {
}
});
}
Ответ 5
Вам нужно вернуть true внутри onInterceptTouchEvent()
, когда вы обрабатываете событие click.
Ответ 6
Вы можете проверить, есть ли у вас похожие записи, если вы получаете коллекцию с размером 0, запустите новый запрос для сохранения.
ИЛИ
более профессиональный и быстрый способ.
создать триггер облака (перед сохранением)
проверить этот ответ
fooobar.com/questions/106892/...
Ответ 7
Просто поместите метод переопределения с именем getItemId. Получите его, щелкнув правой кнопкой мыши> Generate> методы переопределения> getItemId. Поместите этот метод в класс Adapter.