Android 手势识别类 ( 三 ) GestureDetector 源码浅析

  前言:上 篇介绍了提供手势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的手势,是需要存储以及在必要的利用时加载取出手势。所 以,用户绘制出的一个完整的手势是需要一定的代码机制来完成存储以及必要时加载取出的;那么,在源码中Gesture这个类就是用来描述完整的手势的。一 个Gesture就是用户手指在触摸屏上绘制形成的不规则几何图形(A gesture is a hand-drawn shape on a touch screen);

   一. Gesture的组成

我们知道,当我们在GestureOverlayView上绘制手势时,形成的不规则几何图形是由多数个点形成的,这些点都有其对应的在屏幕上的坐 标值和时间戳(event.getEventTime());那么,这些点是如何组成Gesture的呢?针对这个问题,通过对Android手势源码的 浅析来寻求答案;

下图总体上大概描述了Gesture的形成结构,如下:

%title插图%num

从上图描述的类关系中,可以知道:

1. 触摸屏上的点首先是通过GesturePoint这个类来描述的,GesturePoint封装点的x,y轴值和时间戳。

2. GesturePoint中封装的点的信息会在GestureStroke类中被拆解处理,点对应的x,y值被拆解存放在GestureStroke的 float类型数组成员points中,而点对应的时间戳则放在long类型成员数组timestamps中。

3. GestureStroke表示一个手势行程(用户手指点下屏幕到手势离开屏幕绘制出的轨迹就是一个手势行程)。一个完整的手势由一个或多个手势行程组成(单笔画或多笔画绘制手势)

4. Gesture由单个或多个GestureStroke组成,Gesture类中的mStrokeBuffer成员为ArrayList类型集合,存放的是GestureStroke;

   二. Gesture的形成过程:

当我们在GestureOverlayView上绘制手势时,会调用GestureOverlayView的touchDown、touchMove、touchUp方法,然后将通过这个三个方法捕获到的形成手势的多数个点组成Gesture。如下代码:

 

  1. public class GestureOverlayView extends FrameLayout {  
  2.     private void touchDown(MotionEvent event) {  
  3.     …
  4.         mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));  
  5.     …
  6.     }
  7.     private Rect touchMove(MotionEvent event) {  
  8.     …
  9.         mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));  
  10.     …
  11.     }
  12.     private void touchUp(MotionEvent event, boolean cancel) {  
  13.     …
  14.          mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer));  
  15.     …
  16.     }
  17. }

—->通过上面的代码可知,当用户正在绘制手势时,会调用touchDown、touchMove,执行mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())),实现将点的x、y、event.getEventTime() 值作为GesturePoint的构造函数的实参创建GesturePoint对象,然后将得到的GesturePoint添加到mStrokeBuffer集合中(mStrokeBuffer为ArrayList<GesturePoint>类型);

GesturePoint的源代码如下:

 

  1. /* 
  2.  * Copyright (C) 2008-2009 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the “License”); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an “AS IS” BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16. package android.gesture;  
  17. import java.io.DataInputStream;  
  18. import java.io.IOException;  
  19. /** 
  20.  * A timed point of a gesture stroke. Multiple points form a stroke. 
  21.  */  
  22. //一个手势行程的定时点,多个点形成一个手势行程。GesturePoint封装x,y轴和时间戳的值  
  23. public class GesturePoint {  
  24.     public final float x;  
  25.     public final float y;  
  26.     public final long timestamp;  
  27.     public GesturePoint(float x, float y, long t) {  
  28.         this.x = x;  
  29.         this.y = y;  
  30.         timestamp = t;
  31.     }
  32.     //从输入流中读取之前保存在文件中的数据  
  33.     static GesturePoint deserialize(DataInputStream in) throws IOException {  
  34.         // Read X and Y  
  35.         final float x = in.readFloat(); //从输入流中读出对应x轴的坐标值 (来自通过调用GestureStroke的函数serialize保存的,下同)  
  36.         final float y = in.readFloat(); //从输入流中读出对应y轴的坐标值  
  37.         // Read timestamp  
  38.         final long timeStamp = in.readLong(); //从输入流中读出对应的时间戳  
  39.         return new GesturePoint(x, y, timeStamp);  
  40.     }
  41.     @Override  
  42.     public Object clone() {  
  43.         return new GesturePoint(x, y, timestamp);  
  44.     }
  45. }

通过源码可知,在GesturePoint的构造函数中,将传进来的点的各个信息值分别赋值给自身的成员变量x、y、timestamp;所以GesturePoint描述的就是组成完成手势中的一个点元素;而GestureOverlayView中的mStrokeBuffer集合保存着组成手势的多数个点

—->紧接着,当用户完成手势绘制手指离开屏幕时,会调用touchUp,执行 mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer)),实现将之前绘制手势得到的mStrokeBuffer集合作为GestureStroke构造函数的实参创建GestureStroke对象,然后将GestureStroke对象作为mCurrentGesture(Gesture对象)的方法addStroke的实参,实现将GestureStroke添加到Gesture中;

