前言:Android 关于手势的操作提供两种形式:一种是针对用户手指在屏幕上划出的动作而进行移动的检测,这些手势的检测通过android提供的监听器来实现;另一种是用 户手指在屏幕上滑动而形成一定的不规则的几何图形(即为多个持续触摸事件在屏幕形成特定的形状);本文主要是针对第二种手势的绘制原理进行浅析,我们姑且 称它为输入法手势;

一. 输入法手势

在Android源码中,谷歌提供了相关的手势库源码,供给开发者丰富多彩的接口调用实现;这些提供相关接口的类所在的源码路径为frameworks/base/core/java/android/gesture;

如下图gesture文件中的相关类:

绘制手势需要一个视图界面平台,而这个视图界面平台由GestureOverlayView这个类来实现,该类继承FrameLayout容器视图类。所以,当我们在手机屏幕上画手势时,GestureOverlayView主要负责显示和处理手指在屏幕上滑动所形成的手势。

以下举一个简单的Demo来说明如何通过GestureOverlayView实现在屏幕上绘制手势;

1). main.xml文件代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical" >
  6. <android.gesture.GestureOverlayView
  7. android:id="@+id/gesture"
  8. android:layout_width="fill_parent"
  9. android:layout_height="fill_parent"
  10. >
  11. </android.gesture.GestureOverlayView>
  12. </LinearLayout>

很简单,添加一个android.gesture.GestureOverlayView的布局组件;

2). 加载布局文件和实现手势绘制的Actitivty代码如下:

  1. package com.stevenhu.hu.dgt;
  2. import android.app.Activity;
  3. import android.gesture.Gesture;
  4. import android.gesture.GestureOverlayView;
  5. import android.gesture.GestureOverlayView.OnGesturePerformedListener;
  6. import android.gesture.GestureOverlayView.OnGesturingListener;
  7. import android.os.Bundle;
  8. import android.widget.Toast;
  9. public class DrawGestureTest extends Activity implements OnGesturePerformedListener, OnGesturingListener
  10. {
  11. private GestureOverlayView mDrawGestureView;
  12. /** Called when the activity is first created. */
  13. @Override
  14. public void onCreate(Bundle savedInstanceState)
  15. {
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.main);
  18. mDrawGestureView = (GestureOverlayView)findViewById(R.id.gesture);
  19. //设置手势可多笔画绘制,默认情况为单笔画绘制
  20. mDrawGestureView.setGestureStrokeType(GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE);
  21. //设置手势的颜色(蓝色)
  22. mDrawGestureView.setGestureColor(gestureColor(R.color.gestureColor));
  23. //设置还没未能形成手势绘制是的颜色(红色)
  24. mDrawGestureView.setUncertainGestureColor(gestureColor(R.color.ungestureColor));
  25. //设置手势的粗细
  26. mDrawGestureView.setGestureStrokeWidth(4);
  27. /*手势绘制完成后淡出屏幕的时间间隔,即绘制完手指离开屏幕后相隔多长时间手势从屏幕上消失;
  28. * 可以理解为手势绘制完成手指离开屏幕后到调用onGesturePerformed的时间间隔
  29. * 默认值为420毫秒,这里设置为2秒
  30. */
  31. mDrawGestureView.setFadeOffset(2000);
  32. //绑定监听器
  33. mDrawGestureView.addOnGesturePerformedListener(this);
  34. mDrawGestureView.addOnGesturingListener(this);
  35. }
  36. //手势绘制完成时调用
  37. @Override
  38. public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture)
  39. {
  40. // TODO Auto-generated method stub
  41. showMessage("手势绘制完成");
  42. }
  43. private int gestureColor(int resId)
  44. {
  45. return getResources().getColor(resId);
  46. }
  47. private void showMessage(String s)
  48. {
  49. Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
  50. }
  51. //结束正在绘制手势时调用(手势绘制完成时一般是先调用它在调用onGesturePerformed)
  52. @Override
  53. public void onGesturingEnded(GestureOverlayView overlay)
  54. {
  55. // TODO Auto-generated method stub
  56. showMessage("结束正在绘制手势");
  57. }
  58. //正在绘制手势时调用
  59. @Override
  60. public void onGesturingStarted(GestureOverlayView overlay)
  61. {
  62. // TODO Auto-generated method stub
  63. showMessage("正在绘制手势");
  64. }
  65. @Override
  66. protected void onDestroy()
  67. {
  68. // TODO Auto-generated method stub
  69. super.onDestroy();
  70. //移除绑定的监听器
  71. mDrawGestureView.removeOnGesturePerformedListener(this);
  72. mDrawGestureView.removeOnGesturingListener(this);
  73. }
  74. }

