Android GestureOverlayView详解

转至:http://blog.csdn.net/stevenhu_223/article/details/9394491


  前言: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/alex0203/article/details/7028583


今天起介绍下和手势和多点触摸相关的知识。。。。。。

先上个一道菜,手势的识别。。。。。

  1. java.lang.Object  
  2.    ↳    android.view.View  
  3.        ↳    android.view.ViewGroup  
  4.            ↳    android.widget.FrameLayout  
  5.                ↳    android.gesture.GestureOverlayView  

介绍下GestureOverlayView,这个透明的view就是让你在上面画手势用的,可叠在其他View上面。

和这个类相关的还有三个接口,分别是

GestureOverlayView.OnGestureListener;

GestureOverlayView.OnGesturePerformedListener(作用:根据在GestureOverlayView上画的手势来识别是否匹配手势库里的手势);

GestureOverlayView.OnGesturingListener.


GestureOverlayView的xml的属性介绍:

android:gestureStrokeType 

设置手势的笔画数,它有两个值,GESTURE_STROKE_TYPE_MULTIPLE(多笔),GESTURE_STROKE_TYPE_SINGLE(一笔)


  1. public final class GestureLibraries  
  1. static GestureLibrary   fromFile(File path)   
  2. static GestureLibrary   fromFile(String path)   
  3. static GestureLibrary   fromPrivateFile(Context context, String name)   
  4. static GestureLibrary   fromRawResource(Context context, int resourceId)  

想从SD卡或者raw的资源中直接加载手势;


下面介绍下手势的识别功能,先上代码:

GestureIdentifyDemoActivity.xml

  1. package com.potato;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. import android.app.Activity;  
  6. import android.gesture.Gesture;  
  7. import android.gesture.GestureLibraries;  
  8. import android.gesture.GestureLibrary;  
  9. import android.gesture.GestureOverlayView;  
  10. import android.gesture.Prediction;  
  11. import android.os.Bundle;  
  12. import android.widget.Toast;  
  13.   
  14. public class GestureIdentifyDemoActivity extends Activity {  
  15.     // 手势库  
  16.     GestureLibrary mGestureLib;  
  17.   
  18.     /** Called when the activity is first created. */  
  19.     @Override  
  20.     public void onCreate(Bundle savedInstanceState) {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.main);  
  23.   
  24.         // 手势画板  
  25.         GestureOverlayView gestures = (GestureOverlayView) findViewById(R.id.gesture_overlay_view_test);  
  26.         // 手势识别的监听器  
  27.         gestures.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener() {    // 注1  
  28.   
  29.             @Override  
  30.             public void onGesturePerformed(GestureOverlayView overlay,  
  31.                     Gesture gesture) {  
  32.                 //从手势库中查询匹配的内容,匹配的结果可能包括多个相似的结果,匹配度高的结果放在最前面    
  33.                 ArrayList<Prediction> predictions = mGestureLib  
  34.                         .recognize(gesture);                        // 注3  
  35.                                               
  36.                 if (predictions.size() > 0) {  
  37.                     Prediction prediction = (Prediction) predictions.get(0);  
  38.                     // 匹配的手势  
  39.                     if (prediction.score > 1.0) {  
  40.                         Toast.makeText(GestureIdentifyDemoActivity.this,  
  41.                                 prediction.name, Toast.LENGTH_SHORT).show();  
  42.                     }  
  43.                 }  
  44.   
  45.             }  
  46.         });  
  47.         // 从raw中加载已经有的手势库  
  48.         mGestureLib = GestureLibraries.fromRawResource(this, R.raw.gestures);       // 注2  
  49.         if (!mGestureLib.load()) {  
  50.             finish();  
  51.         }  
  52.   
  53.     }  
  54. }  
注1:
  1. gestures.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener()  

手势识别的监听器。。。。

注2:

  1. mGestureLib = GestureLibraries.fromRawResource(this, R.raw.gestures);  

从res/raw/加载gestures手势这个文件

注3:

  1. ArrayList<Prediction> predictions = mGestureLib.recognize(gesture);  
