Android开发艺术探索知识回顾——第3章 View的事件体系:1、View的基础知识

目录

第3章 View的事件体系

3.1 View基础知识

3.1.1 什么是View

3.1.2 View的位置参数

3.1.3 MotionEvent 和 TouchSlop

1、MotionEvent

2、TouchSlop(滑动的最小距离)

3.1.4 VelocityTracker、GestureDetector 和 Scroller

1、VelocityTracker(手指速度追踪)

2、GestureDetector(手势检测)

表 3-1 OnGestureListener 和 OnDoubleRpListener 中的方法介绍

3、Scroller(弹性滑动对象)


第3章 View的事件体系

本章将介绍 Android 中十分重要的一个概念:View,虽然说View不属于四大组件,但是它的作用堪比四大组件,甚至比 ReceiverProvider 的重要性都要大。在 Android 开发中,Activity 承担这可视化的功能,同时 Android 系统提供了很多基础控件,常见的有Button, TextView, CheckBox等。

很多时候仅仅使用系统提供的控件是不能满足需求的,因此我们就需要能够根据需求进行新控件的定义,而控件的自定义就需要对 Android 的 View 体系有深入的理解,只有这样才能写出完美的自定义控件

同时 Android 手机属于移动设备,移动设备的一个特点就是用户可以直接通过屏幕来进行一系列操作,一个典型的场景就是屏幕的滑动,用户可以通过滑动来切换到不同的界面。很多情况下我们的应用都需要支持滑动操作,当处于不同层级的 View 都可以响应用户的滑动操作时,就会带来一个问题,那就是滑动冲突。

如何解决滑动冲突呢?这对于初学者来说的确是个头疼的问题,其实解决滑动冲突本不难,它需要读者对 View 的事件分发机制有一定的了解,在这个基础上,我们就可以利于这个特性从而得出滑动冲突的解决方法。上述这些内容就是本章所要介绍的内容,同时,View 的内部工作原理和自定义 View 相关的知识会在第4章进行介绍。

 

3.1 View基础知识

本节主要介绍 View 的一些基础知识,从而为更好地介绍后续的内容做铺垫。主要介绍的内容有:View 的位置参数、MotionEvent TouchSlop 对象、VelocityTrackerGestureDetector 和 Scroller 对象,通过对这些基础知识的介绍,可以方便读者理解更复杂的内容。类似的基础概念还有不少,但是本节所介绍的都是一些比较常用的,其他不常用的基础概念读者可以自行了解。

 

3.1.1 什么是View

在介绍 View 的基础知识之前,我们首先要知道到底什么是 View。View 是 Android 中所有控件的基类,不管是简单的 Button和 TextView还是复杂的 RelativeLayout 和 ListView,它们的共同基类都是 View。所以说,View 是一种界面层的控件的一种抽象,它代表了一 个控件。

除了 View,还有ViewGroup,从名字来看,它可以被翻译为控件组,言外之意是 ViewGroup 内部包含了许多个控件,即一组 ViewAndroid 的设计中,ViewGroup 也继承了View,这就意味着 View 本身就可以是单个控件也可以是由多个控件组成的一组控件,通过这种关系就形成了 View 树的结构,这和 Web 前端中的 DOM 树的概念是相似的。

根据这个概念,我们知道,Button 显然是个 View,而 LinearLayout 不但是一个 View 而且还是一个 ViewGroup,而 ViewGroup 内部是可以有子 View 的,这个子 View 同样还可以是 ViewGroup,依此类推。

明白 View 的这种层级关系有助于理解 View 的工作机制。如图3-1所示,可以看到自定义 的 TestButton 是一个View,它继承了TextView,而 TextView 则直接继承了View,因此不管怎么说,TestButton都是一个View,同理我们也可以构造出一个继承自 ViewGroup 的控件。

 

3.1.2 View的位置参数

View 的位置主要由它的四个顶点来决定,分别对应于 View 的四个属性:topleftright、bottom,其中 top 是左上角纵坐标,left是左上角横坐标,right是右上角横坐标,bottom是 右下角纵坐标。

