android View滑动冲突的解决方式

  • View的事件分发机制
  • View的滑动冲突

View的事件分发机制

view的事件分发机制不仅是android中的核心知识,更是难点,掌握view的分发机制能更好的帮助我们理解怎样的自定义view,以及处理
view中的各种点击事件。研究View事件的分发,即是研究MotionEvent.当一个MotionEvent产生了以后,系统需要把这个事件传递给
一个能处理它的view,这个过程就是事件分发过程。

-点击事件的的传递

 点击事件的分发过程由三个很重要的方法来完成:1.dispatchTouchEvent. 2.onInterceptTouchEvent.3onTouchEvent.
1.public boolean dispatchTouchEvent(MotionEvent event)
     用来进行事件的分发。如果事件能够传递给当前的view,此方法被调用,返回的结果受当前view的onTouchEvent和下级的view的
     dispatchTouchEvent影响,表示是否消耗当前事件。
2.onInterceptTouchEvent(MotionEvent event)
    用来判断是否拦截事件。如果当前view拦截了某个事件,那么在同一个事件序列中,此方法不会再次调用,返回的结果表示是否拦截当前事件。
3.onTouchEvent(MotionEvent event)
   用来处理点击事件。在dispathTouchEvent方法中调用。返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前view无法再次接收到事件。

上述的三个方法的关系可如下伪代码所示:
//事件分发
public boolean dispatchTouchEvent(MotionEvent event){
    boolean consume=false;//是否消耗该事件
    if(onInterceptTouchEvent(event)){//如果拦截该事件,就进行点击事件处理。
        consume=onTouchEvent(event);
    }else{//如果不拦截,就交由其子view处理。
        consume=child.dispatchTouchEvent(event);
    }
    return consume;
}

根据上述伪代码,对于一个ViewGroup来说,当产生点击事件时,首先传递给它,触发ViewGroup的dispatchTouchEvent事件,如果ViewGroup的onInterceptTouchEvent方法返回true,就表示当前ViewGroup拦截了此事件,接着此事件就会交由ViewGroup的onTouchEvent方法来处理,处理的结果表示是否消费掉此事件,(如果onTouchEvent方法返回true,表示消费掉此事件,则此事件不再向下传递,如果onTouchEvent方法返回false,则此事件依然向下继续传递),如果ViewGroup的onInterceptTouchEvent方法返回false,表示它不拦截此事件,这时,当前事件就会继续传递给它的子View, 接着子View的patchTouchEvent方法就会被调用,如此反复知道事件被最终处理。
注:**1.只有ViewGroup才有onInterceptTouchEvent方法,而**View没有onInterceptTouchEvent方法,当事件传递给它时,它的onTouchEvent方法就会被调用。
2.ViewGroup默认不拦截任何事件。android源码中ViewGroup的onInterceptTouchEvent方法默认返回false;
3.View的点击事件有onTouch和onTouchEvent,如果View设置了OnTouchListener,那么OnTouchListener中的onTouch方法会被调用,若onTouch方法返回false,则当前View的onTouchEvent方法才会被调用。在onTouchEvent方法中,若设置了OnClickListener,那么它的onClick方法会被调用。由此可见OnTouchListener的优先级比onTouchEvent高。

View的滑动冲突

滑动冲突多表现在内外两层都可以滑动的时候,到底什么时候内层滑动,什么时候外层滑动的问题。
针对滑动冲突,可以有两种解决方案。

1.外部拦截法

外部拦截法就是事件都经过父容器的拦截处理,如果父容器需要此事件就拦截,不需要就继续向下传递。此方法比较符合点击事件的分发机制,外部拦截法需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可。流程的伪代码如下:

public boolean onInterceptTouch(MotionEvent event){
    boolean intercepted =false;
    int x = (int)event.getX();
    int y = (int)event.getY();
    switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:{
            intercepted=false;
            break;
        }
        case MotionEvent.ACTION_MOVE:{
            if(父容器需要当前点击事件){
                intercepted=true;

            }else{
                intercepted=false;
            }
            break;
        }
        case MotionEvent.ACTION_UP:{
            intercepted=false;
            break;
        }
        default:
            break;

    }
    mLastX=x;
    mLastY=y;
    return intercepted;
}

上述代码为外部拦截法的典型逻辑,针对不同的滑动冲突,只需要修改父容器需要当前点击事件这个条件即可,其余均不需要修改。针对ViewGroup的onInterceptTouchEvent,ACTION_DOWN事件必须返回false,即不拦截按下事件,如果一旦父容器拦截了ACTION_DOWN事件,则后续的ACTION_MOVE和ACTION_UP事件都会交由父容器来处理。ACTION_UP事件,也必须返回false,如果父容器返回了true,那么可能导致子View的onClick事件无法触发。

2.内部拦截法

内部拦截法是指父容器不拦截任何事件,所有的事件都传递给子view,如果子view需要就消耗此事件,如果不需要就交由父容器进行处理。需要重写子view的dispatchTouchEvent方法以及配合requestDisallowInterceptTouchEvent方法使用。方法的流程如下:

public boolean dispathTouchEvent(MotionEvent event){
    int x = (int)event.getX();
    int y = (int)event.getY();
    switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:{
            parent.requestDisallowInterceptTouchEvent(true);
            break;
        }
        case MotionEvent.ACTION_MOVE:{
            int deltax = x-LastX;
            int deltay = y-Lasty;
            if(父容器需要此点击事件){
            parent.requestDisallowInterceptTouchEvent(false);
            }

            break;
            }

        case MotionEvent.ACTION_UP:{
            break;
        }
        default:
            break;
        }

    LastX=x;
    LastY=y;
    return super.dispatchTouchEvent(event);

}

**注:**requestDisallowInterceptTouchEvent(boolean); 子view如果不希望父容器拦截点击事件时,可以调用此方法,当requestDisallowInterceptTouchEvent方法返回true时,父容器将不拦截此事件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值