从手势库中识别出和在GestureOverlayView所画的手势;


mian.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.     <TextView  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:text="输入手势:" />  
  11.     <!-- 注意  android.gesture.要加,否则报错,估计是找不到包-->  
  12.     <android.gesture.GestureOverlayView  
  13.         android:id="@+id/gesture_overlay_view_test"  
  14.         android:layout_width="match_parent"  
  15.         android:layout_height="0dip"  
  16.         android:layout_weight="1.0"  
  17.         android:gestureStrokeType="multiple" />  
  18.   
  19. </LinearLayout>  
这里要注意:android.gesture.GestureOverlayView这里的不能用GestureOverlayView


下面介绍如何向手势库中增加自定义的手势:



上代码:

GestureBuilderDemoActivity.java

  1. package com.potato;  
  2.   
  3. import java.io.File;  
  4. import java.util.Collections;  
  5. import java.util.Comparator;  
  6. import java.util.HashMap;  
  7. import java.util.Map;  
  8.   
  9. import android.app.Activity;  
  10. import android.content.Context;  
  11. import android.content.Intent;  
  12. import android.content.res.Resources;  
  13. import android.gesture.Gesture;  
  14. import android.gesture.GestureLibraries;  
  15. import android.gesture.GestureLibrary;  
  16. import android.graphics.Bitmap;  
  17. import android.graphics.drawable.BitmapDrawable;  
  18. import android.graphics.drawable.Drawable;  
  19. import android.os.Bundle;  
  20. import android.os.Environment;  
  21. import android.view.LayoutInflater;  
  22. import android.view.View;  
  23. import android.view.ViewGroup;  
  24. import android.widget.ArrayAdapter;  
  25. import android.widget.Button;  
  26. import android.widget.ListView;  
  27. import android.widget.TextView;  
  28.   
  29. public class GestureBuilderDemoActivity extends Activity {  
  30.   
  31.     private static final int STATUS_SUCCESS = 0;  
  32.     private static final int STATUS_CANCELLED = 1;  
  33.     private static final int STATUS_NO_STORAGE = 2;  
  34.     private static final int STATUS_NOT_LOADED = 3;  
  35.   
  36.     private static final int REQUEST_NEW_GESTURE = 1;  
  37.   
  38.     // 存放手势的文件  
  39.     private final File mStoreFile = new File(  
  40.             Environment.getExternalStorageDirectory(), "gestures");  
  41.   
  42.     private GesturesAdapter mAdapter;  
  43.     private static GestureLibrary mGestureLib;  
  44.   
  45.     private TextView mTvEmpty;  
  46.     Button mBtnAddGesture;  
  47.     Button mBtnIdentify;  
  48.   
  49.     Resources mRes;  
  50.     ListView mListViewGesture;  
  51.   
  52.     // 宽度、高度  
  53.     private int mThumbnailSize;  
  54.     // 密度  
  55.     private int mThumbnailInset;  
  56.     // 颜色  
  57.     private int mPathColor;  
  58.   
  59.     static GestureLibrary getStore() {  
  60.         return mGestureLib;  
  61.     }  
  62.   
  63.     /** 
  64.      * 手势排序 
  65.      */  
  66.     private final Comparator<NamedGesture> mSorter = new Comparator<NamedGesture>() {             
  67.         public int compare(NamedGesture object1, NamedGesture object2) {  
  68.             return object1.name.compareTo(object2.name);  
  69.         }  
  70.     };  
  71.   
  72.     /** Called when the activity is first created. */  
  73.     @Override  
  74.     public void onCreate(Bundle savedInstanceState) {  
  75.         super.onCreate(savedInstanceState);  
  76.         setContentView(R.layout.main);  
  77.   
  78.         mRes = getResources();  
  79.   
  80.         mBtnAddGesture = (Button) findViewById(R.id.addButton);  
  81.         mBtnIdentify = (Button) findViewById(R.id.reloadButton);  
  82.   
  83.         mPathColor = mRes.getColor(R.color.gesture_color);  
  84.         mThumbnailInset = (int) mRes  
  85.                 .getDimension(R.dimen.gesture_thumbnail_inset);  
  86.         mThumbnailSize = (int) mRes  
  87.                 .getDimension(R.dimen.gesture_thumbnail_size);  
  88.   
  89.         mListViewGesture = (ListView) findViewById(android.R.id.list);  
  90.         mAdapter = new GesturesAdapter(this);  
  91.         mListViewGesture.setAdapter(mAdapter);  
  92.   
  93.         if (mGestureLib == null) {  
  94.             mGestureLib = GestureLibraries.fromFile(mStoreFile);                // 注1  
  95.         }  
  96.         mTvEmpty = (TextView) findViewById(android.R.id.empty);  
  97.         loadGestures();                                                           
  98.   
  99.         if (mListViewGesture.getAdapter().isEmpty()) {  
  100.             mListViewGesture.setEmptyView(mTvEmpty);  
  101.             mBtnAddGesture.setEnabled(false);  
  102.         } else {  
  103.             mBtnAddGesture.setEnabled(true);  
  104.         }  
  105.     }  
  106.   
  107.     @Override  
  108.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  109.         super.onActivityResult(requestCode, resultCode, data);  
  110.   
  111.         if (resultCode == RESULT_OK) {  
  112.             switch (requestCode) {  
  113.             case REQUEST_NEW_GESTURE:  
  114.                 loadGestures();  
  115.                 break;  
  116.             }  
  117.         }  
  118.     }  
  119.   
  120.     /** 
  121.      * 识别手势 
  122.      */  
  123.     @SuppressWarnings({ "UnusedDeclaration" })  
  124.     public void identifyGestures(View v) {  
  125.         Intent intent = new Intent(this, GestureIdentifyDemoActivity.class);  
  126.         startActivity(intent);  
  127.     }  
  128.   
  129.     /** 
  130.      * 创建手势 
  131.      * @param v 
  132.      */  
  133.     @SuppressWarnings({ "UnusedDeclaration" })  
  134.     public void addGesture(View v) {  
  135.         Intent intent = new Intent(this, CreateGestureActivity.class);  
  136.         startActivityForResult(intent, REQUEST_NEW_GESTURE);  
  137.     }  
  138.   
  139.     /** 
  140.      * 加载手势 
  141.      * @return 
  142.      */  
  143.     private int loadGestures() {  
  144.         if (!Environment.MEDIA_MOUNTED.equals(Environment  
  145.                 .getExternalStorageState())) {  
  146.             return STATUS_NO_STORAGE;  
  147.         }  
  148.   
  149.         final GestureLibrary gestureLib = mGestureLib;  
  150.   
  151.         mAdapter.clear();  
  152.         if (gestureLib.load()) {                                        // 注2  
  153.             for (String name : gestureLib.getGestureEntries()) {  
  154.   
  155.                 for (Gesture gesture : gestureLib.getGestures(name)) {  
  156.                     final Bitmap bitmap = gesture.toBitmap(mThumbnailSize,          // 注3  
  157.                             mThumbnailSize, mThumbnailInset, mPathColor);  
  158.                     final NamedGesture namedGesture = new NamedGesture();  
  159.                     namedGesture.gesture = gesture;  
  160.                     namedGesture.name = name;  
  161.   
  162.                     mAdapter.addBitmap(namedGesture.gesture.getID(), bitmap);  
  163.                     mAdapter.add(namedGesture);  
  164.                 }  
  165.             }  
  166.             mAdapter.sort(mSorter);                         // 注4  
  167.             mAdapter.notifyDataSetChanged();  
  168.             return STATUS_SUCCESS;  
  169.         }  
  170.   
  171.         return STATUS_NOT_LOADED;  
  172.     }  
  173.   
  174.     static class NamedGesture {  
  175.         String name;  
  176.         Gesture gesture;  
  177.     }  
  178.   
  179.     private class GesturesAdapter extends ArrayAdapter<NamedGesture> {  
  180.         private final LayoutInflater mInflater;  
  181.         private final Map<Long, Drawable> mThumbnails = Collections  
  182.                 .synchronizedMap(new HashMap<Long, Drawable>());  
  183.   
  184.         public GesturesAdapter(Context context) {  
  185.             super(context, 0);  
  186.             mInflater = (LayoutInflater) context  
  187.                     .getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  188.         }  
  189.   
  190.         void addBitmap(Long id, Bitmap bitmap) {  
  191.             mThumbnails.put(id, new BitmapDrawable(bitmap));  
  192.         }  
  193.   
  194.         @Override  
  195.         public View getView(int position, View convertView, ViewGroup parent) {  
  196.             if (convertView == null) {  
  197.                 convertView = mInflater.inflate(R.layout.list_item_gesture,  
  198.                         parent, false);  
  199.             }  
  200.   
  201.             final NamedGesture gesture = getItem(position);  
  202.             final TextView label = (TextView) convertView;  
  203.   
  204.             label.setTag(gesture);  
  205.             label.setText(gesture.name);  
  206.             label.setCompoundDrawablesWithIntrinsicBounds(  
  207.                     mThumbnails.get(gesture.gesture.getID()), nullnullnull);  
  208.   
  209.             return convertView;  
  210.         }  
  211.     };  
  212. }  