示例代码下载链接地址:http://download.csdn.net/detail/stevenhu_223/5789777
  通过上面的Demo可知,要想实现绘制和监听操作手势,GestureOverlayView是必不可少的,GestureOverlayView为何方神圣
,它是如何实现手势的绘制和监听操作的,接下来将对它进行浅析。

二. GestureOverlayView类浅析

其实手势的绘制原理和前篇<<Android中Path类的lineTo方法和quadTo方法画线的区别>>中绘制轨迹线的原理差不多,只不过在GestureOverlayView中的处理相对比较复杂;

GestureOverlayView继承FrameLayout,所以它也是ViewGroup类型(继承
View),GestureOverlayView重写View的dispatchTouchEvent方法。所以,我们手指在屏幕上触摸滑动时,会调用
GestureOverlayView的dispatchTouchEvent方法;代码如下:

  1. public class GestureOverlayView extends FrameLayout {
  2. ...
  3. @Override
  4. public boolean dispatchTouchEvent(MotionEvent event) {
  5. if (isEnabled()) {
  6. final boolean cancelDispatch = (mIsGesturing || (mCurrentGesture != null &&
  7. mCurrentGesture.getStrokesCount() > 0 && mPreviousWasGesturing)) &&
  8. mInterceptEvents;
  9. processEvent(event);
  10. if (cancelDispatch) {
  11. event.setAction(MotionEvent.ACTION_CANCEL);
  12. }
  13. super.dispatchTouchEvent(event);
  14. return true;
  15. }
  16. return super.dispatchTouchEvent(event);
  17. }
  18. ...
  19. }

isEnabled()得到当前视图的enable状态,若当前视图的enable状态为true,则继续执行processEvent(event),传入参数为对应的滑动事件。

----> 我们接着继续跟踪processEvent方法,代码如下:

  1. ...
  2. private boolean processEvent(MotionEvent event) {
  3. switch (event.getAction()) {
  4. case MotionEvent.ACTION_DOWN:
  5. touchDown(event);
  6. invalidate();
  7. return true;
  8. case MotionEvent.ACTION_MOVE:
  9. if (mIsListeningForGestures) {
  10. Rect rect = touchMove(event);
  11. if (rect != null) {
  12. invalidate(rect);
  13. }
  14. return true;
  15. }
  16. break;
  17. case MotionEvent.ACTION_UP:
  18. if (mIsListeningForGestures) {
  19. touchUp(event, false);
  20. invalidate();
  21. return true;
  22. }
  23. break;
  24. case MotionEvent.ACTION_CANCEL:
  25. if (mIsListeningForGestures) {
  26. touchUp(event, true);
  27. invalidate();
  28. return true;
  29. }
  30. }
  31. return false;
  32. }
  33. ...

在processEvent方法中会根据用户手指对屏幕操作的MotionEvent进行处理:

1). 当MotionEvent事件为ACTION_DOWN时,调用touchDown(MotionEvent event)方法;

2). 当MotionEvent事件为ACTION_MOVE,且mIsListeningForGestures为true时(执行touchDown时赋值为true),调用touchMove(MotionEvent event)方法;

3). 当MotionEvent事件为ACTION_UP,且mIsListeningForGestures为true时,调用touchUp(MotionEvent event, boolean cancel)方法;mIsListeningForGestures在执行touchUp时赋值为false;

4). 当MotionEvent事件为ACTION_CANCEL,且mIsListeningForGestures为true时,调用touchUp(MotionEvent event, boolean cancel)方法;
    接下来逐步分析以上分发处理MotionEvent事件的各个函数的实现:

