在Android系统中,每一次手势交互都会依照以下顺序执行。
1. 接触接触屏一刹那,触发一个MotionEvent事件。
2. 该事件被OnTouchListener监听,在其onTouch()方法里获得该MotionEvent对象。
3. 通过GestureDetector(手势识别器)转发次MotionEvent对象至OnGestureListener。
4. OnGestureListener获得该对象,听根据该对象封装的的信息,做出合适的反馈。
在Android中,是由GestureDetector类来负责手势的检测,每一个GestureDetector类的实例都代表一个手势监听器。在创建手势监听器时需要一个类OnGestureListener例。在OnGestureListerner接口里面,有以下事件处理的方法可供调用:
boolean onDown(MotionEvent e):当用户在屏幕上按下时会触发该方法,但在移动或抬起手指时不会触发。
void onShowPress(MotionEvent e):当用户在屏幕上按下,并且既没有移动有没有抬起手指时,会触发该方法。一般通过该方法告知用户他们的动作已经被识别到了,你可以 高亮某个元素来提醒他们。
boolean onSingleTapUp(MotionEvent e);:当用户在屏幕上轻击时(通常是指点击屏幕的时间很短)会触发该方法。
boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);:当用户在屏幕上发起滚动的手势时会触发该方法。参数中的e1指第一个按下开 始滚动的动作事件,e2指触发当前这个方法的移动动作的事件,distanceX和distanceY则分别触发onScroll方法期间的X、Y轴上的滚动距离,而不是指e1和e2两个事件发生直接的距离。
void onLongPress(MotionEvent e);:当用户在屏幕上持续地长按时会触发该方法。
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);:当用户在屏幕上持续地按下并且有“抛”的动作时,会触发该方法。对于该事件的理解,你可以体会一下按住一个图标然后把它扔到某个地方的感觉。参数中的e1指第一个按下的动作事件,e2指触发当前这个方法的“猛扔”动作的事件,velocityX和velocityY则分别触发onFling方法期间X、Y轴上的移动速度。
除了以上还有OnDoubleTapListener这个接口,它是双击事件的一个监听器,它包含了下面这些方法。
boolean onSingleTapConfirmed(MotionEvent e):当用户在屏幕上单击是会触发此方法,与上面的onSingleTapUp方法不同的地方在于,该方法只会在监听器确定了用户在第一次单击后不会触发双击事件时才会被触发。
boolean onDoubleTap(MotionEvent e):当用户在屏幕上双击时会触发此方法。这里的按下动作事件指的时双击中的第一次触击。
boolean onDoubleTapEvent(MotionEvent e):在双击事件发生时会触发此方法,包括了按下、移动和抬起事件。
下面的Demo实现图片的切换:
public class Two_activity extends AppCompatActivity implements View.OnTouchListener, GestureDetector.OnGestureListener {
private GestureDetectorCompat detector;
int[] girls = new int[]{R.mipmap.pic3, R.mipmap.pic6, R.mipmap.pic7, R.mipmap.pic8, R.mipmap.timg, R.mipmap.timg2};
private int index = 0;
private ImageView image;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two_activity);
detector = new GestureDetectorCompat(this,this);
image = (ImageView) findViewById(R.id.image);
image.setImageResource(girls[index]);
image.setOnTouchListener(this);
image.setLongClickable(true);
detector.setIsLongpressEnabled(true);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
detector.onTouchEvent(event);
return true;
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
Toast.makeText(this, "啊呀", Toast.LENGTH_SHORT).show();
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (velocityX < 0) {
goNext();
} else if (velocityX > 0) {
goPrevious();
}
return true;
}
public void goNext() {
index++;
index = Math.abs(index % girls.length);
image.setImageResource(girls[index]);
}
public void goPrevious() {
index--;
index = Math.abs(index % girls.length);
image.setImageResource(girls[index]);
}
}
使用OnGestureListener接口,这样需要重载OnGestureListener接口所有的方法,适合监听所有的手势,正如官方文档提到的“Detecing All Supported Gestures”,这样会造成有些手势动作我们用不到,但是还要重载。SimpleOnGestureListener类的出现为我们解决了这个问题。
public class MainActivity extends Activity {
private GestureDetectorCompat mDetector;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDetector = new GestureDetectorCompat(this, new MyGestureListener());
onTouchEvent中使用了MotionEventCompat,而不直接使用MotionEvent是因为MotionEventCompat使更多的Action适配到API 4。
在View中使用手势:
View myView = findViewById(R.id.my_view);
myView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// ... Respond to touch events
this.mDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
});
手势添加:
Android中使用GestureLibrary来代表手势库,提供了GestureLibraries工具类来创建手势库。
四个加载手势库的静态方法:
获得GestureLibraries对象后,就可以使用该对象提供的下述方法来做相应操作了:
相关方法:
5.手势添加示例:
public void addGesture (String entryName, Gesture gesture):添加一个名为entryName的手势
public Set<String> getGestureEntries ():获得手势库中所有手势的名称
public ArrayList<Gesture> getGestures (String entryName):获得entryName名称对应的全部手势
public ArrayList<Prediction> recognize (Gesture gesture): 从当前手势库中识别与gesture匹配的全部手势
public void removeEntry (String entryName):删除手势库中entryName名称对应的手势
public void removeGesture (String entryName, Gesture gesture):删除手势库中entryName和gesture都匹配的手势
public abstract boolean save ():想手势库中添加手势或从中删除手势后调用该方法保存手势库
GestureOverlayView手势编辑组件:
Android为GestureOverlayView提供了三种监听器接口,如下,一般常用的是:OnGesturePerformedListener;用于手势完成时提供响应!
运行效果图:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请在下方屏幕中绘制手势~"
android:textSize="20sp"/>
<!-- gestureStrokeType控制手势是否需要一笔完成,multiple表示允许多笔-->
<android.gesture.GestureOverlayView
android:id="@+id/gesture"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gestureStrokeType="multiple" />
</LinearLayout>
dialog_save.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="8dp"
android:text="请填写手势名称:"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/edit_name"/>
</LinearLayout>
<ImageView
android:id="@+id/img_show"
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_marginTop="10dp"/>
</LinearLayout>
public class MainActivity extends AppCompatActivity {
private EditText editText;
private GestureOverlayView gesture;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取手势编辑组件后,设置相关参数
gesture = (GestureOverlayView) findViewById(R.id.gesture);
gesture.setGestureColor(Color.GREEN);
gesture.setGestureStrokeWidth(5);
gesture.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener() {
@Override
public void onGesturePerformed(GestureOverlayView gestureOverlayView, final Gesture gesture) {
View saveDialog = getLayoutInflater().inflate(R.layout.dialog_save,null,false);
ImageView img_show = (ImageView) saveDialog.findViewById(R.id.img_show);
final EditText edit_name = (EditText) saveDialog.findViewById(R.id.edit_name);
Bitmap bitmap = gesture.toBitmap(128,128,10,0xffff0000);
img_show.setImageBitmap(bitmap);
new AlertDialog.Builder(MainActivity.this).setView(saveDialog)
.setPositiveButton("保存",new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//获取文件对应的手势库
GestureLibrary gestureLib = GestureLibraries.fromFile("/mnt/sdcard/mygestures");
gestureLib.addGesture(edit_name.getText().toString(),gesture);
gestureLib.save();
}
}).setNegativeButton("取消", null).show();
}
});
}
}
识别手势:
public class MainActivity extends AppCompatActivity {
private GestureOverlayView gesture;
private GestureLibrary gestureLibrary;
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = MainActivity.this;
gestureLibrary = GestureLibraries.fromFile("mmt/sdcard/mygestures");
if (gestureLibrary.load()) {
Toast.makeText(mContext, "手势库加载成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, "手势库加载失败", Toast.LENGTH_SHORT).show();
}
//获取手势编辑组件后,设置相关参数
gesture = (GestureOverlayView) findViewById(R.id.gesture);
gesture.setGestureColor(Color.GREEN);
gesture.setGestureStrokeWidth(5);
gesture.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener() {
@Override
public void onGesturePerformed(GestureOverlayView gestureOverlayView, final Gesture gesture) {
//识别用户刚绘制的手势
ArrayList<Prediction> predictions = gestureLibrary.recognize(gesture);
ArrayList<String> result = new ArrayList<String>();
//遍历所有找到的Prediction对象
for (Prediction pred : predictions) {
if (pred.score > 2.0) {
result.add("与手势【" + pred.name + "】相似度为" + pred.score);
}
}
if (result.size() > 0) {
ArrayAdapter<Object> adapter = new ArrayAdapter<Object>(mContext,
android.R.layout.simple_dropdown_item_1line, result.toArray());
new AlertDialog.Builder(mContext).setAdapter(adapter,null).setPositiveButton("确定",null).show();
}else{
Toast.makeText(mContext,"无法找到匹配的手势!",Toast.LENGTH_SHORT).show();
}
}
});
}
}
添加权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
参考:http://blog.youkuaiyun.com/xyz_lmn/article/details/16826669
http://www.jianshu.com/p/095e81f21e07
http://blog.youkuaiyun.com/makeyourchance/article/details/51814749