需要注意的是,这些坐标都是相对于 View 的父容器来说的,因此它是一种相对坐标,View 的坐标和父容器的关系如图3-2所示。在Android中,x轴和y轴的正方向分别为右和下,这点不难理解,不仅仅是Android,大部分显示系统都是按照这个标准来定义坐标系的。

 

根据图3-2,我们很容易得出View的宽高和坐标的关系:

width = right - left 
height =bottom - top

那么如何得到 View 的这四个参数呢?也很简单,在 View 的源码中它们对应于mLeftmRightmTopmBottom 这四个成员变量,获取方式如下所示。

• Left = getLeft()

• Right = getRight()

• Top = getTop()

• Bottom = getBottom();

Android3.0 开始,View 增加了额外的几个参数:xy、translationX translationY,其中 x 和 y 是 View 左上角的坐标,而translationX 和 translationY 是 View 左上角相对于父容器的偏移量。这几个参数也是相对于父容器的坐标,并且 translationX translationY 的默认值是 0,和 View 的四个基本的位置参数一样,View也为它们提供了 get/set 方法,这几个参数的换算关系如下所示。

x = left + translationX
y = top + translationY

需要注意的是:View在平移的过程中,top 和 left 表示的是原始左上角的位置信息,其值并不会发生改变,此时发生改变的是xytranslationX 和 translationY 这四个参数。

 

3.1.3 MotionEvent TouchSlop

1、MotionEvent

在手指接触屏幕后所产生的一系列事件中,典型的事件类型有如下几种:

  • ACTION_DOWN——手指刚接触屏幕;
  • ACTION_MOVE——手指在屏幕上移动;
  • ACTION_UP——手机从屏幕上松开的一瞬间。

正常情况下,一次手指触摸屏幕的行为会触发一系列点击事件,考虑如下几种情况:

  • 点击屏幕后离开松开,事件序列为DOWN -> UP
  • 点击屏幕滑动一会再松开,事件序列为DOWN -> MOVE -> ...> MOVE -> UP

上述三种情况是典型的事件序列,同时通过 MotionEvent 对象我们可以得到点击事件发生的 和 坐标。为此,系统提供了两组方法:getX/getY 和 getRawX/getRawY它们的区别其实很简单,getX/getY 返回的是相对于当前 View 左上角的 和 坐标,而 getRawX/getRawY 返回的是相对于手机屏幕左上角的 和 坐标。

 

2、TouchSlop(滑动的最小距离)

TouchSlop 是系统所能识别出的被认为是滑动的最小距离,换句话说,当手指在屏幕上滑动时,如果两次滑动之间的距离小于这个常量,那么系统就不认为你是在进行滑动操作。 原因很简单:滑动的距离太短,系统不认为它是滑动。这是一个常量,和设备有关,在不同设备上这个值可能是不同的,通过如下方式即可获取这个常量:Viewconfiguration.get(getContext()).getScaledTouchSlop();

这个常量有什么意义呢?当我们在处理滑动时,可以利用这个常量来做一些过滤,比如当两次滑动事件的滑动距离小于这个值,我们就可以认为未达到滑动距离的临界值,因此就可以认为它们不是滑动,这样做可以有更好的用户体验。

其实如果细心的话,可以在源码中找到这个常量的定义,在 frameworks/base/core/ res/res/values/config.xm文件中,如下所示。这个"config_viewConfigurationTouchSlop" 对 应的就是这个常量的定义。

< !-- Base "touch slop" value used by Viewconfiguration as a movement threshold 
where scrolling should begin. -->
<dimen name="config_viewConfigurationTouchSlop">8dp</dimen>

 

3.1.4 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();

在这一步中有两点需要注意:

第一点,获取速度之前必须先计算速度,即 getXVelocity 和 getYVelocity 这两个方法的前面必须要调用 computeCurrentVelocity 方法;