---->touchDown(MotionEvent event),当用户手指点下屏幕时调用该方法,码如下:

  1. ...
  2. private void touchDown(MotionEvent event) {
  3. mIsListeningForGestures = true;
  4. float x = event.getX();
  5. float y = event.getY();
  6. mX = x;
  7. mY = y;
  8. mTotalLength = 0;
  9. mIsGesturing = false;
  10. if (mGestureStrokeType == GESTURE_STROKE_TYPE_SINGLE || mResetGesture) {
  11. if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor);
  12. mResetGesture = false;
  13. mCurrentGesture = null;
  14. mPath.rewind();
  15. } else if (mCurrentGesture == null || mCurrentGesture.getStrokesCount() == 0) {
  16. if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor);
  17. }
  18. // if there is fading out going on, stop it.
  19. //如果手势已正在淡出,则停止它
  20. if (mFadingHasStarted) {
  21. cancelClearAnimation();
  22. } else if (mIsFadingOut) {
  23. setPaintAlpha(255);
  24. mIsFadingOut = false;
  25. mFadingHasStarted = false;
  26. removeCallbacks(mFadingOut);
  27. }
  28. if (mCurrentGesture == null) {
  29. mCurrentGesture = new Gesture();
  30. }
  31. mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
  32. mPath.moveTo(x, y);
  33. //mInvalidateExtraBorder值由设置手势画笔粗细值决定
  34. final int border = mInvalidateExtraBorder;
  35. mInvalidRect.set((int) x - border, (int) y - border, (int) x + border, (int) y + border);
  36. mCurveEndX = x;
  37. mCurveEndY = y;
  38. // pass the event to handlers
  39. final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
  40. final int count = listeners.size();
  41. for (int i = 0; i < count; i++) {
  42. listeners.get(i).onGestureStarted(this, event);
  43. }
  44. }
  45. ...

在touchDown中,实现处理当用户手指在点下屏幕时的一些操作,这些操作包括:

1).
获取用户手指点下屏幕时所在的坐标值x,y,同时将它们分别赋值给全局变量mX,mY;mTotalLength变量代表绘制手势的总长度,在调用
touchDown时,手势还没绘制,所以mTotalLength为0;mIsGesturing描述是否正在绘制手势,为false表示不是正在绘制
手势;
     2). 根据一些条件判断,设置画笔颜色,处理手势画笔的相关状态,以及创建Gesture对象等。

3). 将1)得到的x,y坐标值和event.getEventTime()的值作为GesturePoint构造函数的实参创建GesturePoint对象,并将该对象添加进mStrokeBuffer数组集合。

4). 将1)得到的x,y坐标值作为mPath画笔路径的初始点。

5). 遍历存放OnGestureListener的集合listeners,调用实现OnGestureListener接口的onGestureStarted()方法;

---->touchMove(MotionEvent event),当用户手指在屏幕上滑动时调用该方法,码如下:

  1. ...
  2. private Rect touchMove(MotionEvent event) {
  3. //更新区域
  4. Rect areaToRefresh = null;
  5. final float x = event.getX();
  6. final float y = event.getY();
  7. final float previousX = mX;
  8. final float previousY = mY;
  9. final float dx = Math.abs(x - previousX);
  10. final float dy = Math.abs(y - previousY);
  11. //手势在屏幕滑动的两点之间的距离大于GestureStroke.TOUCH_TOLERANCE的值,则显示手势的绘制
  12. if (dx >= GestureStroke.TOUCH_TOLERANCE || dy >= GestureStroke.TOUCH_TOLERANCE) {
  13. areaToRefresh = mInvalidRect;
  14. // start with the curve end
  15. final int border = mInvalidateExtraBorder;
  16. areaToRefresh.set((int) mCurveEndX - border, (int) mCurveEndY - border,
  17. (int) mCurveEndX + border, (int) mCurveEndY + border);
  18. //设置贝塞尔曲线的操作点为起点和终点的一半
  19. float cX = mCurveEndX = (x + previousX) / 2;
  20. float cY = mCurveEndY = (y + previousY) / 2;
  21. //二次贝塞尔,实现平滑曲线;previousX, previousY为操作点,cX, cY为终点
  22. mPath.quadTo(previousX, previousY, cX, cY);
  23. // union with the control point of the new curve
  24. /*areaToRefresh矩形扩大了border(宽和高扩大了两倍border),
  25. * border值由设置手势画笔粗细值决定
  26. */
  27. areaToRefresh.union((int) previousX - border, (int) previousY - border,
  28. (int) previousX + border, (int) previousY + border);
  29. // union with the end point of the new curve
  30. areaToRefresh.union((int) cX - border, (int) cY - border,
  31. (int) cX + border, (int) cY + border);
  32. //第二次执行时,第一次结束调用的坐标值将作为第二次调用的初始坐标值
  33. mX = x;
  34. mY = y;
  35. mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
  36. //当调用addOnGesturePerformedListener添加手势完成调用的监听器时,mHandleGestureActions为true;
  37. if (mHandleGestureActions && !mIsGesturing) {
  38. mTotalLength += (float) Math.sqrt(dx * dx + dy * dy);
  39. if (mTotalLength > mGestureStrokeLengthThreshold) {
  40. final OrientedBoundingBox box =
  41. GestureUtils.computeOrientedBoundingBox(mStrokeBuffer);
  42. float angle = Math.abs(box.orientation);
  43. if (angle > 90) {
  44. angle = 180 - angle;
  45. }
  46. /*这个条件成立时,说明所手势绘制已经在进行
  47. */
  48. if (box.squareness > mGestureStrokeSquarenessTreshold ||
  49. (mOrientation == ORIENTATION_VERTICAL ?
  50. angle < mGestureStrokeAngleThreshold :
  51. angle > mGestureStrokeAngleThreshold)) {
  52. mIsGesturing = true;
  53. //手势尚未形成的显示颜色
  54. setCurrentColor(mCertainGestureColor);
  55. final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners;
  56. int count = listeners.size();
  57. for (int i = 0; i < count; i++) {
  58. listeners.get(i).onGesturingStarted(this);
  59. }
  60. }
  61. }
  62. }
  63. // pass the event to handlers
  64. final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
  65. final int count = listeners.size();
  66. for (int i = 0; i < count; i++) {
  67. listeners.get(i).onGesture(this, event);
  68. }
  69. }
  70. return areaToRefresh;
  71. }
  72. ...

