第7章 Android事件处理

tb教学录像:37分钟)

用户在使用手机、平板电脑时,总是通过各种操作来与软件进行交互,较常见的方式包括键盘操作、触摸操作和手势等。在Android中,这些操作都将转换为对应的事件进行处理,本章就对Android中事件处理进行介绍。

通过阅读本章,您可以:

★ 了解事件处理的机制

★ 掌握键盘事件处理

★ 掌握触摸事件处理

★ 掌握手势的创建与识别

7.1 事件处理概述

tb教学录像:光盘\TM\lx\7\ 事件处理概述.exe

在前面的章节中,简单地介绍了Android中各种常用的控件,它们组成了应用程序界面。此外,还应当学习如何处理用户对这些控件的操作,如单击按钮等,这就是本章的核心内容。

现在的图形界面应用程序,都是通过事件来实现人机交互的。事件就是用户对图形界面的操作。在Android手机和平板电脑上,主要包括键盘事件和触摸事件两大类。键盘事件包括按下、弹起等,触摸事件包括按下、弹起、滑动、双击等。

在Android控件中,提供了事件处理的相关方法。例如在View类中,提供了onTouchEvent()方法来处理触摸事件。但是,仅有重写这个方法才能完成事件处理显然并不实用。这种方式主要适用于重写控件的场景。除了onTouchEvent()方法,还可以使用setOnTouchListener()方法为控件设置监听器来处理触摸事件,这在日常开发中更加常用。

7.2 处理键盘事件

tb教学录像:光盘\TM\lx\7\ 处理键盘事件.exe

7.2.1 物理按键简介

对于一个标准的Android设备,包含了多个能够触发事件的物理按键,如图7.1所示。

241-1 图7.1 带有物理键盘的Android模拟器

说明:模拟器Skin使用内置的HVGA。

各个可用的物理按键能够触发的事件及其说明如表7.1所示。

表7.1 Android设备可用物理按键及其触发事件

物理按键 KeyEvent 说 明
电源键 KEYCODE_POWER 启动或唤醒设备,将界面切换到锁定的屏幕
后退键 KEYCODE_BACK 返回到前一个界面
菜单键 KEYCODE_MENU 显示当前应用的可用菜单
Home键 KEYCODE_HOME 返回到Home界面
查找键 KEYCODE_SEARCH 在当前应用中启动搜索
相机键 KEYCODE_CAMERA 启动相机
音量键 KEYCODE_VOLUME_UP KEYCODE_VOLUME_DOWN 控制当前上下文音量,如音乐播放器、手机铃声、通话音量等
方向键 KEYCODE_DPAD_CENTER KEYCODE_DPAD_UP KEYCODE_DPAD_DOWN KEYCODE_DPAD_LEFT KEYCODE_DPAD_RIGHT 某些设备中包含方向键,用于移动光标等
键盘键 KEYCODE_0, …, KEYCODE_9, KEYCODE_A, …, KEYCODE_Z 数字0~9、字母A~Z等按键

Android中控件在处理物理按键事件时,提供的回调方法有onKeyUp()、onKeyDown()和onKeyLongPress()。

7.2.2 范例1:屏蔽后退键

例7.1 在Eclipse中创建Android项目,名称为7.1,屏蔽物理键盘中的后退键。(实例位置:光盘\TM\sl\7\7.1)

编写ForbiddenBackActivity,重写onCreate()方法来加载布局文件,重写onKeyDown()方法来拦截用户单击后退按钮事件,代码如下:

  1. public class ForbiddenBackActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.main); //设置页面布局
  6. }
  7. @Override
  8. public boolean onKeyDown(int keyCode, KeyEvent event) {
  9. if (keyCode == KeyEvent.KEYCODE_BACK) {
  10. return true; //屏蔽后退键
  11. }
  12. return super.onKeyDown(keyCode, event);
  13. }
  14. }

运行程序后,显示如图7.2所示的界面。单击后退键,可以看到应用程序并未退出。

243-1 图7.2 屏蔽物理按键

7.2.3 范例2:提示音量增加事件

例7.2 在Eclipse中创建Android项目,名称为7.2,当用户单击增加音量键时显示提示信息。(实例位置:光盘\TM\sl\7\7.2)

编写VolumeUpMessageActivity类,它继承了Activity类。重写onCreate()方法来加载布局文件,重写onKeyDown()方法,当音量增加键被按下时显示提示信息,代码如下:

  1. public class VolumeUpMessageActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.main); //设置页面布局
  6. }
  7. @Override
  8. public boolean onKeyDown(int keyCode, KeyEvent event) {
  9. if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
  10. Toast.makeText(this, "音量增加", Toast.LENGTH_LONG).show(); //提示音量增加
  11. return false;
  12. }
  13. return super.onKeyDown(keyCode, event);
  14. }
  15. }

