RecyclerView长按拖动条目时父布局onTouch()未触发问题求助
问题根因
你当前的onTouch事件未触发核心是以下两个冲突:
- 你在
LocationViewHolder构造方法中调用了mLocationCard.setOnClickListener(this),调用setOnClickListener会强制将对应View的clickable属性设置为true,覆盖你XML中写的android:clickable="false"配置,导致CardView优先消费了所有触摸事件,不会将事件回传给父布局location_rv_item_layout - 你给
mLocationCardLayout设置触摸监听,但没有给它配置android:clickable="true",即便事件能传到该布局,也无法完整接收触摸事件回调。
可行解决方法
方案1:调整触摸监听绑定对象(最快修复)
因为你的CardView已经占满了整个父布局,直接将触摸监听绑定到CardView上即可,不需要绑定到父布局:
修改onBindViewHolder代码:
@Override public void onBindViewHolder(@NotNull LocationViewHolder holder, int position) { // 把原来给mLocationCardLayout设置监听改成给mLocationCard设置 holder.mLocationCard.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { onDragStartListener.onDragStarted(holder); return true; } else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { onDragFinishListener.onDragFinished(locationNameToViewholderPositionMap); return false; } return false; } }); }
如果需要保留点击事件,你可以将点击逻辑合并到触摸监听中判断,或者在触摸事件中判断如果是点击动作再触发点击回调。
方案2:调整事件消费优先级(保留父布局监听)
如果一定要给父布局设置监听,做以下两个修改:
- 在XML的
location_rv_item_layout节点添加属性:
android:clickable="true" android:focusable="true"
- 移除
mLocationCard.setOnClickListener(this)代码,将点击逻辑改到父布局上,或者给CardView设置setOnTouchListener直接返回false不消费事件。
优化建议(长按触发拖动)
你当前代码是按下就触发拖动,会和点击事件严重冲突,建议改用长按触发:
// 在ViewHolder中设置长按监听 holder.mLocationCard.setOnLongClickListener(v -> { onDragStartListener.onDragStarted(holder); return true; }); // 点击监听单独设置不冲突 holder.mLocationCard.setOnClickListener(v -> { int clickedPosition = holder.getAbsoluteAdapterPosition(); onLocationItemClickListener.OnLocationItemClick(clickedPosition); });
另外推荐直接用官方的ItemTouchHelper工具类实现RecyclerView条目拖动,不用自己处理触摸事件逻辑,兼容性和流畅度更好。
内容的提问来源于stack exchange,提问作者Joven Pua
相关产品推荐
相关产品推荐