touchMove方法中主要有以下功能的实现:
     1). touchMove方法返回值类型为Rect(定义一个矩形区域),若返回值不会空,则调用invalidate(Rectrect)刷新;

2). 得到当前的手指滑动所在屏幕位置的x,y坐标值,将x,y值与调用touchDown()时得到的x,y值相减后取绝对值,得到偏移量dx,dy;

3). dx或dy大于指定的GestureStroke.TOUCH_TOLERANCE时(默认值为3),执行画笔绘制手势的实现流程代码。

4). mPath画笔路径调用quadTo()方法执行贝塞尔曲线计算,实现得到平滑曲线。

5). areaToRefresh矩形区域负责根据手势绘制控制点和结束点的位置不断更新,画出手势画笔轨迹(每次调用touchMove()时,areaToRefresh逐点更新从而汇成一定轨迹的几何图形,即手势的雏形)。

6).
将第二步得到x,y坐标值和event.getEventTime()的值作为GesturePoint构造函数的实参创建GesturePoint对
象,并将该对象添加进mStrokeBuffer数组集合。(保存用户在屏幕上绘制形成手势的相关信息)

7).
当调用GestureOverlayView的addOnGesturePerformedListener方法添加监听器
OnGesturePerformedListener时,mHandleGestureActions为true,这时候会执行计算移动所得的这些点集
的最小边界框,然后根据这个最小边界框进行一些条件判断,进而设置mIsGesturering为true,以及设置手势尚未形成绘制手势的显示颜色。

8). touchMove()的最后,遍历存放OnGestureListener接口的集合listeners,调用实现OnGestureListener接口的onGesture方法。

---->touchUp(MotionEvent event, boolean cancel),当用户手指离开屏幕或MotionEvent 事件取消时调用该方法,码如下:

  1. ...
  2. private void touchUp(MotionEvent event, boolean cancel) {
  3. mIsListeningForGestures = false;
  4. // A gesture wasn't started or was cancelled
  5. if (mCurrentGesture != null) {
  6. // add the stroke to the current gesture
  7. /*将之前调用touchDonw和touchMove收集得到GesturePoint的组成的数组集合mStrokeBuffer,
  8. * 做为GestureStroke构造函数的实参创建GestureStroke对象,
  9. * 然后将GestureStroke对象通过调用addStroke方法添加到mCurrentGesture中
  10. */
  11. mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer));
  12. if (!cancel) {
  13. // pass the event to handlers
  14. final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
  15. int count = listeners.size();
  16. for (int i = 0; i < count; i++) {
  17. listeners.get(i).onGestureEnded(this, event);
  18. }
  19. /*当调用addOnGesturePerformedListener方法时,mHandleGestureActions为true;
  20. * mFadeEnabled默认值为true,可通过setFadeEnabled函数设值
  21. */
  22. clear(mHandleGestureActions && mFadeEnabled, mHandleGestureActions && mIsGesturing,
  23. false);
  24. } else {
  25. cancelGesture(event);
  26. }
  27. } else {
  28. cancelGesture(event);
  29. }
  30. mStrokeBuffer.clear();
  31. mPreviousWasGesturing = mIsGesturing;
  32. mIsGesturing = false;
  33. final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners;
  34. int count = listeners.size();
  35. for (int i = 0; i < count; i++) {
  36. listeners.get(i).onGesturingEnded(this);
  37. }
  38. }
  39. ...

