美文网首页
View事件体系与View的分发机制

View事件体系与View的分发机制

作者: Ivy枫 | 来源:发表于2020-11-29 14:55 被阅读0次

    View基础知识

    • View本身可以是单个控件,也可以是多个控件组成的一组控件
    • ViewGroup也继承了View

    View的位置参数

    • top:左上角纵坐标

    • left:左上角横坐标

    • bottom:右下角纵坐标

    • right:右下角横坐标

    • x,y:左上角坐标

    • translatioX,translationY:左上角相对于父容器的坐标偏移量,默认值为0

    x = left + translationX;
    y = top + translationY;

    MotionEvent和TouchSlop

    1. MotionEvent

    在手指接触到屏幕,触发的典型事件类型:

    • ACTION_DOWN:手指刚接触屏幕
    • ACTION_MOVE:手指在屏幕上移动
    • ACTION_UP:手指离开屏幕

    通过MotionEvent对象可得点击事件发生的x,y坐标

    • getX/getY:获得相对于View左上角x,y的坐标
    • getRowX/getRowY:获得相对于手机屏幕左上角的x,y坐标
    1. TouchSlop

    系统所能识别滑动的最小距离,是个常量,也就是滑动的距离小于这个常量系统就认为没有进行滑动操作

    获得常量方式:ViewConfiguration.get(getContext()).getScaledTouchSlop()

    VelocityTracker、GestureDetector和Scroller

    1. VelocityTracker:速度追踪

    用于追踪手指在滑动过程中的速度,包含水平和垂直上的速度

    • 首先在View的onTouchEvent方法中追踪当前单击事件的速度
    VelocityTracker velocityTracker = VelocityTracker.obtain();
    velocityTracker.addMovement(event);
    
    • 接着,计算速度,获取速度之前要计算速度,这里的速度指一段时间手指所划过的像素数
    velocityTracker.computeCurrentVelocity(1000);//计算速度
    int xVelocity = (int)velocityTracker.getXVelocity();
    int yVelocity = (int)velocityTracker.getYVelocity();
    
    • 最后,调用clear方法重置并回收内存
    velocityTracker.clear();
    velocityTracker.recycle();
    
    1. GestureDetector:手势检测

    用来辅助检测用户的单击、滑动、长按、双击等行为

    1. Scroller:弹性滑动,实现有过渡效果的滑动

    View的滑动

    方式:

    1. 通过View的ScrollTo/ScrollBy方法
    2. 通过动画给View施加平移效果来实现滑动
    3. 通过改变View的LayoutParams使得View重新布局从而实现滑动

    使用scrollTo/scrollBy

    scrollBy最终也是使用scrollTo来实现的,他们都只能该变View内容的位置,不能该变View的布局位置,从左往右滑,mScrollX为正,反之为负,从下往上滑mScrollY为正,反之为负

    使用动画方式(属性动画)

    例:将一个View在100ms内从原始位置向右平移100像素

    ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(100).start();
    

    View动画是对View的影像做操作,并没有该变View的位置参数,要使动画的状态得以保存,要将属性fillAfter设置为true,否则动画完成后结果就会消失,属性动画不会出现此问题

    改变布局参数

    即改变LayoutParams,例如通过marginLeft参数可以达到向右平移的效果,还可以预先设置一个空View等

    3种滑动方式的比较

    • ScrollTo/ScrollBy:操作简单,适合对View内容的滑动,不影响内部元素的单击事件,缺点:只能滑动View的内容,不能滑动View本身
    • 动画:不能该变View本身属性,适合不与用户交互的View和实现复杂动画
    • 该变布局:操作复杂,适用于与用户交互的View

    弹性滑动

    即渐进式滑动,主要思想是将一次很大的滑动分成若干个小的滑动,并且在一段时间内完成

    实现方式:Scroller、Handler.postDelayed、Thread.sleep

    Scroller

    典型使用方法:

    Scroller scroller = new Scroller(mContext);
    //缓慢移动到指定位置
    private void smoothScrollerTo(int destX, int destY) {
        int scrollX = getScrollX();
        int destX = destX - scrollX;
        //1s内滑动destX
        mSroller.startScroll(scrollX, 0, deltaX, 0, 1000);
        invalidate();
    }
    
    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mSroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }
    

    注意:这里的滑动是指内容上的滑动,View本身是没有改变的,在上述中,仅仅调用startScroll方法是无法让View进行滑动的,它的内部没有做滑动相关的事情,只是保存了些数据而已,真正让View滑动的是invalidate方法,这个方法导致View重绘,在View重绘的draw方法中又会去调用computeScroll方法,这个方法又会去向Scroller获取当前的scrollX和scrollY,然后通过scrollTo方法实现滑动

    computeScrollOffset方法返回的是true表示滑动没有结束,返回false,表示滑动结束

    Scroller本身不能实现View滑动,要配合View的computeScroll方法才能完成弹性滑动的效果

    View事件分发机制

    点击事件传递规则

    点击事件分析的对象是MotionEvent,点击事件分发过程是由:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent方法共同完成的

    • dispatchTouchEvent:用于事件的分发,如果事件传递到当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent影响
    • onInterceptTouchEvent:在上述方法内部调用,判断是否拦截此事件,true为拦截
    • onTouchEvent:在dispatchTouchEvent方法中调用,用来处理点击事件

    对于ViewGroup来说,点击事件后,调用dispatchTouchEvent方法,此时ViewGroup的onInterceptTouchEvent方法被调用,返回true代表拦截,接着调用onTouchEvent处理事件,如果返回false,分发给子元素

    当View要处理事件时,如果设置了onTouchListener,此时onTouch方法会被回调,返回false,view的ouTouchEvent会被调用,返回true,不会调用。onTouchListener优先级比onTouchEvent优先级高,onClickListener优先级最低

    事件点击后传递顺序:Acticity,window,view,如果view不处理事件就会向上传递处理


    总结:

    1. 同一个事件序列指手指触摸屏幕到离开这一过程事件
    2. 正常情况,一个事件序列只能给一个View处理
    3. 一旦一个View拦截了一个事件,那么它的同一个事件序列都直接交给它处理,并且它的onInterceptTouchEvent方法不再调用
    4. 某个View一旦开始处理事件,如果不消耗ACTION_DOWN事件的话,那么其它同一系列事件它也不会消耗,并且重新交给父元素处理
    5. 如果View不消耗ACTION_DOWN以外的其他事件,那么这个点击事件就会消失,并且父元素的onTouchEvent方法也不会调用,View可以持续收到后续的事件,最终这些事件都会交由Activity来处理
    6. ViewGroup默认不拦截任何事件
    7. View没有onInterceptTouchEvent方法,一旦有事件传递给它,他就调用onTouchEvent方法
    8. View的onTouchEvent方法都默认会消耗事件的,除非它是不可点击的
    9. View的enable属性不影响onTouchEvent的返回值
    10. 事件的传递方向是从外向内的,总是由父元素传递给子元素,子元素再传递给View
    11. onClick发生的前提是View可点击并且收到了down和up事件

    相关文章

      网友评论

          本文标题:View事件体系与View的分发机制

          本文链接:https://www.haomeiwen.com/subject/txrkwktx.html