重温Android开发艺术探索之三 View事件体系

251 阅读2分钟

View的基本知识

MotionEvent 和 TouchSlop

MotionEvent是手指接触屏幕后产生的一系列事件。主要包括:

  • ACTION_DOWN
  • ACTION_MOVE
  • ACTION_UP

获取当前点击事件的坐标,getX/getY 针对当前View ,getRawX/getRawY 获取相对于屏幕的x,y坐标。

TouchSlop 最小滑动常量 ViewConfiguration.get(getContext()).getScaledTouchaSlop()

VelocityTracher 速度追踪

GestureDetector 手势检测

Scroller 弹性滑动

通过computeScroll 和scrollTo来实现弹性滑动

事件分发机制

-事件传递过程是由外向内的,及事件总是先传递给父元素,然后由父元素传递给子元素,通过requestDissallowInterceptTouchEvent,可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外。如下源码中 DOWN事件中的resetTouchState 方法中重置了标记位。

ViewGroup#dispatchTouchEvent

 // Handle an initial down.
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event for the previous gesture
                // due to an app switch, ANR, or some other state change.
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            // Check for interception.
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags &            FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }
  • mFirstTouchTarget这个在子控件处理事件后会赋值为子控件。所以当子控件处理事件后,后续不会在调用onInterceptTouchEvent。反之如果ViewGroup处理了事件,那么mFirstTouchTarget就等于null,那么onInterceptTouchEvent也不会再次调用。

  • onTouchEventListener的优先级高于onClickLisener

滑动冲突

内部拦截法和外部拦截法,不管是哪种方法,父控件的Down事件一定要返回false否则子控件就玩完了。然后就是在MOVE事件中做相应的逻辑判断,在父控件中处理就直接返回true or false,在子控件处理就parent.requestDissllowInterceptTouchEvent(false);false是请求父控件拦截的意思。