处理button在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) { } }
这个工作,但是,如果我想要说每行删除button。 我不确定如何实现这一点。
我附加了OnClick侦听器来删除工作(删除行)的button,但它也触发全行onclick。
任何人都可以帮助我如何避免点击一个button时单行点击。
谢谢。
这是我如何处理recyclerView中的多个onClick事件:
编辑:更新,包括callback(如其他评论中所述)。 我在ViewHolder
使用了WeakReference
来消除潜在的内存泄漏。
定义界面:
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 } });
要获取哪个项目被点击,你匹配视图id ievgetId()== whateverItem.getId()
希望这种做法有帮助!
我通常发现:
- 我需要使用多个监听器,因为我有几个button。
- 我想我的逻辑是在活动中,而不是适配器或视图。
所以@ 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);
我想要一个解决scheme,不会创build任何额外的对象(即侦听器),这些对象将不得不稍后进行垃圾回收,并且不需要在适配器类中嵌套视图持有者。
在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可能会被设置多次,但是这比检查空值和分支要便宜,而且没有内存开销,只是一个额外的指令。
最后,在创build适配器时,您必须将一个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
是一个成员variables,而不是本地variables
处理单击事件时,您需要在onInterceptTouchEvent()
内返回true。
只是想添加另一个解决scheme,如果你已经有一个回收触摸监听器,并想处理其中的所有触摸事件,而不是分别处理视图持有者中的button触摸事件。 这个类的适配版本所做的关键是返回onItemClick()callback中的button视图,而不是项目容器。 然后,您可以testing视图是一个button,并执行不同的操作。 请注意,在button上长时间敲击将被解释为整行的长按。
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) { } }); }