全面掌握Android中的手勢Gesture

                                          全面掌握Android中的手勢Gesture

首先關於手勢我們常用的應該有幾種呢?

       向上滑、向下滑,向左滑,向後滑,向左上滑,向左下滑,向右上滑,向右下滑,單擊,雙擊,長按,雙擊,等自定義手勢。

     

我們知道View類有個View.OnTouchListener內部接口,通過重寫他的onTouch(View v, MotionEvent event)方法,我們可以處理一些touch事件,但是這個方法太過簡單,如果需要處理一些複雜的手勢,用這個接口就會很麻煩(因爲我們要自己根據用戶觸摸的軌跡去判斷是什麼手勢)。

Android sdk給我們提供了GestureDetectorGesture:手勢Detector:識別)類,通過這個類我們可以識別很多的手勢,主要是通過他的onTouchEvent(event)方法完成了不同手勢的識別。雖然他能識別手勢,但是不同的手勢要怎麼處理,應該是提供給程序員實現的。

GestureDetector這個類對外提供了兩個接口:OnGestureListenerOnDoubleTapListener,還有一個內部類SimpleOnGestureListener

GestureDetector.OnDoubleTapListener接口:用來通知DoubleTap事件,類似於鼠標的雙擊事件。

一、手勢交互的基本原理.

         1. 在接觸屏幕瞬間,觸發一個MotionEvent事件。
         2. 該事件被OnTouchListener監聽,在其onTouch()方法裏獲得該MotionEvent對象。
         3. 通過GestureDetector(手勢識別器)轉發MotionEvent對象至OnGestureListener。
         4. OnGestureListener獲得該對象,聽根據該對象封裝的的信息,做出合適的反饋。

二、手勢識別的核心對象 MotionEvent、GestureDetector 和 OnGestureListener。

           ①MotionEvent: 這個類用於封裝手勢、觸摸筆、軌跡球等等的動作事件。其內部封裝了兩個重要的屬性X和Y,這兩個屬性分別用於記錄橫軸和縱軸的座標。
           ②GestureDetector: 識別各種手勢。手勢識別器
           ③OnGestureListener: 這是一個手勢交互的監聽接口,其中提供了多個抽象方法,並根據GestureDetector的手勢識別結果調用相對應的方法。

package org.crazyit.io;

import android.app.Activity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.widget.Toast;
/**
 * 
 * @author qiyue
 *
 */
public class GestureTest extends Activity
{
	// 定義手勢檢測器實例
	GestureDetector detector;
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		//創建手勢檢測器
		detector = new GestureDetector(this,new myGestureListener());
	}
	
	class myGestureListener implements OnGestureListener{

		@Override
		public boolean onDown(MotionEvent e) {
			Toast.makeText(GestureTest.this,"onDown", Toast.LENGTH_LONG).show();
			return false;
		}

		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
				float velocityY) {
			Toast.makeText(GestureTest.this,"onDown", Toast.LENGTH_LONG).show();
			return false;
		}

		@Override
		public void onLongPress(MotionEvent e) {
			Toast.makeText(GestureTest.this,"onDown", Toast.LENGTH_LONG).show();
		}

		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2,
				float distanceX, float distanceY) {
			Toast.makeText(GestureTest.this,"onDown", Toast.LENGTH_LONG).show();
			return false;
		}

		@Override
		public void onShowPress(MotionEvent e) {
			Toast.makeText(GestureTest.this,"onDown", Toast.LENGTH_LONG).show();
			
		}

		@Override
		public boolean onSingleTapUp(MotionEvent e) {
			Toast.makeText(GestureTest.this,"onDown", Toast.LENGTH_LONG).show();
			return false;
		}
		
	}
	
	//將該Activity上的觸碰事件交給GestureDetector處理
	@Override
	public boolean onTouchEvent(MotionEvent me)
	{
		return detector.onTouchEvent(me);
	}
	
}


  • 按下(onDown): 剛剛手指接觸到觸摸屏的那一剎那,就是觸的那一下。

  • 拋擲(onFling): 手指在觸摸屏上迅速移動,並鬆開的動作。

  • 長按(onLongPress): 手指按在持續一段時間,並且沒有鬆開。

  • 滾動(onScroll): 手指在觸摸屏上滑動。

  • 按住(onShowPress): 手指按在觸摸屏上,它的時間範圍在按下起效,在長按之前。

  • 擡起(onSingleTapUp):手指離開觸摸屏的那一剎那。

