[英]Handle Button click inside a row in RecyclerView
I am using following code for handling row clicks. 我正在使用以下代码来处理行点击。 ( source )
( 来源 )
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) {
}
}
This works however, if I want to have say a delete button on each row. 但是,如果我想在每一行上说一个删除按钮,这是有效的。 I am not sure to how to implement that with this.
我不确定如何实现这一点。
I attached OnClick listener to delete button which works (deletes the row) but it also fires the onclick on full row. 我附加了OnClick监听器来删除工作的按钮(删除行),但它也会触发整行上的onclick。
Can anybody help me in how to avoid full row click if a single button is clicked. 如果单击一个按钮,任何人都可以帮助我避免完全行单击。
Thanks. 谢谢。
this is how I handle multiple onClick events inside a recyclerView: 这就是我在recyclelerView中处理多个onClick事件的方法:
Edit : Updated to include callbacks (as mentioned in other comments). 编辑:已更新以包含回调(如其他注释中所述)。 I have used a
WeakReference
in the ViewHolder
to eliminate a potential memory leak. 我在
ViewHolder
使用了WeakReference
来消除潜在的内存泄漏。
Define interface : 定义界面:
public interface ClickListener {
void onPositionClicked(int position);
void onLongClicked(int position);
}
Then the Adapter : 那么适配器:
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;
}
}
}
Then in your activity/fragment - whatever you can implement : Clicklistener
- or anonymous class if you wish like so : 然后在你的活动/片段中 - 你可以实现的任何东西:
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
}
});
To get which item was clicked you match the view id ievgetId() == whateverItem.getId() 要获取单击的项目,您将匹配视图ID ievgetId()== whateverItem.getId()
Hope this approach helps! 希望这种方法有帮助!
I find that typically: 我发现通常:
So @mark-keen's answer works well but having an interface provides more flexibility: 所以@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());
}
});
}
}
Where onClickListener is defined in your adapter: 在适配器中定义onClickListener的位置:
public MyAdapterListener onClickListener;
public interface MyAdapterListener {
void iconTextViewOnClick(View v, int position);
void iconImageViewOnClick(View v, int position);
}
And probably set through your constructor: 并且可能通过构造函数设置:
public MyAdapter(ArrayList<MyListItems> newRows, MyAdapterListener listener) {
rows = newRows;
onClickListener = listener;
}
Then you can handle the events in your Activity or wherever your RecyclerView is being used: 然后,您可以处理Activity中的事件或使用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);
I wanted a solution that did not create any extra objects (ie listeners) that would have to be garbage collected later, and did not require nesting a view holder inside an adapter class. 我想要一个没有创建任何额外对象(即监听器)的解决方案,这些对象必须在以后进行垃圾收集,并且不需要在适配器类中嵌套视图持有者。
In the ViewHolder
class 在
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());
}
}
Points to note: the ClickHandler
interface is defined, but not initialized here, so there is no assumption in the onClick
method that it was ever initialized. 注意事项:
ClickHandler
接口已定义,但未在此处初始化,因此onClick
方法中没有假设它已初始化。
The ClickHandler
interface looks like this: ClickHandler
界面如下所示:
private interface ClickHandler {
void onMyButtonClicked(final int position);
}
In the adapter, set an instance of 'ClickHandler' in the constructor, and override onBindViewHolder
, to initialize `clickHandler' on the view holder: 在适配器中,在构造函数中设置一个'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;
}
Note: I know that viewHolder.clickHandler is potentially getting set multiple times with the exact same value, but this is cheaper than checking for null and branching, and there is no memory cost, just an extra instruction. 注意:我知道viewHolder.clickHandler可能会使用完全相同的值多次设置,但这比检查null和分支更便宜,而且没有内存开销,只需要额外的指令。
Finally, when you create the adapter, you are forced to pass a ClickHandler
instance to the constructor, as so: 最后,在创建适配器时,您必须将
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
}
});
Note that adapter
is a member variable here, not a local variable 请注意,
adapter
在此处是成员变量,而不是局部变量
Just wanted to add another solution if you already have a recycler touch listener and want to handle all of the touch events in it rather than dealing with the button touch event separately in the view holder. 如果您已经拥有一个回收器触摸侦听器并想要处理其中的所有触摸事件而不是在视图持有者中单独处理按钮触摸事件,那么只是想添加另一个解决方案。 The key thing this adapted version of the class does is return the button view in the onItemClick() callback when it's tapped, as opposed to the item container.
这个改编版本的关键是在点击时返回onItemClick()回调中的按钮视图,而不是项容器。 You can then test for the view being a button, and carry out a different action.
然后,您可以测试作为按钮的视图,并执行不同的操作。 Note, long tapping on the button is interpreted as a long tap on the whole row still.
请注意,长按此按钮会被解释为长按整个行。
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) {
}
}
Then using it from activity / fragment: 然后从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) {
}
});
}
处理单击事件时,需要在onInterceptTouchEvent()
内返回true。
You can check if you have any similar entries first, if you get a collection with size 0, start a new query to save. 您可以先检查是否有任何类似的条目,如果您获得大小为0的集合,则启动要保存的新查询。
OR 要么
more professional and faster way. 更专业,更快捷的方式。 create a cloud trigger (before save)
创建云触发器(保存前)
check out this answer https://stackoverflow.com/a/35194514/1388852 看看这个答案https://stackoverflow.com/a/35194514/1388852
只需放置一个名为getItemId的覆盖方法,右键单击> generate> override methods> getItemId将此方法放入Adapter类中
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.