运行程序后,显示如图7.3所示的界面。单击音量增加键,屏幕下方显示音量增加信息。

注意:当单击音量增加键时,onKeyDown()方法的返回值是false,这并没有屏蔽该键的功能。

244-1 图7.3 显示音量增加信息

7.3 处理触摸事件

tb教学录像:光盘\TM\lx\7\处理触摸事件.exe

目前,主流的手机都以较大的屏幕取代了外置键盘,平板电脑也没有提供键盘,这些设备都需要通过触摸来操作,下面介绍一下Android中如何实现触摸事件的处理。

7.3.1 范例1:按钮触摸事件

对于触摸屏上的按钮,可以使用OnClickListener和OnLongClickListener监听器分别处理用户短时间单击和长时间单击(按住按钮一段时间)事件。

例7.3 在Eclipse中创建Android项目,名称为7.3,当用户短时间单击按钮和长时间单击按钮时,显示不同的提示信息。(实例位置:光盘\TM\sl\7\7.3)

编写TouchEventActivity类,它继承了Activity类。重写onCreate()方法来加载布局文件,使用findViewById()方法获得布局文件中定义的按钮,并为其增加OnClickListener和OnLongClickListener事件监听器,代码如下:

  1. public class TouchEventActivity extends Activity {
  2. /** Called when the activity is first created. */
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.main); //设置页面布局
  7. Button button = (Button) findViewById(R.id.button); //获得按钮控件
  8. button.setOnClickListener(new OnClickListener() {
  9. public void onClick(View v) { //处理用户短时间单击按钮事件
  10. Toast.makeText(TouchEventActivity.this, getText(R.string.short_click),
  11. Toast.LENGTH_SHORT).show();
  12. }
  13. });
  14. button.setOnLongClickListener(new OnLongClickListener() {
  15. public boolean onLongClick(View v) { //处理用户长时间单击按钮事件
  16. Toast.makeText(TouchEventActivity.this, getText(R.string.long_click),
  17. Toast.LENGTH_SHORT).show();
  18. return true;
  19. }
  20. });
  21. }
  22. }

运行程序后,短时间单击按钮,显示如图7.4所示的提示信息。

245-1 图7.4 显示短时间单击按钮信息

长时间单击按钮,显示如图7.5所示的提示信息。

245-2 图7.5 显示长时间单击按钮信息

View类是其他Android控件的父类。在该类中,定义了setOnTouchListener()方法用来为控件设置触摸事件监听器,下面演示该监听器的用法。

7.3.2 范例2:检测触摸事件

例7.4 在Eclipse中创建Android项目,名称为7.4,当用户触摸屏幕时显示提示信息。(实例位置:光盘\TM\sl\7\7.4)

编写ScreenTouchEventActivity类,它继承了Activity类并实现了OnTouchListener接口。重写onCreate()方法来定义线性布局管理器,并为其增加触摸事件监听器及设置背景图片,重写onTouch()方法来处理触摸事件,显示提示信息,代码如下:

  1. public class ScreenTouchEventActivity extends Activity implements OnTouchListener {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState); //调用父类构造方法
  5. LinearLayout layout = new LinearLayout(this); //定义线性布局
  6. layout.setOnTouchListener(this); //设置触摸事件监听器
  7. layout.setBackgroundResource(R.drawable.background); //设置背景图片
  8. setContentView(layout); //使用布局
  9. }
  10. @Override
  11. public boolean onTouch(View v, MotionEvent event) {
  12. Toast.makeText(this, "发生触摸事件", Toast.LENGTH_LONG).show();
  13. return true;
  14. }
  15. }

运行程序后,触摸屏幕,显示如图7.6所示的提示信息。

246-1 图7.6 显示触摸事件信息

7.4 手势的创建与识别

tb教学录像:光盘\TM\lx\7\手势的创建与识别.exe

前面介绍的触摸事件比较简单,下面介绍一下如何在Android中创建和识别手势。目前有很多款手机都支持手写输入,其原理就是根据用户输入的内容,在预先定义的词库中查找最佳的匹配项供用户选择。在Android中,也需要先定义类似的词库。

7.4.1 手势的创建

下面请读者运行自己的模拟器,进入到应用程序界面,如图7.7所示。

247-1 图7.7 应用程序界面