經驗總結:

  • 任何手勢動作都會先執行一次按下(onDown)動作。

  • 長按(onLongPress)動作前一定會執行一次按住(onShowPress)動作。

  • 按住(onShowPress)動作和按下(onDown)動作之後都會執行一次擡起(onSingleTapUp)動作。

  • 長按(onLongPress)、滾動(onScroll)和拋擲(onFling)動作之後都不會執行擡起(onSingleTapUp)動作。

最後,雙擊和三擊的識別過程,在第一次點擊down時,給Handler發送一個演示300ms的消息,如果300ms裏,發生了第二次單擊的down事件,那麼,就認爲是雙擊事件了,並移除之前發送的延時消息。如果300ms後仍沒有第二次的down消息,那麼久判定爲SingleTapConfirmed事件,三擊和此類似多了一次發送消息的過程

例子1:

 實現圖片的翻頁效果


import org.sim.io.R;

import android.app.Activity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.ViewFlipper;


public class GestureFlip extends Activity 
	implements OnGestureListener
{
	// ViewFlipper實例
	ViewFlipper flipper;
	// 定義手勢檢測器實例
	GestureDetector detector;
	// 定義一個動畫數組,用於爲ViewFlipper指定切換動畫效果
	Animation[] animations = new Animation[4];
	// 定義手勢動作兩點之間的最小距離
	final int FLIP_DISTANCE = 50;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// 創建手勢檢測器
		detector = new GestureDetector(this, this);
		// 獲得ViewFlipper實例
		flipper = (ViewFlipper) this.findViewById(R.id.flipper);
		// 爲ViewFlipper添加5個ImageView組件
		flipper.addView(addImageView(R.drawable.java));
		flipper.addView(addImageView(R.drawable.ee));
		flipper.addView(addImageView(R.drawable.ajax));
		flipper.addView(addImageView(R.drawable.xml));
		flipper.addView(addImageView(R.drawable.classic));
		// 初始化Animation數組
		animations[0] = AnimationUtils.loadAnimation(
			this, R.anim.left_in);
		animations[1] = AnimationUtils.loadAnimation(
			this, R.anim.left_out);
		animations[2] = AnimationUtils.loadAnimation(
			this, R.anim.right_in);
		animations[3] = AnimationUtils.loadAnimation(
			this, R.anim.right_out);
	}

	// 定義添加ImageView的工具方法
	private View addImageView(int resId)
	{
		ImageView imageView = new ImageView(this);
		imageView.setImageResource(resId);
		imageView.setScaleType(ImageView.ScaleType.CENTER);
		return imageView;
	}

	@Override
	public boolean onFling(MotionEvent event1, MotionEvent event2,
		float velocityX, float velocityY)
	{
		// 如果第一個觸點事件的X座標大於第二個觸點事件的X座標超過FLIP_DISTANCE
		// 也就是手勢從右向左滑。
		if (event1.getX() - event2.getX() > FLIP_DISTANCE)
		{
			// 爲flipper設置切換的的動畫效果
			flipper.setInAnimation(animations[0]);
			flipper.setOutAnimation(animations[1]);
			flipper.showPrevious();
			return true;
		}
		// 如果第二個觸點事件的X座標大於第一個觸點事件的X座標超過FLIP_DISTANCE
		// 也就是手勢從右向左滑。
		else if (event2.getX() - event1.getX() > FLIP_DISTANCE)
		{
			// 爲flipper設置切換的的動畫效果
			flipper.setInAnimation(animations[2]);
			flipper.setOutAnimation(animations[3]);
			flipper.showNext();
			return true;
		}
		return false;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		// 將該Activity上的觸碰事件交給GestureDetector處理
		return detector.onTouchEvent(event);
	}

	@Override
	public boolean onDown(MotionEvent arg0)
	{
		return false;
	}

	@Override
	public void onLongPress(MotionEvent event)
	{
	}

	@Override
	public boolean onScroll(MotionEvent event1
		, MotionEvent event2, float arg2, float arg3)
	{
		return false;
	}

	@Override
	public void onShowPress(MotionEvent event)
	{
	}

	@Override
	public boolean onSingleTapUp(MotionEvent event)
	{
		return false;
	}
}