注1
  1. mGestureLib = GestureLibraries.fromFile(mStoreFile);  
从SD卡中加载已有的手势;


注2

  1. gestureLib.load()  
从手势库加载手势


注3

  1. final Bitmap bitmap = gesture.toBitmap(mThumbnailSize,          // 注3  
  2.                             mThumbnailSize, mThumbnailInset, mPathColor);  
把手势转换成bitmap


注4

排序


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:background="#009966"  
  6.     android:orientation="vertical" >  
  7.   
  8.     <ListView  
  9.         android:id="@android:id/list"  
  10.         android:layout_width="fill_parent"  
  11.         android:layout_height="0dip"  
  12.         android:layout_weight="1.0" />  
  13.   
  14.     <LinearLayout  
  15.         style="@android:style/ButtonBar"  
  16.         android:layout_width="match_parent"  
  17.         android:layout_height="wrap_content"  
  18.         android:orientation="horizontal">  
  19.   
  20.         <Button  
  21.             android:id="@+id/addButton"  
  22.             android:layout_width="0dip"  
  23.             android:layout_height="wrap_content"  
  24.             android:layout_weight="1"  
  25.             android:enabled="false"  
  26.             android:onClick="addGesture"  
  27.             android:text="button_add" />  
  28.   
  29.         <Button  
  30.             android:id="@+id/reloadButton"  
  31.             android:layout_width="0dip"  
  32.             android:layout_height="wrap_content"  
  33.             android:layout_weight="1"  
  34.             android:onClick="identifyGestures"  
  35.             android:text="button_identify" />  
  36.   
  37.     </LinearLayout>  
  38.   
  39. </LinearLayout>  

