自定义 ViewGroup 支持无限循环翻页之二(处理触摸事件)

来源:互联网 发布:linux查看端口是否打开 编辑:程序博客网 时间:2024/04/27 14:47

大家如果喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢

转载请标明出处,再次感谢

#######################################################################

自定义 ViewGroup 支持无限循环翻页系列

自定义 ViewGroup 支持无限循环翻页之一(重写 onLayout以及 dispatchDraw)

自定义 ViewGroup 支持无限循环翻页之二(处理触摸事件)

自定义 ViewGroup 支持无限循环翻页之三(响应回调事件)

#######################################################################


在之前的博客()里,我对无限循环的界面布局和 draw 的分发做了介绍,现在,我们来具体实现一下 SerailScreenLayout 的触摸响应.

作为一个依赖用户手势进行滑动的自定义布局,我们需要重写 onInterceptTouchEvent 和 onTouchEvent 两个方法,来处理用户的点击事件,进而通知界面更改显示区域,以起到界面移动的效果.

首先,我们先简单介绍一下onInterceptTouchEvent 和 onTouchEvent两个方法.

这两个方法在 viewgroup 被 dispatchTouchEvent 调用,调用顺序是先调用 onInterceptTouchEvent,将返回值赋值给一个叫做intercepted的布尔值变量,接下来判断该变量以及另外一个叫做 canceled 的变量是否同时 false,若是则说明,view group 不会拦截 touchEvent 事件,viewgroup 调用 dispatchTransformedTouchEvent 方法,这个方法中,系统会调用对应子 view 的 onTouchEvent 事件,从而将 touchEvent 分发到子 view 进行处理,最后在子 view 没有消耗 touch 事件的情况下, view group 才调用自己的 onTouchEvent 事件进行事件处理.

也就是说如果 onInterceptTouchEvent 返回 true的时候,进行 Touch 事件分发的时候,就会之间分发到自己的 onTouchEvent, 而不会让子 view 进行处理.

在本布局中,由于我们需要响应点击事件,同时也要考虑到子 view 的触摸事件处理,因此我们在构造函数的阶段就调用了

setFocusable(true);        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);

这两个方法能够强行帮助我们设置控件可点击,并且touch 事件分发时间设置在子类之后,从而避免我们阻塞了子 view 的点击事件处理.

接下来我们正式开始重写 onInterceptTouchEvent 以及 onTouchEvent 方法.

考虑到触摸事件很多应用都会响应,因此我写了一个工具类来处理 onInterceptTouchEvent 和 onTouchEvent,这个工具类叫做TouchHandler, 在类中有一个接口

public interface Callback {        //开始触摸事件        void onTouch(MotionEvent event);        //开始多点触摸        void onPointerTouch(MotionEvent event);        /**         * 移动位置         *         * @param dx x轴方向偏移         * @param dy y轴方向偏移         */        void onScrollBy(int dx, int dy);        /**         * 触摸事件结束         *         * @param velocityX 结束时,x轴加速度         * @param velocityY 结束时,y轴加速度         * @param cancel    是否取消         */        void onRelease(int velocityX, int velocityY, boolean cancel);    }

我们在 SerialScreenLayout 的 onInterceptTouchEvent 和 onTouchEvent 方法中,做了一次委托,将这两个事件委托给了 TouchHandler 处理,自身继承 Callback 接口,当 TouchHandler 进行接口回调时,处理响应事件


接下来,我们开始详细看看 TouchHandler 为实现onInterceptTouchEvent 和 onTouchEvent 而使用的 handle 方法,

(1)handleInterceptTouchEvent:

public boolean handleInterceptTouchEvent(MotionEvent event) {        final int action = event.getAction() & MotionEvent.ACTION_MASK;        if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) {            return true;        }        switch (action) {            case MotionEvent.ACTION_DOWN:                mLastMotionX = event.getX();                mLastMotionY = event.getY();                mActivePointerId = event.getPointerId(0);                initOrResetVelocityTracker();                break;            case MotionEvent.ACTION_MOVE:                final int activePointerId = mActivePointerId;                if (activePointerId == INVALID_POINTER) {                    break;                }                final int pointerIndex = event.findPointerIndex(activePointerId);                final float x = event.getX(pointerIndex);                final int xDiff = (int) Math.abs(x - mLastMotionX);                if (xDiff > mTouchSlop) {                    mIsBeingDragged = true;                    mLastMotionX = x;                    requestParentDisallowInterceptTouchEvent(true);                }                break;            case MotionEvent.ACTION_CANCEL:            case MotionEvent.ACTION_UP:                mIsBeingDragged = false;                mActivePointerId = INVALID_POINTER;                recycleVelocityTracker();                break;            case MotionEvent.ACTION_POINTER_DOWN:                onSecondaryPointerUp(event);                break;        }        if (mVelocityTracker != null) {            mVelocityTracker.addMovement(event);        }        return mIsBeingDragged;    }