<!-- 定義ViewFlipper組件 -->
<ViewFlipper android:id="@+id/flipper"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    />
</LinearLayout>  
四、添加手勢
       android除了提供了手勢檢測之外,還允許應用程序吧用戶手勢添加到指定文件中,以備以後使用-------如果程序需要,當用戶下次再次畫出該手勢時,系統將可識別該手勢。
    android使用GestureLibrary來代表手勢庫,並提供了GestureLibrary工具類
             下面是4個靜態方法,
              static GestureLibary from File(String path): 從path代表的文件中加載手勢庫。
              static GestureLibrary fromFile(File path):從path代表的文件中加載手勢庫。
              static GestureLibrary fromPrivateFile(Context context,int resourceId);
             從resourceId所代表的資源中加載手勢庫
      一旦在程序中或得了GestureLibary對象之後,該對象提供瞭如下方法來添加手勢,識別手勢
         void addGesture(String entryName,Gesture gesture): 添加一個名爲entryName的手勢
        Set<String>getGestureEntries():獲取該手勢庫中的所有手勢的名稱
        ArrayList<Gesture>getGestures(String entryName):獲取entryName名稱對應的全部手勢
        void removeEntry(String entryName):刪除手勢庫中entryName對應的手勢。
        void removeEntry(String engtryName):刪除手勢庫中entryName對應的手勢。
        void removeGesture(String entryName,Gesture gesture):刪除手勢庫總entryName、gesture對應的手勢
        boolean save() :當向手勢中添加手勢或從中刪除手勢後調用該方法保存手勢庫。
    這裏Android還提供了一個GestureOverlayView,該組件就像一個“繪圖組件”,只是用戶在組件上繪製的不是圖形,而是手勢,爲GestureOverlayView,提供了OnGestureListenterOnGesturePerformedListenerOnGesturingdListener三個監聽器接口,這些是分別響應手勢的開始、結束、完成、取消等事件。其中OnGesturePerformedListener較爲常用
 下面看一個例子
package org.sim.io;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;

/**
 * 
 * @author qiyue
 *
 */
public class AddGesture extends Activity
{
	EditText editText;
	GestureOverlayView gestureView;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// 獲取文本編輯框
		editText = (EditText) findViewById(R.id.gesture_name);
		// 獲取手勢編輯視圖
		gestureView = (GestureOverlayView) 
			findViewById(R.id.gesture);
		// 設置手勢的繪製顏色
		gestureView.setGestureColor(Color.RED);
		// 設置手勢的繪製寬度
		gestureView.setGestureStrokeWidth(4);
		// 爲gesture的手勢完成事件綁定事件監聽器
		gestureView.addOnGesturePerformedListener(
			new OnGesturePerformedListener()
			{
				@Override
				public void onGesturePerformed(GestureOverlayView overlay,
					final Gesture gesture)
				{
					// 加載save.xml界面佈局代表的視圖
					View saveDialog = getLayoutInflater().inflate(
						R.layout.save, null);
					// 獲取saveDialog裏的show組件
					ImageView imageView = (ImageView) saveDialog
						.findViewById(R.id.show);
					// 獲取saveDialog裏的gesture_name組件
					final EditText gestureName = (EditText) saveDialog
						.findViewById(R.id.gesture_name);
					// 根據Gesture包含的手勢創建一個位圖
					Bitmap bitmap = gesture.toBitmap(128, 
						128, 10, 0xffff0000);
					imageView.setImageBitmap(bitmap);
					// 使用對話框顯示saveDialog組件
					new AlertDialog.Builder(AddGesture.this)
					.setView(saveDialog)
					.setPositiveButton("保存", new OnClickListener()
					{
						@Override
						public void onClick(DialogInterface dialog,
							int which)
						{
							// 獲取指定文件對應的手勢庫
							GestureLibrary gestureLib = GestureLibraries
								.fromFile("/mnt/sdcard/mygestures");
							// 添加手勢
							gestureLib.addGesture(gestureName.getText()
								.toString(), gesture);
							// 保存手勢庫
							gestureLib.save();
						}
					}).setNegativeButton("取消", null).show();
				}
			});
	}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:gravity="center_horizontal"
	>
