前面用 Editor.md 写好了,本来想关掉 as 发布博客的,结果一不小心把 chrome 关掉了,真是日了狗啊,没办法只能重写了。

第 3 章 View 的事件体系

本章主要介绍 VIew 的相关知识和事件体系。

3.1 View 基础知识

本节主要知识点:View 的位置参数、MotionEvent 和 TouchSlop 对象、VelocityTracker、GestureDetector 和 Scroller 对象。

View 是 Android 中所有控件的基类,ViewGroup 是 View 的子类,是用来存放其他控件的一种 VIew。View 的位置主要由它的四个顶点决定,分别对应于 View 的四个属性:top、left、right、bottom,这四个属性分别是相对于 View 的父容器的坐标。

如果要知道 View 的位置信息,可以通过调用 View 对象的 getLeft()、getTop()、getRight()、getBottom() 来获得。从 Android 3.0 开始,View 增加了 x、y 参数,用来表示 VIew 左上角的坐标,和 translationX、translationY 参数,用来表示 View 左上角相对于父容器的坐标,View 也为这些参数提供了 get/set 方法,方便我们使用。这几个参数的换算关系如下:

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

需要注意的是,在 VIew 的平移过程中,top 和 left 表示的是原始左上角的位置信息,其值并不会发生改变。

在手指接触屏幕后会产生一系列事件,如:

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

当发生这些事件时,我们可以通过 MotionEvent 对象来获取 View 的坐标信息:

        tv_test.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // 获取相对于当前 View 左上角坐标
                event.getX();
                event.getY();
                // 获取绝对坐标
                event.getRawX();
                event.getRawY();
                return false;
            }
        });

TouchSlop 是系统所能识别出的被认为是滑动的最小距离,通过如下方式可以获取这个值:

        int value = ViewConfiguration.get(MainActivity.this).getScaledTouchSlop();

VelocityTracker 即速度追踪,用于追踪手指在屏幕上滑动的速度。使用方法如下:

            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        // 创建对象并添加追踪
                        tracker = VelocityTracker.obtain();
                        tracker.addMovement(event);
                        break;
                    case MotionEvent.ACTION_UP:
                        // 先计算速度再取出
                        // 传入时间单元
                        tracker.computeCurrentVelocity(1000);
                        // 这两个值可以为负数,比如从右向左滑动时
                        int xVelocity = (int) tracker.getXVelocity();
                        int yVelocity = (int) tracker.getYVelocity();
                        break;
                }
                return false;
            }

最后,如果不需要使用它了,就调用 clear() 方法来重置并回收内存:

        tracker.clear();
        tracker.recycle();

GestureDetector 就是手势检测,是用来检测用户的单击、滑动、长按、双击等行为的一个对象,使用时首先创建一个对象:

        mGestureDetector = new GestureDetector(MainActivity.this, new GestureDetector.OnGestureListener() {
            // 手指轻触屏幕一瞬间,由 1 个 ACTION_DOWN 触发
            @Override
            public boolean onDown(MotionEvent e) {
                return false;
            }

            // 手指轻触屏,尚未松开或滑动,强调的是没有松开或滑动的状态
            @Override
            public void onShowPress(MotionEvent e) {

            }

            // 手指松开,伴随着一个 ACTION_UP 而触发,这是一个单击行为
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return false;
            }

            // 滑动
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                return false;
            }

            // 长按
            @Override
            public void onLongPress(MotionEvent e) {

            }

            // 按下屏幕、快速滑动后松开
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                return false;
            }
        });
        // 解决长按屏幕后无法滑动的问题
        mGestureDetector.setIsLongpressEnabled(true);

然后在 View 的 onTouch 事件中添加如下代码即可:

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return mGestureDetector.onTouchEvent(event);
            }

Scroller 是一个弹性滑动对象,用于实现 View 的弹性滑动。当使用 View 的 scrollTo/scrollBy 方法进行滑动时,其过程是瞬间完成的,这样没有过渡效果的滑动用户体验不好,如果我们需要一个有过渡效果的滑动,就要用到 Scroller 对象了,它的使用也很简单:

public class MyView extends View {

    private Scroller mScroller;

    public MyView(Context context) {
        super(context);
    }

    private void init() {
        mScroller = new Scroller(getContext());
    }

    private void smoothScrollTo(int destX, int destY) {
        int scrollX = getScrollX();
        int delta = destX - scrollX;
        // 1000 ms 内滑向 destX
        mScroller.startScroll(scrollX, 0, delta, 0, 1000);
        invalidate();
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }
}

至于它为什么能实现弹性滑动,我们将在下一节中详细介绍。

版权声明:本文为andorxor原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/andorxor/p/9359927.html