View的事件体系基础知识

本文详细介绍了Android中视图滑动的各种方式,包括scrollTo和scrollBy、属性动画、改变布局参数等方法,并深入探讨了触摸事件的工作原理及如何利用MotionEvent、TouchSlop、VelocityTracker和GestureDetector进行高级手势识别。

View的位置参数

这里写图片描述
View的四个位置主要有思安个顶点决定,分别对应四个属性,top、left、right、bottom.

  • top:表示View的原始左上角的纵坐标
  • left:表示View的原始左上角的横坐标
  • bottom:表示View的原始右下角的纵坐标
  • right:表示View的原始右下角的横坐标

这些坐标都是相对于View的父容器来说的,都是相对坐标
从Android3.0开始增加了几个参数:

  • x:表示View左上角的横坐标
  • y:表示View左上角的纵坐标
  • translationX:表示View左上角相对于父容器的横坐标的偏移量,默认值是0
  • translationY:表示View左上角相对于父容器的纵坐标的偏移量,默认值是0

换算关系是:

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

1.View属性动画的时候,变化的是x / y和translationX / translationY,left / right和top / bottom不会改版变化

2.其他不是属性动画情况,translationX,translationY会一直为0

MotionEvent的位置坐标信息

这里写图片描述
MotionEvent提供的方法:

  • getX():点击位置到View的左边的距离
  • getY():点击位置到View的上边的距离
  • getRawX():点击位置到手机屏幕左边的距离
  • getRawY();点击位置到手机屏幕上边的距离

TouchSlop是系统所能识别出的最小的滑动距离

ViewConfiguration.get(getContext()).getScaledTouchSlop()可获取,在处理滑动时,可以利用它来过滤

速度追踪

VelocityTracker,用于追踪手机在滑动过程中的速度,包括水平和竖直方向的速度
使用过程如下:
首先初始化VelocityTracker对象

VelocityTracker  mVelocityTracker = VelocityTracker.obtain();

在View的onTouchEvent方法中追踪当前点击事件的速度

@Override
    public boolean onTouchEvent(MotionEvent event) {
        mVelocityTracker.addMovement(event);
    }

通过如下方式来获取速度

		mVelocityTracker.computeCurrentVelocity(1000);//计算速度
        float xVelocity = mVelocityTracker.getXVelocity();//获取x方向的速度
        float yVelocity = mVelocityTracker.getYVelocity();//获取y方向的速度

computeCurrentVelocity这个方法的参数表示的是一个时间单元或者时间间隔,单位毫秒,在这时间间隔内手指在水平或竖直方向所划过的像素数。
不使用了的时候,重置并回收

	 mVelocityTracker.clear();//重置
	 mVelocityTracker.recycle();//回收

手势检测,用于辅助检测用户的单击,滑动,长按,双击等行为。

1.创建一个GestureDetector对象并实现OnGestureListener接口,还可以实现OnDoubleTapListener监听双击的行为~~~

mGestureDetector = new GestureDetector(new MyOnGestureListener());//MyOnGestureListener是OnGestureListener接口实现类
//下面这一步是对双击的监听
mGestureDetector.setOnDoubleTapListener(new MyOnDoubleTapListener());//MyOnDoubleTapListener是OnDoubleTapListener接口实现类

2.接管View的onTouchEvent方法,onTouchEvent或者OnTouchListener中添加如下:

		imgView.setOnTouchListener(new OnTouchListener() {
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				boolean consume = mGestureDetector.onTouchEvent(event);//这里添加了
				return consume;
			}
		});
		//或者
	public boolean onTouchEvent(MotionEvent event) {
			boolean consume = mGestureDetector.onTouchEvent(event);//这里添加了
			return consume;
}