touchUp方法中主要有以下功能的实现:
     1). 首先将mIsListeningForGesture赋值为false;

2).
判断当前是否存在mCurrentGesture(Gesture类型),该变量在执行touchDown方法时创建Gesture对象赋值的,也可以通
过调用setGesture方法赋值;(mCurrentGesture描述的就是当前用户绘制形成的整个手势)

3).
若mCurrentGesture不为空,则将之前调用touchDonw和touchMove收集得到的GesturePoint组成的数组集合
mStrokeBuffer做为GestureStroke构造函数的实参,创建GestureStroke对象。然后将GestureStroke对象
通过调用addStroke方法添加到mCurrentGesture中;

4).
若touchUp方法的第二个参数为false(即执行ACTION_UP事件时),则遍历存放OnGestureListener的集合,调用实现该接
口的onGestureEnded()方法。接着调用clear方法,实现将当前绘制形成的手势清除(即手势淡出屏幕;手指离开屏幕时到手势淡出屏幕,这
期间是有时间间隔的,且这个时间间隔也是可以设置);

5).
若touchUp()方法的第二个参数为true(即执行ACTION_CANCEL事件时),调用cancelGesture()方法。在该方法中:首
先遍历存放OnGestureListener的集合,调用实现该接口的onGestureCancelled()方法,接着调用clear()方法实现
回收mCurrentGesture对象、清除画笔等淡出屏幕处理;

---->上面4)中,当touchUp方法的cancel参数为false时,通过调用clear(boolean animated, boolean fireActionPerformed, boolean immediate)处理手势淡出屏幕,我们来看看这个方法的实现,代码如下:

  1. ...
  2. private void clear(boolean animated, boolean fireActionPerformed, boolean immediate) {
  3. setPaintAlpha(255);
  4. removeCallbacks(mFadingOut);
  5. mResetGesture = false;
  6. mFadingOut.fireActionPerformed = fireActionPerformed;
  7. mFadingOut.resetMultipleStrokes = false;
  8. if (animated && mCurrentGesture != null) { //调用addOnGesturePerformedListener时animated为true
  9. mFadingAlpha = 1.0f;
  10. mIsFadingOut = true;
  11. mFadingHasStarted = false;
  12. /*mFadeOffset定义手势淡出屏幕的时间间隔,
  13. * 默认值420,可通过setFadeOffset函数设置
  14. */
  15. mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset;
  16. postDelayed(mFadingOut, mFadeOffset);
  17. } else {
  18. mFadingAlpha = 1.0f;
  19. mIsFadingOut = false;
  20. mFadingHasStarted = false;
  21. if (immediate) {
  22. mCurrentGesture = null;
  23. mPath.rewind();
  24. invalidate();
  25. } else if (fireActionPerformed) {
  26. postDelayed(mFadingOut, mFadeOffset);
  27. } else if (mGestureStrokeType == GESTURE_STROKE_TYPE_MULTIPLE) {
  28. mFadingOut.resetMultipleStrokes = true;
  29. postDelayed(mFadingOut, mFadeOffset);
  30. } else {
  31. mCurrentGesture = null;
  32. mPath.rewind();
  33. invalidate();
  34. }
  35. }
  36. }
  37. ...

通过上面的代码,我们知道,在clear函数中,会通过传入的实参来决定如何去进一步处理手势的淡出,有两种处理方式:

1. 调用mPath.rewind(),将绘制手势的重置清除,然后调用invalidate();

2. 调用postDelayed(mFadingOut, mFadeOffset),到主线程中处理,mFadeOffset就是决定手势淡出屏幕的时间间隔;

