Android事件分发机制四:学了事件分发有什么用? (3)

代码的实现思路很简单,记录两次触控点的位置,然后计算出斜率来判断是垂直还是水平滑动。代码中有个需要注意的点:viewGroup不能拦截up事件和down事件。如果拦截了down事件那么子view将永远接收不到事件信息;如果拦截了up事件那么子view将永远无法触发点击事件。

上面的代码是事件分发的核心代码,更加具体的代码还需要根据实际需求去完善细节,但整体的逻辑是不变的。

内部拦截法的思路和外部拦截的思路很像,只是判断的位置放到了内部view中。内部拦截法意味着内部view必须要有控制事件流走向的能力,才能对事件进行处理。这里就运用到了内部view一个重要的方法: requestDisallowInterceptTouchEvent 。

这个方法可以强制外层viewGroup不拦截事件。因此,我们可以让viewGroup默认拦截除了down事件以外的所有事件。当子view需要处理事件时,只需要调用此方法即可获取事件;而当想要把事件交给viewGroup处理时,那么只需要取消这个标志,外层viewGroup就会拦截所有事件。从而达到内部view控制事件流走向的目的。

代码实现需要分两步走,首先是设置外部viewGroup拦截除了down事件以外的所有事件(这里用viewPager和ListView来进行代码演示):

public class MyViewPager extends ViewPager { public boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.getActionMasked()==MotionEvent.ACTION_DOWN){ return false; } return true; } }

接下来需要重写内部view的dispatchTouchEvent方法:

public class MyListView extends ListView { float lastX = 0; float lastY = 0; @Override public boolean dispatchTouchEvent(MotionEvent ev) { int actionMarked = ev.getActionMasked(); switch (actionMarked){ // down事件,必须请求不拦截,否则拿不到move事件无法进行判断 case MotionEvent.ACTION_DOWN:{ requestDisallowInterceptTouchEvent(true); break; } // move事件,进行判断是否处理事件 case MotionEvent.ACTION_MOVE:{ float x = ev.getX(); float y = ev.getY(); // 如果滑动角度大于90度自己处理事件 if (Math.abs(lastY-y)<Math.abs(lastX-x)){ requestDisallowInterceptTouchEvent(false); } break; } default:break; } // 保存本次触控点的坐标 lastX = ev.getX(); lastY = ev.getY(); // 调用ListView的dispatchTouchEvent方法来处理事件 return super.dispatchTouchEvent(ev); } }

两种方法的代码思路基本一致,但是内部拦截法会更加复杂一点,所以在一般的情况下,还是使用外部拦截法较好。

到这里已经解决了情况一的滑动冲突解决方案,接下来看看情况二的滑动冲突如何解决。

情况二

第二种情况是里外容器的滑动方向是一致的,这种情况的主流解决方法有两种,一种是外容器先滑动,外容器滑动到边界之后再滑动内部view,例如京东app(注意向下滑动时的情况):

Android事件分发机制四:学了事件分发有什么用?

第二种情况的内部view先滑动,等内部view滑动到边界之后再滑动外部viewGroup,例如饿了么app(注意向下滑动时的情况):

Android事件分发机制四:学了事件分发有什么用?

这两种方案没有孰好孰坏,而是需要根据具体的业务需求来确定具体的解决方案。下面就上述的第二种方案展开分析,第一种方案类同。

首先分析一下具体的效果:外层viewGroup与内层view的滑动方向是一致的,都是垂直滑动或水平滑动;向上滑动时,先滑动viewGroup到顶部,再滑动内部view;向下滑动时,先滑动内部view到顶部后再滑动外层viewGroup。

这里我们采用外部拦截法来实现。首先我们先确定好我们的布局:

image.png

最外层是一个ScrollView,内部首先是一个LinearLayout,因为ScrollView只能有一个view。内部顶部是一个LinearLayout可以放置头部布局,下面是一个ListView。现在需要确定ScrollView的拦截规则:

当ScrollView没有滑动到底部时,直接给ScrollView处理

当ScrollView滑动到底部时:

如果LinearLayout没有滑动到顶部,则交给ListView处理

如果LinearLayout滑动到顶部:

如果是向上滑动则交给listView处理

如果是向下滑动则交给ScrollView处理

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wssxwz.html