做完上面两步,我们就可以实现对手势检测了。
查看Android源码发现SimpleOnGestureListener,实现了onGestureListener和OnDoubleTapListener

    public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener,
            OnContextClickListener {

其实实现这个两个接口要实现好多方法,以后我们使用SimpleOnGestureListener可以有选择的实现某些常用的方法,减少代码量。

View的滑动

View的各种滑动方式Demo下载
在这里插入图片描述

1. scrollTo和scrollBy方式

  • View的scrollTo和scrollBy是瞬间完成滑动
  • 这里滑动的是View的内容,View的本身并没有移动
  • scrollBy是相对于上次的滑动。scrollTo是滑动到某个位置
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) event.getX();
                lastY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                ((View) getParent()).scrollBy(-offsetX, -offsetY);//偏移量
                //或者用下面的scrollTo
                // int mScrollX = ((View) getParent()).getScrollX();//初始x
                // int mScrollY = ((View) getParent()).getScrollY();//初始y
                // ((View) getParent()).scrollTo(mScrollX - offsetX, mScrollY - offsetY);//加上偏移量
                break;
        }
        return true;
    }

2.属性动画方式 Android属性动画

    // 属性动画方式,,相对坐标
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 记录触摸点坐标
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算偏移量
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                // 改变x y
                setTranslationX(getTranslationX() + offsetX);
                setTranslationY(getTranslationY() + offsetY);
                break;
        }
        return true;
    }

3.改变布局参数MarginLayoutParams方式

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 记录触摸点坐标
                lastX = (int) event.getX();
                lastY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算偏移量
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
                layoutParams.leftMargin = getLeft() + offsetX;
                layoutParams.topMargin = getTop() + offsetY;
                setLayoutParams(layoutParams);
                break;
        }
        return true;
    }

4.改变View的left、top、right、bottom

4.1视图坐标方式——使用相对坐标

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 记录触摸点坐标
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算偏移量
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                // 在当前left、top、right、bottom的基础上加上偏移量
                layout(getLeft() + offsetX,
                        getTop() + offsetY,
                        getRight() + offsetX,
                        getBottom() + offsetY);
                //或者下面这样,原理同上面一样
                        //offsetLeftAndRight(offsetX);
                        //offsetTopAndBottom(offsetY);
                break;
        }
        return true;
    }

4.2视图坐标方式——使用绝对坐标

  @Override
    public boolean onTouchEvent(MotionEvent event) {
        int rawX = (int) (event.getRawX());
        int rawY = (int) (event.getRawY());
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 记录触摸点坐标
                lastX = rawX;
                lastY = rawY;
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算偏移量
                int offsetX = rawX - lastX;
                int offsetY = rawY - lastY;
                // 在当前left、top、right、bottom的基础上加上偏移量
                layout(getLeft() + offsetX,
                        getTop() + offsetY,
                        getRight() + offsetX,
                        getBottom() + offsetY);
                // 重新设置初始坐标
                lastX = rawX;
                lastY = rawY;
                break;
        }
        return true;
    }

当然其他滑动方式也都可以使用绝对坐标,也是没有问题的。

5.弹性滑动

View也是滑动的内容,本身不滑动

5.1初始化Scroller

        // 初始化Scroller
        mScroller = new Scroller(context);

5.2 onTouchEvent方法操作Scroller

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) event.getX();
                lastY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                ((View) getParent()).scrollBy(-offsetX, -offsetY);
                break;
            case MotionEvent.ACTION_UP:
                // 手指离开时,执行滑动过程
                View viewGroup = ((View) getParent());
                mScroller.startScroll(
                        viewGroup.getScrollX(),//开始位置x
                        viewGroup.getScrollY(),//开始位置y
                        -viewGroup.getScrollX(),//滑动距离x
                        -viewGroup.getScrollY());//滑动距离y
                invalidate();
                break;
        }
        return true;
    }

5.3 调用computeScroll()刷新

    @Override
    public void computeScroll() {
        super.computeScroll();
        // 判断Scroller是否执行完毕
        if (mScroller.computeScrollOffset()) {
            ((View) getParent()).scrollTo(
                    mScroller.getCurrX(),
                    mScroller.getCurrY());
            // 通过重绘来不断调用computeScroll
            invalidate();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值