我们针对第二种在主线程中处理的方式继续跟踪解析代码,mFadingOut是FadeOutRunnable对象,FadeOutRunnable继承Runnable类,该类的实现代码如下:

  1. ...
  2. /*处理手势淡出;
  3. * 手势淡出的条件:
  4. * 1.前面一次画完手势,且画完的同时没有调用onGesturePerformed,
  5. *   则当用户再次画手势时,前面画出的保留在屏幕上的手势将淡出;
  6. * 2.当画完手势,且添加OnGesturePerformedListener监听器时,
  7. *   在完成手势,调用onGesturePerformed时,将手势轨迹画笔淡出
  8. */
  9. private class FadeOutRunnable implements Runnable {
  10. //调用addOnGesturePerformedListener时为true;
  11. boolean fireActionPerformed;
  12. //手势设置为多笔画绘制时为true;
  13. boolean resetMultipleStrokes;
  14. public void run() {
  15. if (mIsFadingOut) { //fireActionPerformed为true且mCurrentGesture不为空是成立
  16. final long now = AnimationUtils.currentAnimationTimeMillis();
  17. final long duration = now - mFadingStart;
  18. //mFadeDuration默认值为150
  19. if (duration > mFadeDuration) {
  20. if (fireActionPerformed) {
  21. //调用onGesturePerformed方法
  22. fireOnGesturePerformed();
  23. }
  24. mPreviousWasGesturing = false;
  25. mIsFadingOut = false;
  26. mFadingHasStarted = false;
  27. mPath.rewind();
  28. mCurrentGesture = null;
  29. setPaintAlpha(255);
  30. } else {
  31. mFadingHasStarted = true;
  32. float interpolatedTime = Math.max(0.0f,
  33. Math.min(1.0f, duration / (float) mFadeDuration));
  34. mFadingAlpha = 1.0f - mInterpolator.getInterpolation(interpolatedTime);
  35. setPaintAlpha((int) (255 * mFadingAlpha));
  36. //FADE_ANIMATION_RATE默认值为16
  37. postDelayed(this, FADE_ANIMATION_RATE);
  38. }
  39. } else if (resetMultipleStrokes) { //fireActionPerformed为false且手势为多笔画绘制时成立
  40. mResetGesture = true;
  41. } else {
  42. //调用实现监听器OnGesturePerformedListener的onGesturePerformed方法
  43. fireOnGesturePerformed();
  44. mFadingHasStarted = false;
  45. mPath.rewind();
  46. mCurrentGesture = null;
  47. mPreviousWasGesturing = false;
  48. setPaintAlpha(255);
  49. }
  50. invalidate();
  51. }
  52. }
  53. ...

值得注意的是,在主线程中处理手势淡出屏幕,当我们绑定了监听器OnGesturePerformedListener,手势淡出屏幕时会调用fireOnGesturePerformed方法,该方法实现遍历存放OnGesturePerformedListener的集合actionListeners,进而调用实现OnGesturePerformedListener接口的函数onGesturePerformed,代码如下:

  1. ...
  2. private void fireOnGesturePerformed() {
  3. final ArrayList<OnGesturePerformedListener> actionListeners = mOnGesturePerformedListeners;
  4. final int count = actionListeners.size();
  5. for (int i = 0; i < count; i++) {
  6. actionListeners.get(i).onGesturePerformed(GestureOverlayView.this, mCurrentGesture);
  7. }
  8. }
  9. ...

最后,有一点值得注意,
当我们手指在触摸屏上滑动时,在processEvent方法中,每次执行完touchDown、touchMove方法后都会调用
invalidate()、invalidate(rect)进行不断的刷新,那么这时候就调用draw方法将用户在触摸屏上绘制的手势轨迹显示出来,代
码如下:

  1. ...
  2. @Override
  3. public void draw(Canvas canvas) {
  4. super.draw(canvas);
  5. if (mCurrentGesture != null && mGestureVisible) {
  6. canvas.drawPath(mPath, mGesturePaint);
  7. }
  8. }
  9. ...

至此,关于实现手势绘制的视图平台类GestureOverlayView的浅析就结束了!

借鉴:http://blog.csdn.net/stevenhu_223/article/details/9394491