创建手势

CreateGestureActivity.java

  1. package com.potato;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.os.Environment;  
  6. import android.view.View;  
  7. import android.view.MotionEvent;  
  8. import android.gesture.GestureOverlayView;  
  9. import android.gesture.Gesture;  
  10. import android.gesture.GestureLibrary;  
  11. import android.widget.TextView;  
  12. import android.widget.Toast;  
  13.   
  14. import java.io.File;  
  15.   
  16. public class CreateGestureActivity extends Activity {  
  17.     private static final float LENGTH_THRESHOLD = 120.0f;  
  18.   
  19.     private Gesture mGesture;  
  20.     private View mDoneButton;  
  21.   
  22.     @Override  
  23.     protected void onCreate(Bundle savedInstanceState) {  
  24.         super.onCreate(savedInstanceState);  
  25.   
  26.         setContentView(R.layout.create_gesture);  
  27.   
  28.         mDoneButton = findViewById(R.id.done);  
  29.   
  30.         GestureOverlayView overlay = (GestureOverlayView) findViewById(R.id.gestures_overlay);  
  31.         overlay.addOnGestureListener(new GesturesProcessor());  
  32.     }  
  33.   
  34.     @Override  
  35.     protected void onSaveInstanceState(Bundle outState) {  
  36.         super.onSaveInstanceState(outState);  
  37.   
  38.         if (mGesture != null) {  
  39.             outState.putParcelable("gesture", mGesture);  
  40.         }  
  41.     }  
  42.   
  43.     @Override  
  44.     protected void onRestoreInstanceState(Bundle savedInstanceState) {  
  45.         super.onRestoreInstanceState(savedInstanceState);  
  46.   
  47.         mGesture = savedInstanceState.getParcelable("gesture");  
  48.         if (mGesture != null) {  
  49.             final GestureOverlayView overlay = (GestureOverlayView) findViewById(R.id.gestures_overlay);  
  50.             overlay.post(new Runnable() {  
  51.                 public void run() {  
  52.                     overlay.setGesture(mGesture);  
  53.                 }  
  54.             });  
  55.   
  56.             mDoneButton.setEnabled(true);  
  57.         }  
  58.     }  
  59.   
  60.     /** 
  61.      * 增加手势的按钮 
  62.      * @param v 
  63.      */  
  64.     public void addGesture(View v) {  
  65.         if (mGesture != null) {  
  66.             final TextView input = (TextView) findViewById(R.id.gesture_name);  
  67.             final CharSequence name = input.getText();  
  68.             if (name.length() == 0) {  
  69.                 input.setError("输入gesture的名字!");  
  70.                 return;  
  71.             }  
  72.               
  73.             // 把手势增加到手势库  
  74.             final GestureLibrary store = GestureBuilderDemoActivity.getStore();         // 注1  
  75.             store.addGesture(name.toString(), mGesture);  
  76.             store.save();  
  77.   
  78.             setResult(RESULT_OK);  
  79.   
  80.             final String path = new File(  
  81.                     Environment.getExternalStorageDirectory(), "gestures")  
  82.                     .getAbsolutePath();  
  83.             Toast.makeText(this"保存成功" + path, Toast.LENGTH_LONG).show();  
  84.         } else {  
  85.             setResult(RESULT_CANCELED);  
  86.         }  
  87.   
  88.         finish();  
  89.   
  90.     }  
  91.   
  92.     /** 
  93.      * 取消手势 
  94.      * @param v 
  95.      */  
  96.     public void cancelGesture(View v) {  
  97.         setResult(RESULT_CANCELED);  
  98.         finish();  
  99.     }  
  100.   
  101.     /** 
  102.      * 手势监听着 
  103.      * @author Administrator 
  104.      * 
  105.      */  
  106.     private class GesturesProcessor implements  
  107.             GestureOverlayView.OnGestureListener {  
  108.         public void onGestureStarted(GestureOverlayView overlay,  
  109.                 MotionEvent event) {  
  110.             mDoneButton.setEnabled(false);  
  111.             mGesture = null;  
  112.         }  
  113.   
  114.         public void onGesture(GestureOverlayView overlay, MotionEvent event) {  
  115.         }  
  116.   
  117.         public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {  
  118.             // 获取在GestureOverlayView手势  
  119.             mGesture = overlay.getGesture();                                        // 注2  
  120.             if (mGesture.getLength() < LENGTH_THRESHOLD) {  
  121.                 overlay.clear(false);  
  122.             }  
  123.             mDoneButton.setEnabled(true);  
  124.         }  
  125.   
  126.         public void onGestureCancelled(GestureOverlayView overlay,  
  127.                 MotionEvent event) {  
  128.         }  
  129.     }  
  130. }  