GesturePoint的部分源代码如下:

  1. /** 
  2.  * A gesture stroke started on a touch down and ended on a touch up. A stroke 
  3.  * consists of a sequence of timed points. One or multiple strokes form a gesture. 
  4.  */  
  5. public class GestureStroke {  
  6.     public final float length;  //length为手势行程的长度     
  7.     public final float[] points; //保存组成手势行程的多数个点的x,y坐标值   
  8.     private final long[] timestamps;//保存组成手势行程的多数个点的时间戳  
  9.     /** 
  10.      * A constructor that constructs a gesture stroke from a list of gesture points. 
  11.      *  
  12.      * @param points 
  13.      */  
  14.     public GestureStroke(ArrayList<GesturePoint> points) {  
  15.         final int count = points.size();  
  16.         final float[] tmpPoints = new float[count * 2];  
  17.         final long[] times = new long[count];  
  18.         RectF bx = null;  
  19.         float len = 0;  
  20.         int index = 0;  
  21.         for (int i = 0; i < count; i++) {  
  22.             final GesturePoint p = points.get(i);  
  23.             tmpPoints[i * 2] = p.x; //偶数位置保存x值  
  24.             tmpPoints[i * 2 + 1] = p.y; //奇数位置保存x值  
  25.             times[index] = p.timestamp;
  26.             if (bx == null) {  
  27.                 bx = new RectF();  
  28.                 bx.top = p.y;
  29.                 bx.left = p.x;
  30.                 bx.right = p.x;
  31.                 bx.bottom = p.y;
  32.                 len = 0;  
  33.             } else {  
  34.                 //Math.pow(a,b)为a的b次方,如Maht.pow(3,2)等于9。下面的公式相当于平方和的根号值  
  35.                 len += Math.sqrt(Math.pow(p.x – tmpPoints[(i – 1) * 2], 2)  
  36.                         + Math.pow(p.y – tmpPoints[(i –1 ) * 2 + 1], 2));  
  37.                 //放大bx覆盖到指定的p.x, p.y点  
  38.                 bx.union(p.x, p.y);
  39.             }
  40.             index++;
  41.         }
  42.         timestamps = times;
  43.         this.points = tmpPoints;  
  44.         boundingBox = bx;
  45.         length = len;
  46.     }
  47. }

通过上面的代码可知,当我们创建GestureStroke的对象时,会执行GestureStroke的构造函数。而在GestureStroke的构造函数中,实现将传进来的mStrokeBuffer集合中封存的多个点进行遍历拆解出来,然后分别赋值给GestureStroke的数组成员变量points,timestamps,同时也根据点的坐标值计算出手势行程的长度length;

—->接着,将创建得到的GestureStroke对象通过调用Gesture的addStroke方法添加到Gesture类的mStrokes中,Gesture的addStroke方法源码实现如下:

  1. /** 
  2.  * A gesture is a hand-drawn shape on a touch screen. It can have one or multiple strokes. 
  3.  * Each stroke is a sequence of timed points. A user-defined gesture can be recognized by  
  4.  * a GestureLibrary.  
  5.  */  
  6. /*手势时是触摸屏上手势绘制的形状,它可以单笔画或者多笔画, 
  7.  * 每一个笔画是一个计时点序列,用户绘制定义的手势可以通过GestureLibrary来识别 
  8.  */  
  9. public class Gesture implements Parcelable {  
  10.     private final ArrayList<GestureStroke> mStrokes = new ArrayList<GestureStroke>();  
  11.     …
  12.     /** 
  13.      * Adds a stroke to the gesture. 
  14.      *  
  15.      * @param stroke 
  16.      */  
  17.     public void addStroke(GestureStroke stroke) {  
  18.         mStrokes.add(stroke);
  19.         …
  20.     }
  21.     …
  22. }

所以,在Gesture的成员mStrokes中存放着是用户在触摸屏上绘制形成的当前手势相关信息。在Gesture中会根据得到的mStrokes 中这些信息去进行一些重要的处理,如将其序列化存储(serialize)、手势转化成bitmap显示(toBitmap)、还原手势的绘制路径 (toPath)等;

*后,针对手势组成类之间的关系进行一个小结:

1). GesturePoint: 描述用户手指在屏幕位置的一个定时点,封装用户手指在屏幕上的点坐标值以及时间戳,时间戳由event.getEventTime()决定。

2). GestureStroke:描述用户手指在屏幕上滑动到手指离开屏幕时所产生的轨迹线(由多个时间序列点形成),一个GestureStroke由多个GesturePoint组成。

3). Gesture:实现Parcelable接口,描述用户完成绘制的完整手势,一个Gesture由单个或多个GestureStroke组成。手势绘制可通过GestureOverlayView.setGestureStrokeType(inttype)来设置单笔和多笔画。