今天起介绍下和手势和多点触摸相关的知识。。。。。。
先上个一道菜,手势的识别。。。。。
java.lang.Object
↳ android.view.View
↳ android.view.ViewGroup
↳ android.widget.FrameLayout
↳ 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(一笔)
public final class GestureLibraries
static GestureLibrary fromFile(File path)
static GestureLibrary fromFile(String path)
static GestureLibrary fromPrivateFile(Context context, String name)
static GestureLibrary fromRawResource(Context context, int resourceId)
想从SD卡或者raw的资源中直接加载手势;
下面介绍下手势的识别功能,先上代码:
GestureIdentifyDemoActivity.xml
package com.potato;
import java.util.ArrayList;
import android.app.Activity;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.Prediction;
import android.os.Bundle;
import android.widget.Toast;
public class GestureIdentifyDemoActivity extends Activity {
// 手势库
GestureLibrary mGestureLib;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 手势画板
GestureOverlayView gestures = (GestureOverlayView) findViewById(R.id.gesture_overlay_view_test);
// 手势识别的监听器
gestures.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener() { // 注1
@Override
public void onGesturePerformed(GestureOverlayView overlay,
Gesture gesture) {
//从手势库中查询匹配的内容,匹配的结果可能包括多个相似的结果,匹配度高的结果放在最前面
ArrayList<Prediction> predictions = mGestureLib
.recognize(gesture); // 注3
if (predictions.size() > 0) {
Prediction prediction = (Prediction) predictions.get(0);
// 匹配的手势
if (prediction.score > 1.0) {
Toast.makeText(GestureIdentifyDemoActivity.this,
prediction.name, Toast.LENGTH_SHORT).show();
}
}
}
});
// 从raw中加载已经有的手势库
mGestureLib = GestureLibraries.fromRawResource(this, R.raw.gestures); // 注2
if (!mGestureLib.load()) {
finish();
}
}
}
注1:
gestures.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener()
手势识别的监听器。。。。
注2:
mGestureLib = GestureLibraries.fromRawResource(this, R.raw.gestures);
从res/raw/加载gestures手势这个文件
注3:
ArrayList<Prediction> predictions = mGestureLib.recognize(gesture);
从手势库中识别出和在GestureOverlayView所画的手势;
mian.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="输入手势:" />
<!-- 注意 android.gesture.要加,否则报错,估计是找不到包-->
<android.gesture.GestureOverlayView
android:id="@+id/gesture_overlay_view_test"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
android:gestureStrokeType="multiple" />
</LinearLayout>
这里要注意:android.gesture.GestureOverlayView这里的不能用GestureOverlayView
下面介绍如何向手势库中增加自定义的手势:
上代码:
GestureBuilderDemoActivity.java
package com.potato;
import java.io.File;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Environment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
public class GestureBuilderDemoActivity extends Activity {
private static final int STATUS_SUCCESS = 0;
private static final int STATUS_CANCELLED = 1;
private static final int STATUS_NO_STORAGE = 2;
private static final int STATUS_NOT_LOADED = 3;
private static final int REQUEST_NEW_GESTURE = 1;
// 存放手势的文件
private final File mStoreFile = new File(
Environment.getExternalStorageDirectory(), "gestures");
private GesturesAdapter mAdapter;
private static GestureLibrary mGestureLib;
private TextView mTvEmpty;
Button mBtnAddGesture;
Button mBtnIdentify;
Resources mRes;
ListView mListViewGesture;
// 宽度、高度
private int mThumbnailSize;
// 密度
private int mThumbnailInset;
// 颜色
private int mPathColor;
static GestureLibrary getStore() {
return mGestureLib;
}
/**
* 手势排序
*/
private final Comparator<NamedGesture> mSorter = new Comparator<NamedGesture>() {
public int compare(NamedGesture object1, NamedGesture object2) {
return object1.name.compareTo(object2.name);
}
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mRes = getResources();
mBtnAddGesture = (Button) findViewById(R.id.addButton);
mBtnIdentify = (Button) findViewById(R.id.reloadButton);
mPathColor = mRes.getColor(R.color.gesture_color);
mThumbnailInset = (int) mRes
.getDimension(R.dimen.gesture_thumbnail_inset);
mThumbnailSize = (int) mRes
.getDimension(R.dimen.gesture_thumbnail_size);
mListViewGesture = (ListView) findViewById(android.R.id.list);
mAdapter = new GesturesAdapter(this);
mListViewGesture.setAdapter(mAdapter);
if (mGestureLib == null) {
mGestureLib = GestureLibraries.fromFile(mStoreFile); // 注1
}
mTvEmpty = (TextView) findViewById(android.R.id.empty);
loadGestures();
if (mListViewGesture.getAdapter().isEmpty()) {
mListViewGesture.setEmptyView(mTvEmpty);
mBtnAddGesture.setEnabled(false);
} else {
mBtnAddGesture.setEnabled(true);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUEST_NEW_GESTURE:
loadGestures();
break;
}
}
}
/**
* 识别手势
*/
@SuppressWarnings({ "UnusedDeclaration" })
public void identifyGestures(View v) {
Intent intent = new Intent(this, GestureIdentifyDemoActivity.class);
startActivity(intent);
}
/**
* 创建手势
* @param v
*/
@SuppressWarnings({ "UnusedDeclaration" })
public void addGesture(View v) {
Intent intent = new Intent(this, CreateGestureActivity.class);
startActivityForResult(intent, REQUEST_NEW_GESTURE);
}
/**
* 加载手势
* @return
*/
private int loadGestures() {
if (!Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())) {
return STATUS_NO_STORAGE;
}
final GestureLibrary gestureLib = mGestureLib;
mAdapter.clear();
if (gestureLib.load()) { // 注2
for (String name : gestureLib.getGestureEntries()) {
for (Gesture gesture : gestureLib.getGestures(name)) {
final Bitmap bitmap = gesture.toBitmap(mThumbnailSize, // 注3
mThumbnailSize, mThumbnailInset, mPathColor);
final NamedGesture namedGesture = new NamedGesture();
namedGesture.gesture = gesture;
namedGesture.name = name;
mAdapter.addBitmap(namedGesture.gesture.getID(), bitmap);
mAdapter.add(namedGesture);
}
}
mAdapter.sort(mSorter); // 注4
mAdapter.notifyDataSetChanged();
return STATUS_SUCCESS;
}
return STATUS_NOT_LOADED;
}
static class NamedGesture {
String name;
Gesture gesture;
}
private class GesturesAdapter extends ArrayAdapter<NamedGesture> {
private final LayoutInflater mInflater;
private final Map<Long, Drawable> mThumbnails = Collections
.synchronizedMap(new HashMap<Long, Drawable>());
public GesturesAdapter(Context context) {
super(context, 0);
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
void addBitmap(Long id, Bitmap bitmap) {
mThumbnails.put(id, new BitmapDrawable(bitmap));
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_gesture,
parent, false);
}
final NamedGesture gesture = getItem(position);
final TextView label = (TextView) convertView;
label.setTag(gesture);
label.setText(gesture.name);
label.setCompoundDrawablesWithIntrinsicBounds(
mThumbnails.get(gesture.gesture.getID()), null, null, null);
return convertView;
}
};
}
注1
mGestureLib = GestureLibraries.fromFile(mStoreFile);
从SD卡中加载已有的手势;
注2
gestureLib.load()
从手势库加载手势
注3
final Bitmap bitmap = gesture.toBitmap(mThumbnailSize, // 注3
mThumbnailSize, mThumbnailInset, mPathColor);
把手势转换成bitmap
注4
排序
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#009966"
android:orientation="vertical" >
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1.0" />
<LinearLayout
style="@android:style/ButtonBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/addButton"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:enabled="false"
android:onClick="addGesture"
android:text="button_add" />
<Button
android:id="@+id/reloadButton"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="identifyGestures"
android:text="button_identify" />
</LinearLayout>
</LinearLayout>
创建手势
CreateGestureActivity.java
package com.potato;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.MotionEvent;
import android.gesture.GestureOverlayView;
import android.gesture.Gesture;
import android.gesture.GestureLibrary;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
public class CreateGestureActivity extends Activity {
private static final float LENGTH_THRESHOLD = 120.0f;
private Gesture mGesture;
private View mDoneButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_gesture);
mDoneButton = findViewById(R.id.done);
GestureOverlayView overlay = (GestureOverlayView) findViewById(R.id.gestures_overlay);
overlay.addOnGestureListener(new GesturesProcessor());
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mGesture != null) {
outState.putParcelable("gesture", mGesture);
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mGesture = savedInstanceState.getParcelable("gesture");
if (mGesture != null) {
final GestureOverlayView overlay = (GestureOverlayView) findViewById(R.id.gestures_overlay);
overlay.post(new Runnable() {
public void run() {
overlay.setGesture(mGesture);
}
});
mDoneButton.setEnabled(true);
}
}
/**
* 增加手势的按钮
* @param v
*/
public void addGesture(View v) {
if (mGesture != null) {
final TextView input = (TextView) findViewById(R.id.gesture_name);
final CharSequence name = input.getText();
if (name.length() == 0) {
input.setError("输入gesture的名字!");
return;
}
// 把手势增加到手势库
final GestureLibrary store = GestureBuilderDemoActivity.getStore(); // 注1
store.addGesture(name.toString(), mGesture);
store.save();
setResult(RESULT_OK);
final String path = new File(
Environment.getExternalStorageDirectory(), "gestures")
.getAbsolutePath();
Toast.makeText(this, "保存成功" + path, Toast.LENGTH_LONG).show();
} else {
setResult(RESULT_CANCELED);
}
finish();
}
/**
* 取消手势
* @param v
*/
public void cancelGesture(View v) {
setResult(RESULT_CANCELED);
finish();
}
/**
* 手势监听着
* @author Administrator
*
*/
private class GesturesProcessor implements
GestureOverlayView.OnGestureListener {
public void onGestureStarted(GestureOverlayView overlay,
MotionEvent event) {
mDoneButton.setEnabled(false);
mGesture = null;
}
public void onGesture(GestureOverlayView overlay, MotionEvent event) {
}
public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
// 获取在GestureOverlayView手势
mGesture = overlay.getGesture(); // 注2
if (mGesture.getLength() < LENGTH_THRESHOLD) {
overlay.clear(false);
}
mDoneButton.setEnabled(true);
}
public void onGestureCancelled(GestureOverlayView overlay,
MotionEvent event) {
}
}
}
注1
final GestureLibrary store = GestureBuilderDemoActivity.getStore(); // 注1
store.addGesture(name.toString(), mGesture);
store.save();
手势增加到手势库
注2
获取在GestureOverlayView手势
create_gesture.xml\
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="6dip"
android:text="prompt_gesture_name"
android:textAppearance="?android:attr/textAppearanceMedium" />
<EditText
android:id="@+id/gesture_name"
android:layout_width="0dip"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:maxLength="40"
android:singleLine="true" />
</LinearLayout>
<android.gesture.GestureOverlayView
android:id="@+id/gestures_overlay"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
android:gestureStrokeType="multiple" />
<LinearLayout
style="@android:style/ButtonBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/done"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:enabled="false"
android:onClick="addGesture"
android:text="button_done" />
<Button
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="cancelGesture"
android:text="button_discard" />
</LinearLayout>
</LinearLayout>
注意,不要忘记,写入手势时,一定要增加权限,写入sd卡
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
有问题,请留言或者发邮件
ligexiao@gmail.com
源代码下载地址:http://download.youkuaiyun.com/detail/alex0203/3868687