在图7.7中,单击Gestures Builder应用,如图7.8所示。

247-2 图7.8 Gestures Builder程序界面

在图7.8中,单击Add gesture增加手势,如图7.9所示。在Name栏中输入该手势所代表的字符,在Name栏下方画出对应的手势。单击Done按钮完成手势的增加。

247-3 图7.9 增加手势界面

类似的,继续增加数字1、2、3所对应的手势,如图7.10所示。

247-4 图7.10 显示当前已经存在的手势

7.4.2 手势的导出

在创建完手势后,需要将保存手势的文件导出,以便在自己开发的应用程序中使用。打开Eclipse并切换到DDMS视图。在File Explorer中找到\mnt\sdcard\gestures文件,如图7.11所示。将该文件导出,使用默认名称。

247-5 图7.11 导出保存手势的文件

7.4.3 手势的识别

例7.5 在Eclipse中创建Android项目,名称为7.5,实现识别用户输入手势的功能。(实例位置:光盘\TM\sl\7\7.5)

(1)在res文件夹中创建子文件夹,名称为raw。将前面导出的手势文件复制到该文件夹中。

(2)修改layout文件夹中的main.xml文件,添加一个GuestOverlayView控件来接收用户的手势。修改完成后,main.xml文件代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:background="@drawable/background"
  6. android:orientation="vertical" >
  7. <TextView
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:gravity="center_horizontal"
  11. android:text="@string/title"
  12. android:textColor="@android:color/black"
  13. android:textSize="20dp" />
  14. <android.gesture.GestureOverlayView
  15. android:id="@+id/gestures"
  16. android:layout_width="fill_parent"
  17. android:layout_height="0dip"
  18. android:layout_weight="1.0" />
  19. </LinearLayout>

(3)创建GesturesRecognitionActivity类,它继承了Activity类并实现了OnGesturePerformedListener接口。在onCreate()方法中,加载raw文件夹中的手势文件,接着获得布局文件中定义的GestureOverlayView控件。在onGesturePerformed()方法的实现中,获得得分最高的预测结果并提示,该类代码如下:

  1. public class GesturesRecognitionActivity extends Activity implements OnGesturePerformedListener {
  2. private GestureLibrary library;
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.main);
  7. library = GestureLibraries.fromRawResource(this, R.raw.gestures); //加载手势文件
  8. if (!library.load()) { //如果加载失败则退出
  9. finish();
  10. }
  11. GestureOverlayView gesture = (GestureOverlayView) findViewById(R.id.gestures);
  12. gesture.addOnGesturePerformedListener(this); //增加事件监听器
  13. }
  14. @Override
  15. public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
  16. ArrayList<Prediction> gestures = library.recognize(gesture); //获得全部预测结果
  17. int index = 0; //保存当前预测的索引号
  18. double score = 0.0; //保存当前预测的得分
  19. for (int i = 0; i < gestures.size(); i++) { //获得最佳匹配结果
  20. Prediction result = gestures.get(i); //获得一个预测结果
  21. if (result.score > score) {
  22. index = i;
  23. score = result.score;
  24. }
  25. }
  26. Toast.makeText(this, gestures.get(index).name, Toast.LENGTH_LONG).show();
  27. }
  28. }

运行程序后,绘制手势,如图7.12所示。

249-1 图7.12 用户绘制的手势

在手势绘制完成后,显示提示信息,如图7.13所示。

249-2 图7.13 手势对应的信息

7.5 经典范例

7.5.1 查看手势对应分值

例7.6 在Eclipse中创建Android项目,名称为7.6,实现显示用户绘制的手势所对应的分值。(实例位置:光盘\TM\sl\7\7.6)

(1)在res文件夹中创建子文件夹,名称为raw,将自定义的手势文件复制到该文件夹中。

说明:这里使用的手势文件仅包含0~9十个数字,用户可以自己制作。

(2)修改layout文件夹中的main.xml文件,添加一个GuestOverlayView控件来接收用户的手势;添加一个标签显示结果。修改后的main.xml文件代码,请参见光盘。

