自定义View事件拦截机制(自定义viewGroup和外部法解决滑动冲突)

3个方法:

dispathcTouchEvent()   返回值决定是否消耗事件 返回值由下面两个方法共同决定。 (这个方法通常不需要重写,只需要把下面两个方法处理正确,则会正确的分发)


onInterceptTouchEvent()  返回值决定是否拦截事件 ,有时不会调用。(所以内部拦截法要处理事件前需要写在dispathcTouchEvent方法中)


onTouchEvent();


三个方法的关系:

public Boolean dispatchTouchEvent (MotionEvent ev){
boolean cosume=false;
if(onInterceptTouchEvent(ev)){
 consume=onTouchEvent(ev);
}
else{
consume=child.dispatchTouchEvent(ev);
}
return consume;
}

不拦截: dispathchTouchEvent 向下传递

不消费:onTouchEvent 向上传递 类似于递归回调!(这点是很重要的 ,这种思想可以处理 最下级没有完成的事件,给父类容器一个机会继续处理)


这三个方法并不是在所有容器内都存在的:


Activity :   只有 dispathcTouchEvent ,和onTouchEvnet方法

ViewGroup : 三个方法都存在

view :     没有onInterceptTouchEvent 方法(因为直接拦截了), 返回值默认为true 消耗事件。


分析以下几种情况:

一, 从头到尾都无拦截无消耗:

 则dispathchTouchEvent返回为false ,说明消息发送失败. 假如DOWN事件被传递后,无拦截,无消耗,则后续的MOVE,和UP事件不会在传递了!


二.默认被view拦截了,并且消耗掉事件了。  则后面的点击后续事件,都会来找这个View(因为dispathcTouchEvent 上面的返回值都是true) 


三. 特殊情况 

 1:被拦截了,并且消耗了事件后,又被前面的给拦截了, 那么跟这个最初拦截的view 就基本没什么关系了,只传递了 CANCEL一个事件给这个view。

 2:一开始就被拦截并且也消费了,则跟后面的view 一毛钱关系都没有了。

特别注意 :

  如果DOWN事件被消费,后续的MOVE和UP也归这个view. 通过阅读源码,可以知道,是因为firstTouchTage这个标记被更改为true 导致 后续事件将不会调用onInterceptTouch方法询问是否拦截了!

  如果DOWN事件不消费,后续的MOVE 和up事件会递归调用父容器的TouchEvent方法

  如果出了DOWN以外的事件不消费,那么不会调用父容器的TouchEvent方法,并且听说事件会失效,并交给Activty处理。             


点击事件的优先级:

 onTouchListner  优先级最高

 onTouchEvent    第二

 onClicListener   第三

  如果返回的true 表示消费后后面的方法就不会调用了!


通用基本规则:


1.通常情况下一个事件序列只能被一个View拦截并消耗。(即down,move,up都交给一个view处理)

2.一旦View开始处理事件那么整个事件序列都交给它处理,onIntercept不会再次调用,因为不再需要确认是否拦截了也合乎情理。


3.ViewGroup的拦截与ACTION_DOWN这个方法和FLAG_DisALLOW标记有关。(根据这点可以用内部拦截法处理滑动冲突)内部拦截法就是回路拦截


处理事件后如果不消耗,ACTION_DOWN事件那么后面的事件不会再交给当前view而会交给父容器处理,而非ACTION_DOWN事件则不会调用父容器,会传给Activity处理。


通过上面对view事件拦截机制的理解,做出处理滑动冲突的方案:


最后外部拦截法处理冲突的方式:

  外部拦截法就是 InterceptTouchEvent()方法获取到事件后 判断是不是要拦下 ,如果不需要这个事件就不拦 返回false 给下面处理。如果需要就返回true 

但是特别需要注意的有以下两点:

  1.ACTION_DOWN 这个事件一定不能拦截 ,因为 如果拦下之后,后续的事件都会归当前这个view 处理! 

  2.ACTION_UP这个事件也没有必要拦 ,返回false 就可以了, 拦也没有什么意义。


代码如下:(核心思想就是针对MOVE事件进行判断和拦截)

public boolean onInterceptTouchEvent(MotionEvent ev) {
        Boolean intercept=false;
        //获取坐标点:
          int x= (int) ev.getX();
          int y= (int) ev.getY();
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                intercept=false;

                break;
            case MotionEvent.ACTION_MOVE:
                int deletx=x-mx;
                int delety=y-my;
                if(Math.abs(deletx)>Math.abs(delety))
                {
                    intercept=true;
                    Log.d("拦截了","111");
                }
                else {
                    intercept=false;
                    Log.d("没拦截","222");
                }
                break;
            case MotionEvent.ACTION_UP:
                intercept=false;
                break;
            default:
                break;
        }
        //这里尤其重要,解决了拦截MOVE事件却没有拦截DOWN事件没有坐标的问题
        lastx=x;
        lasty=y;
        mx=x;
        my=y;
        return intercept;
    }
}

 上次滑动点坐标lx,ly;                                                                                                            !!!这个很重要!!

 lx,ly坐标重置 这次重置是为了下一次MOVE事件时提供初始值。                                         !!!这个很重要!!

 特别注意这段代码的一些细节,例如坐标需要重置!


滑动也是view事件的责任:

第二部分是view事件处理:

主要是view滑动(7种):

通过ontouchListener()

1获取偏移()

2将偏移量放进位置方法中改变位置。



附练习代码

https://github.com/cuizehui/myDemo





你可能感兴趣的:(Android,应用层开发)