[关闭]
@TryLoveCatch 2022-05-17T10:28:50.000000Z 字数 2467 阅读 532

Android知识体系之滑动冲突

Android知识体系



滑动冲突有两种场景:

有小伙伴肯定有疑问,ViewPager带ListView并没有出现滑动冲突啊。
那是因为ViewPager已经为我们处理了滑动冲突!如果我们自己定义一个水平滑动的ViewGroup内部再使用ListView,那么是一定需要处理滑动冲突的。

滑动方向不一致

由于外部与内部的滑动方向不一致,那么我们可以根据当前滑动方向,水平还是垂直来判断这个事件到底该交给谁来处理。
如何判断滑动的方向是水平还是垂直?

滑动方向一致

由于外部与内部的滑动方向一致,那么不能根据滑动角度、距离差或者速度差来判断。这种情况下必需通过业务逻辑来进行判断。比较常见ScrollView嵌套了ListView。

外部拦截法

父View根据需要对事件进行拦截。逻辑处理放在父View的onInterceptTouchEvent方法中。我们只需要重写父View的onInterceptTouchEvent方法,并根据逻辑需要做相应的拦截即可。

  1. public boolean onInterceptTouchEvent(MotionEvent event) {
  2. boolean intercepted = false;
  3. int x = (int) event.getX();
  4. int y = (int) event.getY();
  5. switch (event.getAction()) {
  6. case MotionEvent.ACTION_DOWN: {
  7. intercepted = false;
  8. break;
  9. }
  10. case MotionEvent.ACTION_MOVE: {
  11. if (满足父容器的拦截要求) {
  12. intercepted = true;
  13. } else {
  14. intercepted = false;
  15. }
  16. break;
  17. }
  18. case MotionEvent.ACTION_UP: {
  19. intercepted = false;
  20. break;
  21. }
  22. default:
  23. break;
  24. }
  25. mLastXIntercept = x;
  26. mLastYIntercept = y;
  27. return intercepted;
  28. }
  • ACTION_DOWN 一定返回false,不要拦截它,否则根据View事件分发机制,后续ACTION_MOVE 与 ACTION_UP事件都将默认交给父View去处理!
  • 原则上ACTION_UP也需要返回false,如果返回true,并且ACTION_MOVE交给子View处理,那么子View将接收不到ACTION_UP事件,子View的onClick事件也无法触发。而父View不一样,如果父View在ACTION_MOVE中开始拦截事件,那么后续ACTION_UP也将默认交给父View处理!

内部拦截法

父View不拦截任何事件,所有事件都传递给子View,子View根据需要决定是自己消费事件还是给父View处理。
这需要子View使用requestDisallowInterceptTouchEvent方法才能正常工作。

子View的dispatchTouchEvent方法的伪代码:

  1. public boolean dispatchTouchEvent(MotionEvent event) {
  2. int x = (int) event.getX();
  3. int y = (int) event.getY();
  4. switch (event.getAction()) {
  5. case MotionEvent.ACTION_DOWN: {
  6. parent.requestDisallowInterceptTouchEvent(true);
  7. break;
  8. }
  9. case MotionEvent.ACTION_MOVE: {
  10. int deltaX = x - mLastX;
  11. int deltaY = y - mLastY;
  12. if (父容器需要此类点击事件) {
  13. parent.requestDisallowInterceptTouchEvent(false);
  14. }
  15. break;
  16. }
  17. case MotionEvent.ACTION_UP: {
  18. break;
  19. }
  20. default:
  21. break;
  22. }
  23. mLastX = x;
  24. mLastY = y;
  25. return super.dispatchTouchEvent(event);
  26. }

父View需要重写onInterceptTouchEvent方法:

  1. public boolean onInterceptTouchEvent(MotionEvent event) {
  2. int action = event.getAction();
  3. if (action == MotionEvent.ACTION_DOWN) {
  4. return false;
  5. } else {
  6. return true;
  7. }
  8. }

参考

一文读懂Android View事件分发机制
一文解决Android View滑动冲突

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注