(3)创建GesturesGuessActivity类,它继承了Activity类并实现了OnGesturePerformedListener接口。在onCreate()方法中,加载raw文件夹中的手势文件,接着获得布局文件中定义的GestureOverlayView控件。在onGesturePerformed()方法的实现中,获得所有手势所对应的分值并进行显示,该类代码如下:

  1. public class GestureGuessActivity extends Activity implements OnGesturePerformedListener {
  2. private GestureLibrary library;
  3. private TextView resultTV;
  4. @Override
  5. public void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.main);
  8. library = GestureLibraries.fromRawResource(this, R.raw.gestures); //加载手势文件
  9. resultTV = (TextView) findViewById(R.id.prediction);
  10.   if (!library.load()) { //如果加载失败则退出
  11. finish();
  12. }
  13. GestureOverlayView gesture = (GestureOverlayView) findViewById(R.id.gestures);
  14. gesture.addOnGesturePerformedListener(this); //增加事件监听器
  15. }
  16. @Override
  17. public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
  18. ArrayList<Prediction> gestures = library.recognize(gesture); //获得全部预测结果
  19. Collections.sort(gestures, new Comparator<Prediction>() { //将预测结果进行排序
  20. @Override
  21. public int compare(Prediction lhs, Prediction rhs) {
  22. return lhs.name.compareTo(rhs.name); /使用结果对应的字符串来排序
  23. }
  24. });
  25. StringBuilder results = new StringBuilder(); //保存全部结果
  26. NumberFormat formatter = new DecimalFormat("#00.00"); //定义格式化样式
  27. for (int i = 0; i < gestures.size(); i++) { //遍历全部结果
  28. Prediction result = gestures.get(i);
  29. results.append(result.name + ": " + formatter.format(result.score) + "\n");
  30. }
  31. resultTV.setText(results); /显示结果
  32. }
  33. }

运行程序后,绘制手势,如图7.14所示。

250-1 图7.14 用户绘制的手势

在手势绘制完成后,显示得分信息,如图7.15所示。

250-2 图7.15 手势得到的分值

7.5.2 使用手势输入数字

例7.7 在Eclipse中创建Android项目,名称为7.7,利用用户绘制的手势在编辑框中输入数字。(实例位置:光盘\TM\sl\7\7.7)

(1)在res文件夹中创建子文件夹,名称为raw。将自定义的手势文件复制到该文件夹中。

说明:这里使用的手势文件仅包含0~9十个数字,用户可以自己制作。

(2)修改layout文件夹中的main.xml文件,添加一个编辑框显示结果;添加一个GuestOverlayView控件来接收用户的手势,修改后的main.xml文件代码请参见光盘。

(3)创建NumberInputActivity类,它继承了Activity类并实现了OnGesturePerformedListener接口。在onCreate()方法中,加载raw文件夹中的手势文件,接着获得布局文件中定义的GestureOverlayView控件。在onGesturePerformed()方法的实现中,获得最佳匹配进行显示,该类代码如下:

  1. public class NumberInputActivity extends Activity implements OnGesturePerformedListener {
  2. private GestureLibrary library;
  3. private EditText et;
  4. @Override
  5. public void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.main);
  8. library = GestureLibraries.fromRawResource(this, R.raw.gestures); //加载手势文件
  9. et = (EditText) findViewById(R.id.editText);
  10. if (!library.load()) { //如果加载失败则退出
  11. finish();
  12. }
  13. GestureOverlayView gesture = (GestureOverlayView) findViewById(R.id.gestures);
  14. gesture.addOnGesturePerformedListener(this); //增加事件监听器
  15. }
  16. @Override
  17. public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
  18. ArrayList<Prediction> gestures = library.recognize(gesture); //获得全部预测结果
  19. int index = 0; //保存当前预测的索引号
  20. double score = 0.0; //保存当前预测的得分
  21. for (int i = 0; i < gestures.size(); i++) { //获得最佳匹配结果
  22. Prediction result = gestures.get(i); //获得一个预测结果
  23. if (result.score > score) {
  24. index = i;
  25. score = result.score;
  26. }
  27. }
  28. String text = et.getText().toString(); //获得编辑框中已经包含的文本
  29. text += gestures.get(index).name; //获得最佳匹配
  30. et.setText(text); //更新编辑框
  31. }
  32. }

运行程序后,绘制手势,如图7.16所示。

252-1 图7.16 用户绘制的手势

在手势绘制完成后,显示最佳匹配信息,如图7.17所示。

252-2 图7.17 手势对应的字符

7.6 小 结

本章重点介绍了Android中常见的事件处理方式,通过与前面介绍的常用控件结合,就可以实现Android应用程序的外部骨架。本章介绍的内容几乎在各个应用程序中都会使用,请读者务必熟练掌握。

7.7 实践与练习

  1. 编写Android程序,显示用户触摸持续的时间。(答案位置:光盘\TM\sl\7\7.8)

  2. 编写Android程序,显示用户触摸的位置。(答案位置:光盘\TM\sl\7\7.9)