zoukankan      html  css  js  c++  java
  • Android 手势识别类 ( 二 ) GestureDetector 源码浅析

      前言: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.   
    7.    <android.gesture.GestureOverlayView   
    8.        android:id="@+id/gesture"  
    9.        android:layout_width="fill_parent"  
    10.        android:layout_height="fill_parent"  
    11.        >      
    12.    </android.gesture.GestureOverlayView>  
    13.   
    14. </LinearLayout>  

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

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

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

         示例代码下载链接地址: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.   
    10.             processEvent(event);  
    11.   
    12.             if (cancelDispatch) {  
    13.                 event.setAction(MotionEvent.ACTION_CANCEL);  
    14.             }  
    15.   
    16.             super.dispatchTouchEvent(event);  
    17.   
    18.             return true;  
    19.         }  
    20.   
    21.         return super.dispatchTouchEvent(event);  
    22.     }  
    23. ...  
    24. }  

       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.   
    32.         return false;  
    33.     }  
    34. ...  

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

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

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

       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.   
    9.         if (animated && mCurrentGesture != null) { //调用addOnGesturePerformedListener时animated为true  
    10.             mFadingAlpha = 1.0f;  
    11.             mIsFadingOut = true;  
    12.             mFadingHasStarted = false;  
    13.             /*mFadeOffset定义手势淡出屏幕的时间间隔, 
    14.              * 默认值420,可通过setFadeOffset函数设置 
    15.              */  
    16.             mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset;  
    17.   
    18.             postDelayed(mFadingOut, mFadeOffset);  
    19.         } else {  
    20.             mFadingAlpha = 1.0f;  
    21.             mIsFadingOut = false;  
    22.             mFadingHasStarted = false;  
    23.   
    24.             if (immediate) {  
    25.                 mCurrentGesture = null;  
    26.                 mPath.rewind();  
    27.                 invalidate();  
    28.             } else if (fireActionPerformed) {  
    29.                 postDelayed(mFadingOut, mFadeOffset);  
    30.             } else if (mGestureStrokeType == GESTURE_STROKE_TYPE_MULTIPLE) {  
    31.                 mFadingOut.resetMultipleStrokes = true;  
    32.                 postDelayed(mFadingOut, mFadeOffset);  
    33.             } else {  
    34.                 mCurrentGesture = null;  
    35.                 mPath.rewind();  
    36.                 invalidate();  
    37.             }  
    38.         }  
    39.     }  
    40. ...  

        通过上面的代码,我们知道,在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.   
    15.         public void run() {  
    16.             if (mIsFadingOut) { //fireActionPerformed为true且mCurrentGesture不为空是成立  
    17.                 final long now = AnimationUtils.currentAnimationTimeMillis();  
    18.                 final long duration = now - mFadingStart;  
    19.   
    20.                 //mFadeDuration默认值为150  
    21.                 if (duration > mFadeDuration) {  
    22.                     if (fireActionPerformed) {  
    23.                         //调用onGesturePerformed方法  
    24.                         fireOnGesturePerformed();  
    25.                     }  
    26.   
    27.                     mPreviousWasGesturing = false;  
    28.                     mIsFadingOut = false;  
    29.                     mFadingHasStarted = false;  
    30.                     mPath.rewind();  
    31.                     mCurrentGesture = null;  
    32.                     setPaintAlpha(255);  
    33.                 } else {  
    34.                     mFadingHasStarted = true;  
    35.                     float interpolatedTime = Math.max(0.0f,  
    36.                             Math.min(1.0f, duration / (float) mFadeDuration));  
    37.                     mFadingAlpha = 1.0f - mInterpolator.getInterpolation(interpolatedTime);  
    38.                     setPaintAlpha((int) (255 * mFadingAlpha));  
    39.                     //FADE_ANIMATION_RATE默认值为16  
    40.                     postDelayed(this, FADE_ANIMATION_RATE);  
    41.                 }  
    42.             } else if (resetMultipleStrokes) { //fireActionPerformed为false且手势为多笔画绘制时成立  
    43.                 mResetGesture = true;  
    44.             } else {  
    45.                 //调用实现监听器OnGesturePerformedListener的onGesturePerformed方法  
    46.                 fireOnGesturePerformed();  
    47.   
    48.                 mFadingHasStarted = false;  
    49.                 mPath.rewind();  
    50.                 mCurrentGesture = null;  
    51.                 mPreviousWasGesturing = false;  
    52.                 setPaintAlpha(255);  
    53.             }  
    54.   
    55.             invalidate();  
    56.         }  
    57.     }  
    58. ...  

      值得注意的是,在主线程中处理手势淡出屏幕,当我们绑定了监听器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.   
    6.         if (mCurrentGesture != null && mGestureVisible) {  
    7.             canvas.drawPath(mPath, mGesturePaint);  
    8.         }  
    9.     }  
    10. ...  

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

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

  • 相关阅读:
    C++继承基础总结
    C++如何设计只实例化一次的类?
    复试专业课知识记录(6)
    复试专业课知识记录(5)
    复试专业课知识记录(4)
    复试专业课知识记录(3)
    复试专业课知识记录(2)
    卡尔曼滤波算法学习记录
    复试专业课知识记录(1)
    PAT:1048. Find Coins (25)(哈希表法) AC
  • 原文地址:https://www.cnblogs.com/ldq2016/p/5290933.html
Copyright © 2011-2022 走看看