处理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) { } }); }