Android 手势识别类 ( 二 ) GestureDetector 源码浅析的更多相关文章

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

    前言:上 篇介绍了提供手势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的手势,是需要存储以及在必要的利用时加载取出手势.所 以,用户绘制出的一个完整的手势是需要一定的代码 ...

  2. Android Handler机制(二)---MessageQueue源码解析

    MessageQueue 1.变量 private final boolean mQuitAllowed;//表示MessageQueue是否允许退出 @SuppressWarnings(" ...

  3. 15 BasicHashTable基本哈希表类(二)——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...

  4. Android 手势识别类 ( 一 ) GestureDetector 基本介绍

    为了加强鼠标响应事件,Android提供了GestureDetector手势识别类.通过GestureDetector.OnGestureListener来获取当前被触发的操作手势(Single Ta ...

  5. Android源码浅析(二)——Ubuntu Root,Git,VMware Tools,安装输入法,主题美化,Dock,安装JDK和配置环境

    Android源码浅析(二)--Ubuntu Root,Git,VMware Tools,安装输入法,主题美化,Dock,安装JDK和配置环境 接着上篇,上片主要是介绍了一些安装工具的小知识点Andr ...

  6. Android开发之Theme、Style探索及源码浅析

    1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...

  7. Android源码浅析(四)——我在Android开发中常用到的adb命令,Linux命令,源码编译命令

    Android源码浅析(四)--我在Android开发中常用到的adb命令,Linux命令,源码编译命令 我自己平时开发的时候积累的一些命令,希望对你有所帮助 adb是什么?: adb的全称为Andr ...

  8. Android源码浅析(三)——Android AOSP 5.1.1源码的同步sync和编译make,搭建Samba服务器进行更便捷的烧录刷机

    Android源码浅析(三)--Android AOSP 5.1.1源码的同步sync和编译make,搭建Samba服务器进行更便捷的烧录刷机 最近比较忙,而且又要维护自己的博客,视频和公众号,也就没 ...

  9. Android源码浅析(一)——VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置

    Android源码浅析(一)--VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置 最近地方工作,就是接触源码的东西了,所以好东西还是要分享,系列开了这 ...

随机推荐

  1. Wechat4j之Hello world——使用wechat4j快速开发java版微信公众号

    Wechat4j是一个开源的java微信开发框架,是目前最简单易用的java微信开发框架. 项目地址:https://github.com/sword-org/wechat4j Wechat4j.ja ...

  2. 使用一般处理程序HTTPHandler下载文件

    一般来说我们可以用HTTPHandler来处理一些简单的逻辑,比如验证码.下载文件等. 以下载word文档为例讲解一下如何在HHTPHandler中下载文件,不限于word文档,如果下载其他文件,需要 ...

  3. CRM 2013 系统设置新功能二:Entity images 图像字段

    CRM2013系统默认的许多实体都已经有了图像的选择,如 右上角可以设置当前用户的头像. 客户.联系人等都可以,下面将介绍一下此项新功能: 一.实体中新建图像字段,其中系统自动命名entityinag ...

  4. Button 对 TreeView1 所有节点的全选

    protected void Button1_Click(object sender, EventArgs e)    {        for (int i = 0; i < this.Tre ...

  5. ArcGis在Oracle中常用的sql

    ) m 查询坐标数据 更新坐标: update tableName set SHAPE = mdsys.sdo_geometry(2002,28420,null,mdsys.sdo_elem_info ...

  6. Android项目实战(十四):TextView显示html样式的文字

    项目需求: TextView显示一段文字,格式为:(消息个数,不确定)条消息 这段文字中名字和数字的长度是不确定的,还要求名字和数字各自有各自的颜色. 一开始我想的是用(转) SpannableStr ...

  7. 汇编语言--寄存器(cpu工作原理)

    本文地址:http://www.cnblogs.com/archimedes/p/assembly-register.html,转载请注明源地址. 本文主要将介绍的是8086 CPU中的寄存器, 寄存 ...

  8. 错误:升级为xcode8之后无法上网的解决方法

    主要是在info.list中增加以下的节点,因为XCode开始所有的http都转为https来联网了. 添加类型为Dictionary的:NSAppTransportSecurity 再添加节点为:N ...

  9. java:String使用equals和==比较的区别

    原文链接:http://www.cnblogs.com/tinyphp/p/3768214.html "=="操作符的作用 1.用于基本数据类型的比较 2.判断引用是否指向堆内存的 ...

  10. windows 编程中的常见bug

    错误 1 :   error LNK2001: 无法解析的外部符号 _WTSQueryUserToken@8 解决办法:   ——>查看链接器->输入->附加依赖项,依照debug模 ...