开始讨论Gestures技术之前,首先回顾与触摸屏技术相关的历史。在1971年,Elographics公司(已经更名为:Elo TouchSystems)的创始人Sam Hurst博士作为肯塔基州立大学导师期间发明了第一块触摸传感器,取名为“Elograph”。与当今的技术相比,其并不算是真正意义上的Touch Screen,但却是触摸屏技术发展史上最重要的里程碑。直到1974年,一款真正意义上的触摸屏问世,实现了透明和显示图像的特性。在1977年,又创 造出了five-wire resistive,这就是今天广泛被应用于许多领域的触摸屏核心技术,具有更加精确和稳定的特点。
目前 Touch Screens技术在不同的应用领域发挥着重要的作用。尤其各种智能移动设备向着更薄、更轻、更强大的方向发展,这对于人机交互提出了更高的要求。凭借触 摸屏技术,使用者将从繁琐的按键操作逐渐过渡为利用各种手势快速和准确的实现人机交互,比如轻轻触摸、拖拽、甩或者滑动等一系列的简单手势快速的实现一些 常规的命令操作。在Android 1.6之前的版本中,需要开发者编写大量的代码才能实现某些更为复杂的Gestures功能。为了让Gestures技术可以被方便的嵌入到第三方软件 中,Android 1.6 SDk中嵌入标准的Gestures API库(Package: android.gesture),包括了所有与Gesture技术相关的操作:存储、加载、创建新Gestures和识别等。接下来将通过具体的例子全 面的介绍如何将Gestures技术加入到Application中。开始前请先下载实例的源码。
创建一个Gestures Library
Gestures Builder 在Android 1.6 SDK 中作为系统工具经Emulator启动时被默认加载。开发者可以直接通过Gestures Builder工具创建一批Gestures库,并添加到自己的程序中为用户提供可直接使用的Gestures行为。如果希望用户可以在程序中自定义 Gestures信息,也可以将Gestures Builder作为一个单独的模块整合到程序中,使用的方式与单独操作Gestures Builder工具完全一致。Gestures Builder工具的源码存放在Samples中,建议大家亲自尝试。使用Gestures Builder生成的gestures集合将自动存放在SD Card中。如果使用Emulator调试程序,需要在使用Gestures Builder之前确保当前有可用的虚拟SD Card映射。下边的截图表示当前已经添加三个gestures。
上 边的图示中可以看到每个条目包含两个基本元素,gesture 2D信息和标签,标签在Application中起到索引的作用。在同一个Application中允许包含一个以上带有相同标签的集合,这样可以提高某 些指令被准确识别的几率。利用Gestures Builder添加或者编辑gestures,生成的最终文件存放在SD card中的 gestures目录。可以直接将这个目录下的gestures拷贝到Application的/res/raw中作为默认的gestures资源。
加载Gestures Library
根 据前一节的了解,完全可以掌握利用Gestures Builder创建gestures的方法,并将生成的文件捆绑在自己的应用程序中,作为默认的gestures库。接下来了解如何在 Applicationg程序中加载库中的gestures资源(利用GestureLibraries类可以非常容易处理这个请求):
1.mLibrary = GestureLibraries.fromRawResource(this, R.raw.spells);
2.if (!mLibrary.load()) {
3. finish();
4.}
上 边的代码实现了调用gestures前的准备工作。另外除了从本地raw中获取资源以外,也可以直接从SD Card中加载资源,特别针对允许用户自行创建gestures的应用程序,需要在程序中实现基于SD Card的读取和存储方法。由raw中读取的gestures仅具有可读属性,用户无法编辑默认捆绑在Application中的gestures资源。
下边的图标展示了Gesture Library的层级结构。
Gestures的识别方法
在应用程序中识别gestures包括绘制和识别两个部分。为了让用户可以直接在Applications表面绘制gestures,需要将下边的标签加入到当前窗口的XML Layout中。
?View Code XML
<android.gesture.GestureOverlayView
android:id="@+id/gestures"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1.0" />
由 于GestureOverlayView类并不属于android.widget资源范畴,因此调用时必须提供完整名称(Botton或者其它的 Views只需要提供Short Name就可以直接被系统识别)。Gesture overlay相当于一块简单的绘画板,允许用户绘制各种gestures。Gesture overlay的显示属性可以根据应用程序的整体样式而制定合适的色彩和笔触宽度。利用Listener跟踪Gestures的不同行为,从而相应用户的 细微操作。其中OnGesturePerformedListener是最常用的方法,当用户完成一次Gesture绘制后,系统将自动运行这个 Listener中的代码。至此,完成了Gestures识别功能中的第一个步骤。
1.GestureOverlayView gestures = (GestureOverlayView) findViewById(R.id.gestures);
2.gestures.addOnGesturePerformedListener(this);
接 下来通过GestureLibrary对用户绘制的结果进行比对,每次的比对结果将可能包括多个相似的结果。根据数量级(系统中会针对一个 Gesture创建多个实例,目的是达到更加精确的识别目的)的原则,依据降序的排列方法选择最贴近用户意图的结果。下边的代码实现了完整的“筛选”过 程。
01.public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture)
02.{
03. ArrayList predictions = mLibrary.recognize(gesture);
04.
05. // We want at least one prediction [androidres.com]
06. if (predictions.size() > 0) {
07. Prediction prediction = predictions.get(0);
08. // We want at least some confidence in the result
09. if (prediction.score > 1.0) {
10. // Show the spell
11. <a title="Toast" href="http://www.androidres.com/index.php/2009/04/18/toast/">Toast</a>.makeText(this, prediction.name,Toast.LENGTH_SHORT).show();
12. }
13. }
14.}
根 据代码的执行过程,recognize()的返回结果是一个prediction集合,包含了所有与gesture相匹配的结果。prediction 的score属性代表了与gesture得相似程度(通常情况下不考虑score小于1的结果)。以上是Gestures应用的两个基本功能的介绍,非常 简单的几行代码就就可以让你的程序支持当前最流行的人性化操作方式。
Gestures Overlay
接 下来介绍另外一个全新的用法。前面是将GestureOverlayView作为一般的View,被直接嵌入在LinearLayout的子集中。然而, 根据这个类名的直观理解是可以作为所有Views元素之上的一个Overlay来使用,更形象的解释是将GestureOverlayView看做一个盒 子,而其它的Views应用元素成为它的子集。这样的方式被普遍应用于游戏操作或者仅限制Application中的某一部分的UI支持 Gestures功能。接下来我们利用GestureListDemo例子来体验这种用法,实现的效果是在一个Contacts列表上创建一个 Gestures Overlay。首先仍然需要利用Gestures Builder初始化一些gestures数据:
XML layout代码(完全不同于第一个例子的配置方法):
?View Code XML
<android.gesture.GestureOverlayView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gestures"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gestureStrokeType="multiple"
android:eventsInterceptionEnabled="true"
android:orientation="vertical">
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</android.gesture.GestureOverlayView>
当利用Gestures view的Overlay特性时,需要利用更多的属性来设置当前Gesture的状态,在第一个例子中则不需要考虑这些:
1)gestureStrokeType:设置Stroke的类型。大多数情况下,gestures都是通过一笔完成。然而有一些特别的需求就需要通过多个笔画来实现,例如通过“+”来实现“Add”的行为。
* Single:0
* Multiple:1
2)eventsInterceptionEnabled: 可以简单理解为当前应用程序在Gesture overlay模式下屏蔽其它Views Events的开关。当它设置为True时,所有基于当前应用程序屏幕的操作都被视作为绘制gestures的行为,从而避免用户与应用程序界面的交互造 成混乱。
3)orientation:这个属性作为当前Gesture recognizing的参考标准(相当于filter的作用),例如:在这个例子中的Items垂直排列,所有基于Overlay的垂直 (vertical) gesture,都无法被识别为有效的gesture,其与当前的垂直滚动条操作造成混淆。对于水平 (horizontal) gesture可以立即识别为有效的gesture,或者任何以垂直为起始的操作需要至少包含一段水平的绘制轨迹才可以被正常识别。
* Horizontal : 0
* Vertical : 1
01.public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture)
02.{
03. ArrayList<prediction> predictions = mLibrary.recognize(gesture);
04. if (predictions.size() > 0 && predictions.get(0).score > 1.0)
05. {
06. String action = predictions.get(0).name;
07. if ("action_add".equals(action))
08. {
09. Toast.makeText(this, "Adding a contact",Toast.LENGTH_SHORT).show();
10. }
11. else if ("action_delete".equals(action))
12. {
13. Toast.makeText(this, "Removing a contact",Toast.LENGTH_SHORT).show();
14. }
15. else if ("action_refresh".equals(action))
16. {
17. Toast.makeText(this, "Reloading contacts",Toast.LENGTH_SHORT).show();
18. }
19. }
20.}
21.</prediction>
根据上边的设置,目前用户可以基于ListView之上做任何Gesture操作,而不会与当前的Scrolling Events混淆。
从视觉上可以直接判断当前Gesture是否有效的被当前Application识别。对于无效的Stroke将会以半透明的方式显示。
相信很快这项技术将作为最基本的应用部署在大部分的应用程序中。让我们开始尝试创建第一个带有Gestures功能的应用程序吧!