前言: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. 【干货分享】Node.js 中文资料导航

    这篇文章与大家分享一批高质量的的 Node.js 中文资料.Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台, 用来方便地搭建快速的, 易于扩展的网络应用 Node ...

  2. ArcGIS制图之Sub Points点抽稀

    简介 Sub Points工具是 Esri 中国自主开发的一个插件,该工具优先考虑点在空间分布上的均匀合理性,并结合点数据中包含的 "优先级" 属性进行筛选.通过获取每个点在一定范 ...

  3. gridView使用

    只读 for (int i = 0; i <9; i++) { this.gridView1.Columns[i].OptionsColumn.ReadOnly = true; } 不显示面板 ...

  4. ssh无法登录linux服务器的解决办法

    最近之前使用的一台linux服务器被长官重装系统了,导致ssh登录的时候出现如下错误: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ...

  5. 【读书笔记】iOS-查看一个软件ipa包的内容

    一,打开itunes----->我的iPhone应用程序. 二,右键点击app---->在Finder中显示---->出现下图所示界面. 三,将上图中的ipa包拷贝到桌面,如下图所示 ...

  6. 【读书笔记】iOS-Xcode-查找特殊字符的方法

    如图所示,为搜索图框,然后,点击放大镜图标------->Insert Pattern---->即可看到特殊字符----->选择特殊字符进行插入. 参考资料:<iOS开发进阶& ...

  7. iOS 清理缓存功能的实现第二种方法

    /** * 清理缓存第二种方法 * * @param sender <#sender description#> */ - (void)clearCache:(id)sender { // ...

  8. iOS之 PJSIP静态库编译(三)

    dada哪个所有静态库编译完成后还是不能运行那个demo,提示你找不到arm**.a 你lipo后要记得吧合并成.a  名字更改成你最后编译版本生成的.a名字....... 或者吧所有库add到你的工 ...

  9. Reveal常用技巧(翻译来自Reveal官网blog)

    翻译来自官网:http://revealapp.com/blog/reveal-common-tips-cn.html 以下基于Reveal 1.6. 用于快速上手的内置应用 刚刚下载Reveal,啥 ...

  10. XCode的安装包校验伪真

    校验文件方法:shasum xxx.dmgORmd5 xxx.dmg - Xcode_7.1.dmgMD5:8962e1a843a51232b92a908b6cfb180dSHA-1:d4e9b9e8 ...