Android View之事件分发机制以及滑动处理(Android开发艺术读书笔记)

事件分发机制是一个很重要的东西。废话不多说,接下来我便跟着任教主的步伐走一走。

  • 点击事件的传递规则
public boolean dispatchTouchEvent(MotionEvent ev)

用来进行事件分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。

public boolean onIntereceptTouchEvent(MotionEvent event)

在上述方法中被调用,用来判断是否拦截某个事件,如果拦截了,那么同一事件序列中,此方法不会被再次调用。返回结果表示是否拦截当前事件

public boolean onTouchEvent(MotionEvent event)

在dispatchTouchEvent中调用,用来处理点击时间,返回结果表示是否消耗当前事件,如果不消耗,则同一事件序列中,当前view无法 再次收到事件。

看如下精辟的伪代码

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

对于一个根ViewGroup来说,点击事件产生后,首先会传递给他,dispatch就会被调用,如果onInterecept返回true,就表示要拦截,那么事件就会由viewgroup来处理。如果返回false,就表示不拦截,那么就传递给子view。

TouchListener>onTouch>onClickListener 优先级

Activity->window->view

如果一个view的onTouch返回false,那么他的父容器就会调用onTouchEvent。

看看主席的总结吧:

  1. 同一时间序列 down->move-…->move->up
  2. 正常情况下 ,一个时间序列只能被一个view拦截且消耗,但是通过特殊手段可以实现2个view同时处理
  3. 某个view一旦决定拦截,那么这一个时间序列都只能由他来处理,并且他的onIntereceptTouchEvent不会再被调用。
  4. 某个view一旦开始处理事件,若不消耗down时间(在onTouchEvent中),那么同一事件序列中其他事件都不会再交给它来处理,并且将事件重新交由他的父元素去处理。
  5. 如果不消耗除down以外的事件,时间就会消失
  6. ViewGroup默认不拦截任何事件
  7. view没有onIntereceptTouchEvent,一旦有点击事件传递给它,那么它的onTouchEvent方法就会被调用。
  8. view的onTouchEvent默认消耗事件,除非不可点击
  9. view的enable属性不影响onTouchEvent的默认返回值
  10. onclick会发生的前提是当前view可点击
  11. 事件传递过程是由外向内的,即时间总是先传递给父元素。通过requestDisallowIntereceptTouchEvent可以在子元素中干扰父元素的事件分发,down事件除外。

事件分发机制源码解析
任玉刚大神
郭霖大神-1
郭霖大神-2

张鸿洋

在界面中出现上下2层都可以滑动的时候就会出现滑动冲突。常见的滑动冲突的场景都三种

  • 外部滑动方向与内部滑动方向不一致
  • 外部滑动方向和内部滑动方向一致
  • 上面2中情况的嵌套

那么我们如何解决呢?我们可以判断手指x、y方向上的速度或者移动距离,根据速度差或者距离差来处理。

解决方式:

  • 外部拦截法

在父控件中拦截

@Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false ;
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                //如果正在滑动,就不拦截down事件,如果滑动结束了,就拦截
                intercepted = false;
                if (!mScroller.isFinished()){
                    mScroller.abortAnimation();
                    intercepted = true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                //如果x方向上偏移量大,就判定为x,拦截
                int deltaX = x-mLastXIntercept;
                int deltaY = y-mLastYIntercept;
                if (Math.abs(deltaX)>Math.abs(deltaY)){
                    intercepted = true;
                }else{
                    intercepted = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                intercepted = false;
                break;
            default:
                break;
        }
        Log.e(TAG, "intercepted=" + intercepted);
        mLastX = x;
        mLastY = y;
        mLastXIntercept = x;
        mLastYIntercept = y;
        return intercepted;
    }
  • 内部拦截法
    父控件中
public boolean onInterceptTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN){
            mLastX =x;
            mLastY = y;
            if (!mScroller.isFinished()){
                mScroller.abortAnimation();
                return true;
            }
            return false;
        }else{
            return true;
        }
    }

view中

 @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = x-mLastX;
                int deltaY = y-mLastY;
                if (Math.abs(deltaX)>Math.abs(deltaY)){
                    mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(false);
                }
                break;
            case MotionEvent.ACTION_UP:

                break;
            default:
                break;
        }
        mLastX = x;
        mLastY = y;
        return super.dispatchTouchEvent(event);
    }

代码就是上面哪些,其实不难。更多的东西,请去看书。

你可能感兴趣的:(android)