经过了一段Android的学习,基本上了解了Android的基础知识。今天,我们来研究一下Android中特有的手势识别技术,即Gesture。首先,我从网上找了很多资料,具体归纳起来有2类:一类是触摸屏手势识别,另一类是输入法手势识别。
我们先来讨论一下第一类触摸屏手势识别,这个比较简单,就是利用触摸屏的Fling、Scroll等Gesture(手势)来操作屏幕,比如用Scroll手势在 浏览器中滚屏,用Fling在阅读器中翻页等。在Android系统中,手势的识别是通过 GestureDetector.OnGestureListener接口来实现的。下面通过我自己的一个动手实验来进行介绍。
首先,我们在Eclipse中创建一个工程,名称叫SignFilpDemo。然后,在src中创建一个SignFilpDemo源文件。这个工程是基于Android1.6的。因为,在Android 1.6之前的版本中,需要开发者编写大量的代码才能实现某些更为复杂的Gestures功能。而在之后的版本SDk中嵌入标准的Gestures API库(Package: android.gesture),包括了所有与Gesture技术相关的操作:存储、加载、创建新Gestures和识别等。其工程结构如下:
图1
接着,我们知道Android的事件处理机制是基于Listener(监听器)来实现的,比如我们今天所说的触摸屏相关的事件,就是通过onTouchListener。因此,我们在源代码中要实现这个接口。另外,我们还要实现OnGestureListener接口,因为,那个手势的识别是通过 GestureDetector.OnGestureListener接口来实现的。其中,onTouchEvent方法则是实现了OnTouchListener中的抽象方法,我们只要在这里添加逻辑代码即 可在用户触摸屏幕时做出响应。在这里我们调用GestureDetector的onTouchEvent()方法,将捕捉到的MotionEvent交给 GestureDetector 来分析是否有合适的callback函数来处理用户的手势。接下来,就是实现了以下6个抽象方法,其中最有用的当然是onFling()、onScroll()和onLongPress()了。下面介绍一下:
(1) 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发,其源代码如下:
public boolean onDown(MotionEvent e) {
return false;
}
public void onShowPress(MotionEvent e) {
}
(2) 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发,其源代码如下:
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
(3) 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发
// 用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发
public void onLongPress(MotionEvent e) {
}
// 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,float distanceY) {
return false;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {
return false;
}
我们今天来做一个onFling()事件的处理吧,这里需要注意的是Fling事件的处理代 码中,除了第一个触发Fling的ACTION_DOWN和最后一个ACTION_MOVE中包含的坐标等信息外,我们还可以根据用户在X轴或者Y轴上的 移动速度作为条件。在这个例子中,我们是根据用户在屏幕上移动的x轴坐标或者是y轴坐标位移来判断方向。如果x轴坐标位移大于50向左,小于-50向右,如果y轴坐标位移大于50向上,小于-50向下。源代码如下:
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (e1.getX() - e2.getX() > 50) {
Toast.makeText(SignFlipDemo.this, "左", Toast.LENGTH_LONG).show();
return true;
} else if (e1.getX() - e2.getX() < -50) {
Toast.makeText(SignFlipDemo.this, "右", Toast.LENGTH_LONG).show();
return true;
} else if (e1.getY() - e2.getY() > 50) {
Toast.makeText(SignFlipDemo.this, "上", Toast.LENGTH_LONG).show();
return true;
} else if (e1.getY() - e2.getY() < -50) {
Toast.makeText(SignFlipDemo.this, "下", Toast.LENGTH_LONG).show();
return true;
}
return false;
}
如果鼠标向上移动,运行效果如下图2所示:
图2
如果鼠标向下移动,运行效果如下图3所示:
图3
如果鼠标向左移动,运行效果如下图4所示:
图4
如果鼠标向右移动,运行效果如下图5所示:
图5
通过上面的运行结果可以看出,只要鼠标向上下左右四个方向移动,就是相应地识别出,并且显示出相应的方向。这里需要注意的是因为是模拟器,所以只有通过鼠标来模拟,在真机环境中,就是触摸屏的识别了。
接下来,我们来讨论一下第二种输入法手势识别。这种方法就是需要定义一个描述手势动作的文件。在手写输入中,会为每一个字符定义一个特征码,这些特征码都保存在相应的文件中(可能有一个或多个这样的文件),当用户绘制一个描述字符的图形时,系统会为所绘制的图形提取特征码,然后会在保存特征码文件中查找相对应的特征码,如果找到,就会将对应的字符返回。其中,这些文件被称为手势文件。这里我们讨论的是在Android 2.1中提供了一个GestureBuilder工程(实际上从Android 1.6就带这个例子了),这个工程是一个手势API的演示,主要功能是建立手势信息,并将手势信息保存在手势文件中。下面,我们来演示一下。
首先在Eclipse中打开这个工程,然后可以直接导入运行。运行后界面如图6所示。
图6
由于上面还没建立手势,所以手势列表为空。下面开始建立我们的第一个手势。单击“Add gesture”按钮,会进入建立手势的界面。如图7所示。建立手势需要两个信息:手势名和手势图形。在文本框中输入任意的字符串,如“矩形”,并且在文本框下面画一个如图7所示的矩形(差不多就行,不一定要很标准)。然后单击“Done”按钮,第一个手势已经建立成功了。按着这种方法,再建立几个手势。每成功建立完一个手势后,会在主界面的列表中看到我们所建立的手势,如图8所示。进入DDMS透视图,会在SD卡的根目录看到一个gestures文件,如图9所示。然后将该文件导出到PC上,以备以后使用。
图7
图8
图9
通过前面的介绍,我们知道绘制手势图像好像是在一个类似画布的区域,实际上,这是一个android.gesture.GestureOverlayView组件。我们可以在这个组件上绘制手势,并会触发一个识别手势的事件。了解了原理,接下来,我们利用手势自动输入字符串。
首先需要在布局文件中添加一个EditText和GestureOverlayView组件,代码如下:
<?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"
>
<EditText android:id="@+id/edittext"
android:layout_height="wrap_content" android:layout_width="fill_parent"/>
<android.gesture.GestureOverlayView
android:gestureStrokeType="multiple" android:id="@+id/gestures"
android:layout_height="fill_parent" android:layout_width="fill_parent"/>
</LinearLayout>
接着,使用手势文件的第一步是装载手势文件。可以将手势文件放在SD卡的相应目录,也可以放在apk中一起打包。在本例中将gestures文件放在了工程目录的res/raw目录中,因此,这个手势文件是包含在apk文件中的。下面在onCreate方法中装载这个手势文件,代码如下:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
edittext = (EditText)findViewById(R.id.edittext);
// 从res/raw目录中装载gestures文件
gestureLibrary = GestureLibraries.fromRawResource(this, R.raw.gestures);
// 如果成功装载手势文件,获得GestureOverlayView对象,并向该对象注册事件
if(gestureLibrary.load())
{
setTitle("手势文件装载成功。");
GestureOverlayView gestures = (GestureOverlayView) findViewById(R.id.gestures);
gestures.addOnGesturePerformedListener((OnGesturePerformedListener) this);
}
else
{
setTitle("手势文件装载失败。");
}
}
处理手势识别事件需要实现GestureOverlayView.OnGesturePerformedListener接口,该接口有一个事件方法,当绘制完手势图形后被调用,可根据相应的API来判断是否成功识别了当前手势,该事件的代码如下:
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture){
ArrayList predictions = gestureLibrary.recognize(gesture);
if(predictions.size()>0){
// predictions保存了所有与当前手势可能匹配的候选手势
for(int i=0;i<predictions.size();i++)
{
Prediction prediction = (Prediction) predictions.get(i);
if(prediction.score>1.0)
{
// 只选择了第一个与当前手势匹配的手势,将手势名输出的文本框中
edittext.append(prediction.name);
break;
}
}
}
}
运行效果如图10
图10
上面,我们已经实现了输入手势识别,绘制手势,程序自动输入对应的文本。主要是导入外部的手势文件进行识别。
本文主要介绍了Android中的两种手势识别技术。一类是触摸屏手势识别,另一类是输入法手势识别。相比较,第一种容易实现,第二种实现起来复杂,还要创建手势文件。在实验中,我碰到一个问题,那就是DDMS中没有显示sdcard文件,这耗费我很长时间,后来只能通过外面的DDMS工具导入gestrue文件。如果有知道的朋友告诉我一下,在此表示谢谢。其实,在第二种识别中,我们可以不用自带的GestureBuilder文件来创建手势文件,那个相对复杂一些,可以自己写一个工程来创建。这里篇幅有限,就留给读者自己思考了。