onTouch

OnTouch
OmOnTouchListener
OnTouchEvent

View的事件分发 :
    对于事件分发机制,举个简单的例子,在一个Activity中只有一个按钮,如果我们想给这个按钮注册一个点击事件,只需要调用setOnClickListener方法,这样在onClick方法里
    面写实现的代码,就可以在按钮被点击的时候执行.我们再给这个按钮添加一个touch事件,只需要调用setOnTouchListener方法,onTouch方法里能做的事情比onClick要多一
    些,比如判断手指按下、抬起、移动等事件。如果我两个事件都注册了,我之前做过一个实验,运行程序点击按钮的结果是onTouch是优先于onClick执行的,并且onTouch执行
    了两次,一次是ACTION_DOWN,一次是ACTION_UP(你还可能会有多次ACTION_MOVE的执行,如果你手抖了一下)。因此事件传递的顺序是先经过onTouch,再传递到onClick。
    onTouch方法是有返回值的,如果我们尝试把onTouch方法里的返回值改成true,再运行一次就会发现onClick方法不再执行了,这是因为onTouch方法返回true就认为这个事件
    被onTouch消费掉了,因而不会再继续向下传递。


1、为什么要有事件分发机制
    如果ViewGroupA里面包含一个ViewGroupB,ViewGroupB里面再包含一个view,
    当点击view的时候,为什么只有view相应事件,点击的也是A的区域。
    这就引发了事件分发机制

2、MotionEvent主要有几个事件类型、
    down
    move
    up

3、事件分发的主要方法
    分发事件:
        dispatchTouchEvent(View,ViewGroup:view 之间touch事件传递的入口函数 onInterceptTouchEvent:判断 touch 事件是否拦截 onTouchEvent:处理 touch 事件)
        如果返回true/false,则表示事件传递到这儿就终止了,不会继续分发
        也不会传给父级

    拦截事件:onInterceptTouchEvent
        只有ViewGroup有这个方法,只有子类才需要拦截。返回true,表示拦截该事件
        就不会分发给子类。

    消费事件:onTouchEvent
        Touch 事件 ACTIONDOWN, ACTIONUP ,ACTIONMOVE, ACTIONCANCLE
        返回true,这个事件被消费掉了,就不会再传给出去了。如果没有人消费掉,则最终
        会被当前的activity消耗掉,下次的move和up事件就不会在传下去了。

    注意:
        一个down事件分发之后,还有回传的过程。因为一个事件分发包括down、move、
        up这个几个动作,当手指触碰到屏幕那一刻,首先分发down事件,事件分发完之
        后还要回传回去,然后继续从头开始分发,执行下一个move操作,知道执行完up
        事件,整个分发过程到此结束。

        当自定义viewgroup时不会拦截down事件,一旦拦截,后边的move和up就不会再
        传递下去了。事件以后只会传给viewgroup这里。

    事件一旦分发到view则默认一定会执行他的onTouchEvent分发
        父view把touch 事件传递给子view 处理,是调用了子view 的 dispatchTouchEvent 方法
        子view的 dispatchTouchEvent 方法把 touch 事件交给两个兄弟负责:
        mOnTouchListener.onTouch 方法
        onTouchEvent 方法
        先是让 onTouch 方法处理 touch 事件,如果 onTouch 方法消耗掉 touch 事件,那么 dispatchTouchEvent 方法直接返回 true;如果 onTouch 方法没有消耗掉
        touch 事件,那么调用 onTouchEvent 方法来处理 touch 事件,此时 dispachTouchEvent 方法的返回值,就靠 onTouchEvent 方法来决定。

列举:
    A包括B,B包括C(view)详解:
    1、点击c,c不消费事件
    当一个事件产生后,传递流程是  activity --> phoneWindow --> view
    当点击c的时候,事件从mainActivity的事件分发开始,然后去问b是否消费事件,然后将事件
    分发给B,事件到了B,他不拦截事件,先去问自己的孩子要不要消费事件,然后发给c,事件
    到了view,开始执行dispatchevent方法来决定是否消费事件。
    事件回传, c不消费事件,所以它开始回传,告诉B我不消费事件(view的onTouchEvent返回false),然
    后B才有权利决定是否消费该事件,这时开始执行onTouchEvent事件,B也不消费事件,所以
    onTouchEvent也返回false,事件继续传给A的onTouchEvent方法,由于A也不消费,最终扔给
    了mainActivity去消费。

    2、点击C,view消费掉事件
    mainActivity一层一层往下传,到了view便开始执行onTouchEvent方法来决定是否消费该事件,
    由于view消费掉了这个事件,view的onTouchEvent便return false,B收到通知view消费了事
    件,所以就不会执行B的onTouchEvent事件,只能回传告诉A,view消费了事件,你没机会了。
    A收到return true后,就知道事件被消费了,所以他也return true,最终事件传给
    了mainActivity,由于事件被消费了,所以不会执行mainActivity的onTouchEvent方法,
    接下来执行Move事件,流程和之前的一样,重新开始执行。
    结论:onTouchEvent被view消费掉,A、B的onTouchEvent都不会执行。

    3、点击B,但是B不消费事件。
    B和A都不消费事件,最终只能交给mainActivity了去消费了。
    view的mOnTouchListener.onTouch方法优先于view的onTouchEvent方法。
         onTouchEvent 方法主要实现了 3 个功能:
         1. 根据 touch 事件产生 click 事件,调用 onClickListener.onclick 方法
         2. 根据 touch 事件产生 press 状态的变化
         3. 根据 touch 事件产生 焦点分发
    
view和viewgroup对onTouchEvent事件的处理什么不同和联系?



代码说明:

请父级View不拦截touch事件:

    1.解决SwipeLayout和ListView的触摸事件冲突问题
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mDownX = event.getRawX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float deltaX = event.getRawX() - mDownX;
                    if(Math.abs(deltaX) > mHelper.getTouchSlop()){
                        getParent().requestDisallowInterceptTouchEvent(true);//请父级View不拦截touch事件
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    mDownX = 0;
                    break;
            }
            try {
                mHelper.processTouchEvent(event);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return true;
        }

touch事件分发的三个问题:

    1. onTouch和onTouchEvent有什么区别,又该如何使用?
        从源码中可以看出,这两个方法都是在View的dispatchTouchEvent中调用的,onTouch优先于onTouchEvent执行。如果在onTouch方法中通过返回true将事件消费掉,
        onTouchEvent将不会再执行。
        另外需要注意的是,onTouch能够得到执行需要两个前提条件,第一mOnTouchListener的值不能为空,第二当前点击的控件必须是enable的。因此如果我们有一个控件
        是非enable的,那么给它注册onTouch事件将永远得不到执行。对于这一类控件,如果我们想要监听它的touch事件,就必须通过在该控件中重写onTouchEvent方法来实
        现。

    2. 为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?
        如果你阅读了http://blog.csdn.net/guolin_blog/article/details/8744400 滑动菜单的功能是通过给ListView注册了一个touch事件来实现的。如果我们在onTouch方
        法里处理完了滑动逻辑后返回true,那么ListView本身的滚动事件就被屏蔽了,自然也就无法滑动(原理同前面例子中按钮不能点击),因此解决办法就是在onTouch方法
        里返回false。

    3. 为什么图片轮播器里的图片使用Button而不用ImageView?
        提这个问题的朋友可看看http://blog.csdn.net/guolin_blog/article/details/8769904 这篇文章。这篇文章里,在图片轮播器里使用Button,主要就是因为Button是  
        可点击的,而ImageView是不可点击的。如果想要使用ImageView,可以有两种改法。第一,在ImageView的onTouch方法里返回true,这样可以保证ACTION_DOWN之后的其
        它action都能得到执行,才能实现图片滚动的效果。第二,在布局文件里面给ImageView增加一个android:clickable="true"的属性,这样ImageView变成可点击的之后
         ,即使在onTouch里返回了false,ACTION_DOWN之后的其它action也是可以得到执行的。


ZHBJ中...
触摸事件的处理:
    我认为触摸事件的处理实质上就是重写控件的  
    dispatchTouchEvent(),
    onInterceptTouchEvent(),
    onTouchEvent()方法
    处理控件的分发,拦截,触摸的逻辑,用以达到我们想要的目的.以我之前做过的项目为例,
    其中比较难的有两个,一个是三次ViewPager嵌套,一个是ScrollView嵌套ViewPager再嵌套Viewpager和ListView

    >>>>>>>
    你看这个项目,它就是有三次ViewPager嵌套    
    他的最外层界面其实是一个VIewPager(其实它也可以用Fragment来做,不过我考虑到他是要不断切换界面这点和ViewPager的特点很像于是就用VIewPager来做了),我重写了它
    的onTouchEvent()方法,只返回true,屏蔽了它的滑动事件,并且设置了切换界面不滑动的属性,
    这点比较有意思,同样实现相同的功能,

    用ViewPager做我们就想屏蔽它的界面切换效果,用fragment来做又想自己给它添加一个界面切换的动画).然后把界面的切换交给了界面下方的四个单选按钮实现(这个功能的
    实现方法有很多,可以写四个TextView,然后drawableTop画上图片或者用线布局上面放ImageView下面放TextView实现都可以,只是考虑到功能需求是只能单选,和单选按钮的
    功能最为相似,用它做最简单,

    所有我使用一个RadioGroup嵌套四个RadioButton并且设置Radiobutton的button属性为null取消单选按钮的圆圈,drawableTop画上图片,给图片和字体颜色设置选择器实现的)
    ,并且把它的拦截事件onInterceptTouchEvent()方法重写返回false,让它不拦截子View的触摸事件(这个方法在ViewGroup中实现都比较简单,都是直接返回false,不过ViewPa
     ger和ListView都重写了这个方法,做了大量的逻辑和判断).

    其次,社区界面的发现和关注也是用ViewPager实现的,由于它的父控件的onInterceptTouchEvent()拦截方法已经被我重写并且返回false,所以它可接收到父控件传递过来的
    触摸事件并对其处理,所以我并没有对这层ViewPager做什么特殊的处理,直接使用了系统V4包里原生的ViewPager

    然后这个ViewPager里面的布局实际是一个Listview,前面几个比较特殊的界面是作为头布局添加进去的,这个头布局以及listview的条目中都有VIewPager的存在,这种情况就
    比较复杂了,
    因为这种ViewPager有很多控件都和它争抢触摸事件,比如它的父控件,listview,于是我做了这样的处理,首先ViewPager肯定是上下滑动不了的,然后只要ViewPager不是最后
    一个条目,我左右滑动都可以切换当前ViewPager,如果是最后一个条目的话,我再向右滑就可以切换父控件的ViewPager.所以我就重写了它的dispatchTouchEvent方法(),(虽然
    这里只有父ViewPager两个条目,但是我还是按照有多数pager的情况下写的以防以后业务发生改变,增加了条目),如果不是上下滑动,第一个条目右滑,最后一个条目左滑,我请
    求父控件不阻拦其触摸事件,让这个ViewPager处理滑动事件否则就不让父控件不阻拦,让父控件走自己的逻辑.

    而这个项目是我协助我朋友做的,它涉及到ScollView嵌套ViewPager,它这里面还用到了很多自定义控件
 

posted @ 2017-04-07 21:20  dub  阅读(940)  评论(0编辑  收藏  举报