起因:项目需要,布局放置了纵向滑动的scrollview和横向滑动的recycleview,之后为了避免横向滑动时触发纵向移动,重写了两个控件的onInterceptTouchEvent()方法。如下:
public class JudgeNestedScrollView extends NestedScrollView { private boolean isNeedScroll = true; private float xDistance, yDistance, xLast, yLast; private int scaledTouchSlop; public JudgeNestedScrollView(Context context) { super(context, null); } public JudgeNestedScrollView(Context context, AttributeSet attrs) { super(context, attrs, 0); } public JudgeNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); xLast = curX; yLast = curY; //横向移动大于纵向时,不会拦截该事件 return !(xDistance >= yDistance || yDistance < scaledTouchSlop) && isNeedScroll; } return super.onInterceptTouchEvent(ev); } /* 该方法用来处理NestedScrollView是否拦截滑动事件 */ public void setNeedScroll(boolean isNeedScroll) { this.isNeedScroll = isNeedScroll; } }
public class ScrollRecycleView extends RecyclerView { private float lastX, lastY; @Override public boolean onInterceptTouchEvent(MotionEvent e) { boolean intercept = super.onInterceptTouchEvent(e); switch (e.getAction()) { case MotionEvent.ACTION_DOWN: lastX = e.getX(); lastY = e.getY(); break; case MotionEvent.ACTION_MOVE: // 只要横向大于竖向,就拦截掉事件 float slopX = Math.abs(e.getX() - lastX); float slopY = Math.abs(e.getY() - lastY); if((slopX > 0 || slopY > 0) && slopX >= slopY){ requestDisallowInterceptTouchEvent(true); intercept = true; } break; case MotionEvent.ACTION_UP: intercept = false; break; } return intercept; }
然后,在较低分辨率的屏幕上测试,一切安好。换了一个高分辨率的手机(三星galaxy S10)就发生了如标题所说的事情,item点击失效了。在进行了若干调试之后发现,在高分辨率的屏幕上点击item很大概率会触发MotionEvent.ACTION_MOVE事件,由于该事件横向和纵向滑动都已经被拦截,使得事件无法到达item导致点击失效。
解决方法:修改了x和y方向判断值,在一定取值范围内不去拦截事件
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); xLast = curX; yLast = curY; Log.e("SiberiaDante", "xDistance :" + xDistance + "---yDistance:" + yDistance); return !(((xDistance >= yDistance) || yDistance < scaledTouchSlop) || (xDistance < 10 && yDistance < 10)) && isNeedScroll; } return super.onInterceptTouchEvent(ev); }
@Override public boolean onInterceptTouchEvent(MotionEvent e) { boolean intercept = super.onInterceptTouchEvent(e); switch (e.getAction()) { case MotionEvent.ACTION_DOWN: lastX = e.getX(); lastY = e.getY(); break; case MotionEvent.ACTION_MOVE: // 只要横向大于竖向,就拦截掉事件。 float slopX = Math.abs(e.getX() - lastX); float slopY = Math.abs(e.getY() - lastY); if((slopX > 5 || slopY > 5) && slopX >= slopY){ requestDisallowInterceptTouchEvent(true); intercept = true; } break; case MotionEvent.ACTION_UP: intercept = false; break; } return intercept; }
PS:也许有更好的解决方式,将来发现再来更新