第3章 view的事件体系

  1. view是android所有控件的基类,view位置有四个属性:top,left,bottom(相对于父容器坐标)。从Android 3.0开始,view增加了x、y、translationX、translationY四个参数,这几个参数也是相对于父容器的坐标。x和y是左上角的坐标,而translationX和translationY是view左上角相对于父容器的偏移量,默认值都是0。x = left + translationX;y = top + translationY。

  2. MotionEvent有ACTION_UP、ACTION_DOWN、ACTION_MOVE;通过MotionEvent可以得到点击事件发生的x和y坐标,其中getX和getY是相对于当前view左上角的x和y坐标,getRawX和getRawY是相对于手机屏幕左上角的x和y坐标。

  3. TouchSlop是系统所能识别出的可以被认为是滑动的最小距离,ViewConfiguration.get(getContext()).getScaledTouchSlope()。

  4. velocityTracker用于追踪手指在滑动过程中的速度,包括水平和垂直方向上的速度。
    速度计算公式: 速度 = (终点位置 - 起点位置) / 时间段
    当手指从屏幕右边往左边滑动的时候 ,速度为负值。

  5. GestureDetector.用于辅助检测用户的单击、滑动、长按、双击等行为.实现onGestureListener。监听滑动可以在onTouchEvent,监听双击可以使用GestureDetector。

  6. Scroller可以通过scrollby和scrollto来进行滑动,需要配合view的computeScroll.


view的滑动

  1. 三种方式实现view的滑动 1)通过view自身提供的scrollto和srcollby方法;2)通过动画给view实现平移来实现;3)通过改变view的layoutparams使得view重新布局来实现滑动;

  2. scrollby实际上调用scrollto的方法;如果从左向右滑动,mScrollx为负值反之为正值;如果从上向下滑动,那么mScrolly为负值,反正为正值;

  3. 使用动画;主要操作view的translationx和translationy属性,也可以使用属性动画。简单的view动画并不能改变别view的真正位置。

  4. 改变布局参数;假如向右平移移动一个button就增加这个button的marginleft。

    总结 srcoll方式操作简单适合view内容的滑动;动画使用于没有交互的view和没有复杂动画效果;改变参数适用于所有。

弹性滑动

通过scroller或者handler的postdelay或者thread sleep;

  1. 使用scroller的computerscroll获取当前的scrollx和scrolly使用scrollto去滑动接着调用postinvalidate方法第二次重会。
  2. 使用动画;anmator的addupdatelistener监听button的srcollto方法;
    3.使用hangdler的delay方法。

view的事件分发

  1. 点击事件的分发由dispatchtouchevent,onInterruptevent和ontouchevent组成;

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

public boolean onInterceptTouchEvent(MotionEvent event)
在dispatchTouchEvent方法内部调用,用来判断是否拦截某个事件,如果当前view拦截了某个事件,那么在同一个事件序列当中,此方法不会再被调用,返回结果表示是否拦截当前事件。
若返回值为True事件会传递到自己的onTouchEvent();
若返回值为False传递到子view的dispatchTouchEvent()。

public boolean onTouchEvent(MotionEvent event)
在dispatchTouchEvent方法内部调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前view无法再次接收到事件。
若返回值为True,事件由自己处理,后续事件序列让其处理;
若返回值为False,自己不消耗事件,向上返回让其他的父容器的onTouchEvent接受处理。
伪代码实现:

public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean consume = false;
    if (onInterceptTouchEvent(ev)) {
        consume = onTouchEvent(ev);
    } else {
        consume = child.dispatchTouchEvent(ev);
    }
    return consume;
}
  1. OnTouchListener的优先级比onTouchEvent要高
    如果给一个view设置了OnTouchListener,那么OnTouchListener中的onTouch方法会被回调。这时事件如何处理还要看onTouch的返回值,如果返回false,那么当前view的onTouchEvent方法会被调用;如果返回true,那么onTouchEvent方法将不会被调用。在onTouchEvent方法中,如果当前view设置了OnClickListener,那么它的onClick方法会被调用,所以OnClickListener的优先级最低。总结下来就是ontouchlistener>ontouchevent>onclick;

view滑动冲突

可以根据滑动距离和水平方向形成的夹角;或者根据水平和竖直方向滑动的距离差;或者两个方向上的速度差等
解决办法是内部拦截或者外部拦截;
内部:父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器来处理。这种方法和Android中的事件分发机制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作。

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

    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN: {]
        getParent().requestDisallowInterceptTouchEvent(true);
        break;
    }
    case MotionEvent.ACTION_MOVE: {
        int deltaX = x - mLastX;
        int deltaY = y - mLastY;
        if (当前view需要拦截当前点击事件的条件,例如:Math.abs(deltaX) > Math.abs(deltaY)) {
            getParent().requestDisallowInterceptTouchEvent(false);
        }
        break;
    }
    case MotionEvent.ACTION_UP: {
        break;
    }
    default:
        break;
    }

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

外部:点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要就不拦截。该方法需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可,其他均不需要做修改

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: {
        intercepted = false;
        break;
    }
    case MotionEvent.ACTION_MOVE: {
        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;
    }

    mLastXIntercept = x;
    mLastYIntercept = y;

    return intercepted;
}

你可能感兴趣的:(安卓开发艺术探索读书笔记)