<TextView
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:text="請在下面屏幕上繪製手勢"/>
<!-- 使用手勢繪製組件 -->
<android.gesture.GestureOverlayView
	android:id="@+id/gesture"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:gestureStrokeType="multiple" />
</LinearLayout>
save.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">
<LinearLayout
	android:orientation="horizontal"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">
<TextView
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_marginRight="8dip"
	android:text="@string/gesture_name"
	 />
<!-- 定義一個文本框來讓用戶輸入手勢名 -->	 
<EditText
	android:id="@+id/gesture_name"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"/>	
</LinearLayout>
<!-- 定義一個圖片框來顯示手勢 -->	 
<ImageView
	android:id="@+id/show"
	android:layout_width="128dp"
	android:layout_height="128dp"
	android:layout_marginTop="10dp" />
</LinearLayout>



識別的代碼
package org.crazyit.io;

import java.util.ArrayList;

import android.app.Activity;
import android.app.AlertDialog;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.Prediction;
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.Toast;

/**
 * 
 * @author qiyue
 *
 */
public class RecogniseGesture extends Activity
{
	// 定義手勢編輯組件
	GestureOverlayView gestureView;
	// 記錄手機上已有的手勢庫
	GestureLibrary gestureLibrary;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// 讀取上一個程序所創建的手勢庫
		gestureLibrary = GestureLibraries
			.fromFile("/mnt/sdcard/mygestures");
		if (gestureLibrary.load())
		{
			Toast.makeText(RecogniseGesture.this, "手勢文件裝載成功!",
				Toast.LENGTH_LONG).show();
		}
		else
		{
			Toast.makeText(RecogniseGesture.this, "手勢文件裝載失敗!",
				Toast.LENGTH_LONG).show();
		}
		// 獲取手勢編輯組件
		gestureView = (GestureOverlayView) findViewById(R.id.gesture);
		// 爲手勢編輯組件綁定事件監聽器
		gestureView.addOnGesturePerformedListener(
			new OnGesturePerformedListener()
			{
				@Override
				public void onGesturePerformed(GestureOverlayView
					overlay, Gesture gesture)
				{
					// 識別用戶剛剛所繪製的手勢
					ArrayList<Prediction> predictions = gestureLibrary
						.recognize(gesture);
					ArrayList<String> result = new ArrayList<String>();
					// 遍歷所有找到的Prediction對象
					for (Prediction pred : predictions)
					{
						// 只有相似度大於2.0的手勢纔會被輸出
						if (pred.score > 2.0)
						{
							result.add("與手勢【" + pred.name + "】相似度爲"
								+ pred.score);
						}
					}
					if (result.size() > 0)
					{
						ArrayAdapter<Object> adapter = new 
							ArrayAdapter<Object>(RecogniseGesture.this,
							android.R.layout.simple_dropdown_item_1line
							, result.toArray());
						// 使用一個帶List的對話框來顯示所有匹配的手勢
						new AlertDialog.Builder(RecogniseGesture.this)
							.setAdapter(adapter, null)
							.setPositiveButton("確定", null).show();
					}
					else
					{
						Toast.makeText(RecogniseGesture.this
							, "無法找到能匹配的手勢!",
							Toast.LENGTH_LONG).show();
					}
				}
			});
	}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">
<!-- 使用手勢編輯組件 -->	
<android.gesture.GestureOverlayView
	android:id="@+id/gesture"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:gestureStrokeType="multiple" />
</LinearLayout>

result.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">
<ListView 
	android:id="@+id/show"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	/>	
</LinearLayout>





發佈了34 篇原創文章 · 獲贊 6 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章