第二点,这里的速度是指一段时间内手指所滑过的像素数,比如将时间间隔设为1000ms时,在 1内,手指在水平方向从左向右滑过100像素,那么水平速度就是100。注意速度可以为负数,当手指从右往左滑动时,水平方向速度即为负值,这个需要理解一下。速度的计算可以用如下公式来表示:

速度 = (终点位置-起点位置) / 时间段

根据上面的公式再加上 Android 系统的坐标系,可以知道,手指逆着坐标系的正方向滑动,所产生的速度就为负值。另外,computeCurrentVelocity 这个方法的参数表示的是一个时间单元或者说时间间隔,它的单位是毫秒(ms),计算速度时得到的速度就是在这个时间间隔内手指在水平或竖直方向上所滑动的像素数。

针对上面的例子,如果我们通过 velocityTracker.computeCurrentVelocity(100) 来获取速度,那么得到的速度就是手指在100ms 内所滑过的像素数,因此水平速度就成了 10像素/每100ms  (这里假设滑动过程是匀速的),

即水平速度为10,这点需要好好理解一下。

最后,当不需要使用它的时候,需要调用 clear 方法来重置并回收内存:

velocityTracker.clear ();
velocityTracker.recycle();

上面就是如何使用 VelocityTracker 对象的全过程,看起来并不复杂。

 

2、GestureDetector(手势检测

手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。要使用 GestureDetector 也不复杂,参考如下过程。

首先,需要创建一个 GestureDetector 对象并实现 OnGestureListener 接口,根据需要我们还可以实现 OnDoubleTapListeiier 从而能够监听双击行为:

GestureDetector mGestureDetector = new GestureDetector(this);
//解决长按屏幕后无法拖动的现象
mGestureDetector.setlsLongpressEnabled(false);

接着,接管目标 View 的 onTouchEvent 方法,在待监听 View 的 onTouchEvent 方法中添加如下实现:

boolean consume = mGestureDetector.onTouchEvent(event);
return consume;

做完了上面两步,我们就可以有选择地实现 OnGestureListener 和 OnDoubleTapListener 中的方法了,这两个接口中的方法介绍如表3-1所示。

3-1 OnGestureListener OnDoubleRpListener 中的方法介绍

3.1里面的方法很多,但是并不是所有的方法都会被时常用到,在日常开发中,比较常用的有:onSingleTapUp(单击)、onFling快速滑动)、onScroll(拖动)、onLongPress(长按)和 onDoubleTap(双击)。

另外这里要说明的是,实际开发中,可以不使用 GestureDetector,完全可以自己在 View 的 onTouchEvent 方法中实现所需的监听,这个就看个人的喜好了。这里有一个建议供读者参考:如果只是监听滑动相关的,建议自己在 onTouchEvent 中实现,如果要监听双击这种行为的话,那么就使用GestureDetector。

 

3、Scroller(弹性滑动对象)

弹性滑动对象,用于实现 View 的弹性滑动。我们知道,当使用 View 的 scrollTo/scrollBy 方法来进行滑动时,其过程是瞬间完成的,这个没有过渡效果的滑动用户体验不好。这个时候就可以使用Scroller来实现有过渡效果的滑动,其过程不是瞬间完成的,而是在一定的时间间隔内完成的。

Scroller 本身无法让 View 弹性滑动,它需要和 View 的 computeScroll 方法配合使用才能共同完成这个功能。那么如何使用Scroller呢?它的典型代码是固定的,如下所示。至于它为什么能实现弹性滑动,这个在3.2节中会进行详细介绍。

Scroller scroller = new Scroller(mContext);

//缓慢滚动到指定位置
private void smoothScrollTo(int destX,int destY){
        int scrollX = getScrollX();
        int delta = destX - scrollX;
        //1000ms内滑向destX,效果就是慢慢的滑动
        scroller.startScroll(scrollX,0,delta,0,1000);
        invalidate();
 }

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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

被开发耽误的大厨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值