注1
  1. final GestureLibrary store = GestureBuilderDemoActivity.getStore();         // 注1  
  2.             store.addGesture(name.toString(), mGesture);  
  3.             store.save();  
手势增加到手势库


注2

获取在GestureOverlayView手势

create_gesture.xml\

  1. <?xml version="1.0" encoding="utf-8"?>  
  2.   
  3. <LinearLayout  
  4.     xmlns:android="http://schemas.android.com/apk/res/android"  
  5.   
  6.     android:layout_width="match_parent"  
  7.     android:layout_height="match_parent"  
  8.       
  9.     android:orientation="vertical">  
  10.   
  11.     <LinearLayout  
  12.         android:layout_width="match_parent"  
  13.         android:layout_height="wrap_content"  
  14.         android:orientation="horizontal">  
  15.       
  16.         <TextView  
  17.             android:layout_width="wrap_content"  
  18.             android:layout_height="wrap_content"  
  19.             android:layout_marginRight="6dip"  
  20.             android:text="prompt_gesture_name"  
  21.             android:textAppearance="?android:attr/textAppearanceMedium" />  
  22.           
  23.         <EditText  
  24.             android:id="@+id/gesture_name"  
  25.             android:layout_width="0dip"  
  26.             android:layout_weight="1.0"  
  27.             android:layout_height="wrap_content"  
  28.             android:maxLength="40"  
  29.             android:singleLine="true" />  
  30.   
  31.     </LinearLayout>  
  32.       
  33.     <android.gesture.GestureOverlayView  
  34.         android:id="@+id/gestures_overlay"  
  35.         android:layout_width="match_parent"  
  36.         android:layout_height="0dip"  
  37.         android:layout_weight="1.0"  
  38.         android:gestureStrokeType="multiple" />  
  39.   
  40.     <LinearLayout  
  41.         style="@android:style/ButtonBar"  
  42.         android:layout_width="match_parent"  
  43.         android:layout_height="wrap_content"  
  44.         android:orientation="horizontal">  
  45.   
  46.         <Button  
  47.             android:id="@+id/done"  
  48.             android:layout_width="0dip"  
  49.             android:layout_height="wrap_content"  
  50.             android:layout_weight="1"  
  51.             android:enabled="false"  
  52.             android:onClick="addGesture"  
  53.             android:text="button_done" />  
  54.       
  55.         <Button  
  56.             android:layout_width="0dip"  
  57.             android:layout_height="wrap_content"  
  58.             android:layout_weight="1"  
  59.             android:onClick="cancelGesture"  
  60.             android:text="button_discard" />  
  61.       
  62.     </LinearLayout>  
  63.       
  64. </LinearLayout>  


