美文网首页
[Android] 在ViewPager里使用自定义控件时的触摸

[Android] 在ViewPager里使用自定义控件时的触摸

作者: Cocoonshu | 来源:发表于2017-09-28 23:33 被阅读133次

我们在使用ViewPager来承载自定义控件时,可能会遇到这样一种情况:我们自定义的控件被触摸后,被滑动的居然是ViewPager,而不是我们的自定义控件被操作。

1 分析

(1)多次操作后,你会发现:
当你触摸自定义控件后,如果先竖向滑动后再正常操作,自定义控件交互也完全正常;
当你触摸自定义控件后,如果先横向滑动,那么你的自定义控件将不会再动作,随之替代的是ViewPager的跟手滑动。

(2)在自定义控件的onTouchEvent()里加上Log来打印MotionEvent的action,你会发现:
自定义控件在接受了ACTION_DOWN和数个ACTION_MOVE后,会接受到一个ACTION_CANCEL事件。随后你的自定义控件将不再受控,ViewPager再次拦截了事件,开始左右滑动起来。就算你在onTouchEvent()里死命地返回true去通知系统你要消化此事件,亦无济于事。

一个View正常处理的事件流程为:
ACTION_DOWN -> ACTION_MOVE -> ... -> ACTION_MOVE -> ACTION_UP

而现在的事件流程变成了这样:
ACTION_DOWN -> ACTION_MOVE -> ... -> ACTION_MOVE -> ACTION_CANCEL

ACTION_CANCEL 表示当前这个触摸手势由于某种原因被宣告无效,你在这个事件里获取不到任何有效的触摸点信息。你需要把它当作ACTION_UP来处理,但不要像正常流程一样去触发任何的控件动作,只要把它当作这次触摸手势已经取消并结束就好。

那么是谁来取消了这次触摸手势呢?触摸手势被取消后,ViewPager又动了起来,说明触摸手势还存在呀?~

别装了,傻子也能看出来是ViewPager劫取了这次触摸手势,并给它的子控件的发送了ACTION_CANCEL事件。

(3) 其他控件会受影响吗?
在ViewPager里添加一个Android自带的SeekBar。这个控件的交互也是左右滑动,刚好和ViewPager的交互方式一致,使用这个控件来测试可以很好的证明ViewPager是否会影响子控件的交互。
可我们会惊奇地发现,SeekBar一切正常,ViewPager也一切正常。那么SeekBar里一定有解决问题的方法!

2 解决方案

查阅了SeekBar的源码,最后发现在SeekBar的onTouchEvent()里有一个奇怪的方法调用:

attemptClaimDrag()  // 尝试拒绝拖拽操作

这个方法尝试获取控件的父容器,并且通知父容器不要截断触摸事件的发送。如果你在ACTION_DOWN时返回了true去告诉父容器你需要处理本次事件流程后,就得在接下来的每次事件中告知父容器,本次事件流程你还需要处理,不要用手势检测器去探测并截断事件流程。

处理代码如下:

private void attemptClaimDrag() {
    ViewParent parent = getParent();
    if (parent != null) {
        // 如果控件有父控件,那么请求父控件不要劫取事件
          // 以便此控件正常处理所有触摸事件
          // 而不是被父控件传入ACTION_CANCEL去截断事件
          parent.requestDisallowInterceptTouchEvent(true);
    }
}
 
@Override
public boolean onTouchEvent(MotionEvent event) {
    boolean isNeedToEatEvent = false;
 
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
        {
            // TODO 处理ACTION_DOWN事件
  
            isNeedToEatEvent = true;
            attemptClaimDrag();   
            break;
        }

        case MotionEvent.ACTION_MOVE:
        {
            // TODO 处理ACTION_MOVE事件
   
            isNeedToEatEvent = true;
            attemptClaimDrag();
            break;
        }

        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
        {
            // TODO 处理ACTION_UP事件
            isNeedToEatEvent = true;
            break;
        }
    }
 
    return isNeedToEatEvent;
}

3 结论

如此,你的自定义控件将安静地躺在ViewPager里处理自己的事件,而不会再被ViewPager打断。

相关文章

网友评论

      本文标题:[Android] 在ViewPager里使用自定义控件时的触摸

      本文链接:https://www.haomeiwen.com/subject/cikbextx.html