其实在 ViewGroup 中, onInterceptTouchEvent 只有 ACTION_DOWN 或者自己的 onInterceptTouchEvent 返回 false,子 view 能够响应 Touch事件的时候才会被触发,所以我们在 ACTION_DOWN的时候,对速度检测工具类进行初始化.而对于其他部分,我们就是做基本的点击初始化和点击事件销毁而已.

(2)handleTouchEvent

public boolean handleTouchEvent(MotionEvent event) {        initVelocityTrackerIfNotExists();        final int action = event.getAction() & MotionEvent.ACTION_MASK;        switch (action) {            case MotionEvent.ACTION_DOWN:                if (mCallback != null) {                    mCallback.onTouch(event);                }                mLastMotionX = event.getX();                mLastMotionY = event.getY();                mActivePointerId = event.getPointerId(0);                break;            case MotionEvent.ACTION_MOVE: {                final int pointerIndex = event.findPointerIndex(mActivePointerId);                if (pointerIndex == -1) {                    break;                }                final float x = event.getX(pointerIndex);                float deltaX = mLastMotionX - x;                final float y = event.getY(pointerIndex);                float deltaY = mLastMotionY - y;                if (!mIsBeingDragged && Math.abs(deltaX) > mTouchSlop) {                    requestParentDisallowInterceptTouchEvent(true);                    mIsBeingDragged = true;                    if (deltaX > 0) {                        deltaX -= mTouchSlop;                    } else {                        deltaX += mTouchSlop;                    }                }                if (mIsBeingDragged) {                    // Scroll to follow the motion event                    mLastMotionX = x;                    mLastMotionY = y;                    if (mCallback != null) {                        mCallback.onScrollBy((int) deltaX, 0);                    }                }                break;            }            case MotionEvent.ACTION_UP:                final VelocityTracker velocityTracker = mVelocityTracker;                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);                int initialVelocityX = (int) velocityTracker.getXVelocity(mActivePointerId);                int initialVelocityY = (int) velocityTracker.getYVelocity(mActivePointerId);                mActivePointerId = INVALID_POINTER;                mIsBeingDragged = false;                recycleVelocityTracker();                if (mCallback != null) {                    mCallback.onRelease(initialVelocityX, initialVelocityY, false);                }                break;            case MotionEvent.ACTION_CANCEL:                mActivePointerId = INVALID_POINTER;                mIsBeingDragged = false;                recycleVelocityTracker();                if (mCallback != null) {                    mCallback.onRelease(0, 0, true);                }                break;            case MotionEvent.ACTION_POINTER_DOWN: {                final int index = event.getActionIndex();                final float x = event.getX(index);                final float y = event.getY(index);                mLastMotionX = x;                mLastMotionY = y;                mActivePointerId = event.getPointerId(index);                break;            }            case MotionEvent.ACTION_POINTER_UP:                if (mCallback != null) {                    mCallback.onPointerTouch(event);                }                onSecondaryPointerUp(event);                break;        }        if (mVelocityTracker != null) {            mVelocityTracker.addMovement(event);        }        return true;    }

这里就是我们实际上处理 Touch 事件的地方,当我们返回 true 的时候, android 系统就会将我们设置为默认的 touch 事件处理人,当本次触摸事件结束之前,对应的 touch 事件都会被分配给自己进行处理.免去反复查找的耗时

在这里,我们处理了 down,move,cancel,up,pointer_down,pointer_up 五种事件,这五种事件分别代表着手指开始触摸,手指移动,取消触摸事件,手指抬起,多点触摸开始,多点触摸结束

 a.手指开始触摸:

在开始触摸的时候,我们记录下当前的坐标位置,以供移动事件时进行比对,并通过回调接口,告诉用户触摸事件开始

b.手指移动:

我们获取当前的坐标位置,并将其与之前上一次的坐标位置进行比对,判断手指移动区域范围,若范围大于最小移动范围常量,则确定用户是开始移动,然后通过回调接口,告诉用户移动偏移量

c.手气抬起:

这时候用户本次的触摸事件结束,我们通过速度计算器计算出当前的 x 轴, y 轴 速度,通过回调接口告知用户

d.取消触摸事件:

一般而言不会触发该事件,若触发则说明之前的操作无效,通过回调接口告诉用户

e.多点触摸开始:

重新更正初始坐标

f.多点触摸结束:

重新更正初始坐标


通过 TouchHandler 工具类,我们处理了相关的触摸事件,并告知回调接口,这能够很好的将触摸事件与 view 本身进行分离,而且不同的 view 当面临 touch 的时候,他们的触摸事件处理都是一致的,因此我们使用工具类进行委托能够帮助我们降低类的耦合程度.


好了关于处理触摸事件就到这里,再下面一节就会跟大家详细介绍如何根据 touch 的回调判断具体的移动操作.


0 0
原创粉丝点击