注意,不要忘记,写入手势时,一定要增加权限,写入sd卡

  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  


有问题,请留言或者发邮件

ligexiao@gmail.com


源代码下载地址:http://download.csdn.net/detail/alex0203/3868687


  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用Android Studio的手势识别的示例代码: ```java import android.gesture.Gesture; import android.gesture.GestureLibraries; import android.gesture.GestureLibrary; import android.gesture.GestureOverlayView; import android.gesture.GestureStroke; import android.gesture.GestureUtils; import android.gesture.Prediction; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.Toast; import java.util.ArrayList; public class MainActivity extends AppCompatActivity implements GestureOverlayView.OnGesturePerformedListener { private GestureLibrary gestureLibrary; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); GestureOverlayView gestureOverlayView = findViewById(R.id.gestureOverlayView); gestureOverlayView.addOnGesturePerformedListener(this); gestureLibrary = GestureLibraries.fromRawResource(this, R.raw.gestures); if (!gestureLibrary.load()) { Toast.makeText(this, "手势库加载失败", Toast.LENGTH_SHORT).show(); } } @Override public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) { ArrayList<Prediction> predictions = gestureLibrary.recognize(gesture); if (predictions.size() > 0) { Prediction prediction = predictions.get(0); if (prediction.score > 2.0) { Toast.makeText(this, "识别到手势:" + prediction.name, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "无法识别手势", Toast.LENGTH_SHORT).show(); } } } } ``` 这段代码演示了如何在Android Studio中使用手势识别。首先,我们需要在`onCreate`方法中加载手势库文件。然后,我们将`GestureOverlayView`添加到布局中,并设置`OnGesturePerformedListener`监听器。当手势被执行时,`onGesturePerformed`方法将被调用。在这个方法中,我们使用`gestureLibrary.recognize`方法来识别手势,并根据识别结果进行相应的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值