Android的事件处理概述
基于监听的事件处理
基于回调的事件处理
响应的系统设置的事件
Handler消息传递机制
Android的事件处理概述
不管是桌面应用还是手机应用程序,面对最多的就是用户,经常需要处理的就是用户动作——也就是需要为用户的动作提供响应,这种为用户动作提供响应的机制就是事件处理 。
Android提供了强大的事件处理机制,包括两套事件处理机制:
基于监听的事件处理
基于回调的事件处理
基于监听的事件处理
基于监听的事件处理是一种“面向对象”的事件处理,主要涉及如下三个对象。
EventSource(事件源):事件发生的场所,通常就是各个组件,例如窗口、按钮、菜单等 。
Event(事件):事件封装了界面组件上发生的特定事情,通常是一次用户操作,如果程序需要获得界面组件上所发生事件的相关信息,一般通过Event对象来取得 。
EventListener(事件监听器):负责监听事件源所发生的事件,并对各种事件作出相应的响应。
Android的事件处理机制是一种委派式事件处理方式:普通组件(事件源)将整个事件处理委托给特定的对象(事件监听器);当该事件源发生指定的事件时,就通知委托的事件监听器,由事件监听器来处理这个事件。
每个组件均可以针对特定的事件指定一个事件监听器,每个事件监听器也可监听一个或多个事件源。同时也可以让一类事件使用同一个事件监听器来处理。如图:
基于监听的事件处理模型的编程步骤如下:
获取普通界面组件(事件源),也就是被监听的对象。
实现事件监听器类,该监听器类是一个特殊的Java类,必须实现一个XxxListener接口。
调用事件源的setXxxListener方法将事件监听器对象注册给普通组件。
当事件源上发生指定事件时,Android会触发事件监听器,由事件监听器调用相应的方法(事件处理器)来处理事件。
Android还有一种更加简单的绑定事件监听器的方式,直接在界面布局文件中为指定标签绑定事件处理方法。
对于很多Android界面组件而言,它们都支持如onClick、onLongClick等属性,这种属性的属性值就是一个形如xxx(View source)的方法名。
基于回调的事件处理
Android基于回调的事件处理,主要做法是重写Android组件特定的回调方法,或者重写Activity的方法。
对于基于回调的事件处理模型来说,事件源和事件监听器是统一的,或者说是事件监听器完全消失了。当用户在GUI组件上激发某个事件时,组件自己特定的方法将会负责处理该事件。
为了实现回调机制的事件处理,Android为所有GUI组件都提供了一些事件处理的回调方法,以View为例,该类包含如下方法:
boolean onKeyDown(int keyCode ,KeyEvent event):当用户在该组件上按下某个按键时触发该方法。
boolean onKeyLongPress(int keyCode, KeyEvent event):当用户在该组件上长按某个按键时触发该方法。
boolean onKeyShortcut(int keyCode, KeyEvent event):当一个键盘快捷键事件发生时触发该方法。
boolean onKeyUp(int keyCode, KeyEvent event):当用户在该组件上松开某个按键时触发该方法。
boolean onTouchEvent(MotionEvent event):当用户在该组件上触发触摸屏事件时触发该方法。
boolean onTrackballEvent(MotionEvent event):当用户在该组件上触发轨迹球屏事件时触发该方法。
Android平台中,每个View都有自己的处理事件的回调方法,开发人员可以通过重写View中的这些回调方法来实现需要的响应事件。当某个事件没有被任何一个View处理时,便会调用Activity中相应的回调方法。
基于回调的事件传播
几乎所有基于回调的事件处理方法都有一个boolean类型的返回值,该方法用于标识该处理方法是否能完全处理该事件:
如果处理事件的回调方法返回true,表明该处理方法已完全处理该事件,该事件不会传播出去。
如果处理事件的回调方法返回false,表明该处理方法并未完全处理该事件,该事件会传播出去。
对于基于回调的事件传播而言,某组件上所发生的事情不仅激发该组件上的回调方法,也会触发该组件所在Activity的回调方法—只要事件能传播到该Activity。
对比Android提供的两种事件处理模型,不难发现基于监听的事件处理模型具有更大的优势:
基于监听的事件模型分工更明确,事件源、事件监听器由两个类分开实现,因此具有更好的可维护性。
Android的事件处理机制保证基于监听的事件监听器会被优先触发。
响应的系统设置的事件
在开发Android应用时,有时候可能需要让应用程序随系统设置而进行调整,比如判断屏幕方向、判断系统方向的方向导航设备等。除此之外,有时还需要让应用程序监听系统设置的更改,对系统设置的更改做出响应。
Configuration类的简介
Configuration类专门用于描述手机设备上的配置信息,该类包含了很多种信息,例如系统字体大小,orientation输入设备类型等等。
程序可调用Activity的如下方法来获取系统的Configuration对象。
Configuration cfg=getResources().getConfiguration();
Configuration对象提供了以下方法来获取系统的配置信息。
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:gravity="center_horizontal"
android:orientation="vertical" >
<EditText
android:id="@+id/ori"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:cursorVisible="false"
android:editable="false"
android:hint="显示屏幕方向" />
<EditText
android:id="@+id/navigation"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:cursorVisible="false"
android:editable="false"
android:hint="显示手机方向控制设备" />
<EditText
android:id="@+id/touch"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:cursorVisible="false"
android:editable="false"
android:hint="显示触摸屏状态" />
<EditText
android:id="@+id/mnc"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:cursorVisible="false"
android:editable="false"
android:hint="显示移动网络代号" />
<Button
android:id="@+id/bn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="获取手机信息" />
</LinearLayout>
ConfigurationTest.java
public class ConfigurationTest extends Activity
{
EditText ori;
EditText navigation;
EditText touch;
EditText mnc;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//获取应用界面中的界面组件
ori = (EditText)findViewById(R.id.ori);
navigation = (EditText)findViewById(R.id.navigation);
touch = (EditText)findViewById(R.id.touch);
mnc = (EditText)findViewById(R.id.mnc);
Button bn = (Button)findViewById(R.id.bn);
bn.setOnClickListener(new OnClickListener()
{
//为按钮绑定事件监听器
@Override
public void onClick(View source)
{
//获取系统的Configuration对象
Configuration cfg = getResources().getConfiguration();
String screen = cfg.orientation ==
Configuration.ORIENTATION_LANDSCAPE ? "横向屏幕": "竖向屏幕";
String mncCode = cfg.mnc + "";
String naviName = cfg.orientation ==
Configuration.NAVIGATION_NONAV
? "没有方向控制" :
cfg.orientation == Configuration.NAVIGATION_WHEEL
? "滚轮控制方向" :
cfg.orientation == Configuration.NAVIGATION_DPAD
? "方向键控制方向" : "轨迹球控制方向";
navigation.setText(naviName);
String touchName = cfg.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
? "无触摸屏" :
cfg.touchscreen == Configuration.TOUCHSCREEN_STYLUS
? "触摸笔式触摸屏" : "接受手指的触摸屏";
ori.setText(screen);
mnc.setText(mncCode);
touch.setText(touchName);
}
});
}
}
如果程序需要监听系统设置的更改,则可以考虑重写Activity的onConfigurationChanged方法,该方法是一个基于回调的事件处理方法。当系统设置发生更改时,该方法会被自动触发。
为了在程序中动态地更改系统设置,我们可调用Activity的setRequestedOrientation(int)方法来修改屏幕的方向。
Handler消息传递机制
在Android平台中,新启动的线程是无法访问Activity里的Widget的,当然也不能将运行状态外送出来,这就需要有Handler机制进行消息的传递了,Handler类位于android.os包下。
Handler类的主要作用有两个:
在新启动的线程中发送消息。
在主线程中获取、处理消息。
Handler类的常用方法如下:
void handleMessage (Message msg) :子类对象通过该方法接收信息,该方法用于被重写。
final boolean hasMessages (int what) :监测消息队列中是否还有what值的消息。
final boolean hasMessages (int what,Object object) :监测消息队列中是否包含对象是object且属性是what值的消息。
多个重载的Message obtainMessage():获取消息。
final boolean sendEmptyMessage (int what) :发送一个只含有what值的消息。
final boolean sendMessage (Message msg) :发送消息到Handler,通过handleMessage方法接收。
final boolean sendMessageDelayed (Message msg,long delayMillis) :指定多少毫秒之后发送消息。
HandlerTest.java
public class HandlerTest extends Activity
{
//定义周期性显示的图片的ID
int[] imageIds = new int[]
{
R.drawable.java,
R.drawable.ee,
R.drawable.ajax,
R.drawable.xml,
R.drawable.classic
};
int currentImageId = 0;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ImageView show = (ImageView)findViewById(R.id.show);
final Handler myHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
//如果该消息是本程序所发送的
if (msg.what == 0x1233)
{
//动态地修改所显示的图片
show.setImageResource(imageIds[currentImageId++]);
if (currentImageId >= 4)
{
currentImageId = 0;
}
}
}
};
//定义一个计时器,让该计时器周期性地执行指定任务
new Timer().schedule(new TimerTask()
{
@Override
public void run()
{
//新启动的线程无法访问该Activity里的组件
//所以需要通过Handler发送信息
Message msg = new Message();
msg.what = 0x1233;
//发送消息
myHandler.sendMessage(msg);
}
}, 0 , 800);
}
}