请简述什么是android事件处理,并分析两种android事件处理机制的实现过程和区别
2016-04-09 · 知道合伙人数码行家
知道合伙人数码行家
向TA提问 私信TA
UI编程通常都会伴随事件处理,Android也不例外,它提供了两种方式的事件处理:基于回调的事件处理和基于监听器的事件处理。
对于基于监听器的事件处理而言,主要就是为Android界面组件绑定特定的事件监听器;对于基于回调的事件处理而言,主要做法是重写Android组件特定的回调函数,Android大部分界面组件都提供了事件响应的回调函数,我们主要重写它们就行。
一 基于监听器的事件处理
相比于基于回调的事件处理,这是更具“面向对象”性质的事件处理方式。在监听器模型中,主要涉及三类对象:
1)事件源Event Source:产生事件的来源,通常是各种组件,如按钮,窗口等。
2)事件Event:事件封装了界面组件上发生的特定事件的具体信息,如果监听器需要获取界面组件上所发生事件的相关信息,一般通过事件Event对象来传递。
3)事件监听器Event Listener:负责监听事件源发生的事件,并对不同的事件做相应的处理。
基于监听器的事件处理机制是一种委派式Delegation的事件处理方式,事件源将整个事件委托给事件监听器,由监听器对事件进行响应处理。这种处理方式将事件源和事件监听器分离,有利于提供程序的可维护性。
举例:
View类中的OnLongClickListener监听器定义如下:(不需要传递事件)
[java] view plaincopyprint?
public interface OnLongClickListener {
boolean onLongClick(View v);
}
public interface OnLongClickListener {
boolean onLongClick(View v);
}
View类中的OnLongClickListener监听器定义如下:(需要传递事件MotionEvent)
[java] view plaincopyprint?
public interface OnTouchListener {
boolean onTouch(View v, MotionEvent event);
}
public interface OnTouchListener {
boolean onTouch(View v, MotionEvent event);
}
二 基于回调的事件处理
相比基于监听器的事件处理模型,基于回调的事件处理模型要简单些,该模型中,事件源和事件监听器是合一的,也就是说没有独立的事件监听器存在。当用户在GUI组件上触发某事件时,由该组件自身特定的函数负责处理该事件。通常通过重写Override组件类的事件处理函数实现事件的处理。
举例:
View类实现了KeyEvent.Callback接口中的一系列回调函数,因此,基于回调的事件处理机制通过自定义View来实现,自定义View时重写这些事件处理方法即可。
[java] view plaincopyprint?
public interface Callback {
// 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于
// 标识该处理函数是否能完全处理该事件
// 返回true,表明该函数已完全处理该事件,该事件不会传播出去
// 返回false,表明该函数未完全处理该事件,该事件会传播出去
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
}
public interface Callback {
// 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于
// 标识该处理函数是否能完全处理该事件
// 返回true,表明该函数已完全处理该事件,该事件不会传播出去
// 返回false,表明该函数未完全处理该事件,该事件会传播出去
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
}
三 比对
基于监听器的事件模型符合单一职责原则,事件源和事件监听器分开实现;
Android的事件处理机制保证基于监听器的事件处理会优先于基于回调的事件处理被触发;
某些特定情况下,基于回调的事件处理机制会更好的提高程序的内聚性。
四 基于自定义监听器的事件处理流程
在实际项目开发中,我们经常需要自定义监听器来实现自定义业务流程的处理,而且一般都不是基于GUI界面作为事件源的。这里以常见的app自动更新为例进行说明,在自动更新过程中,会存在两个状态:下载中和下载完成,而我们的程序需要在这两个状态做不同的事情,“下载中”需要在UI界面上实时显示软件包下载的进度,“下载完成”后,取消进度条的显示。这里进行一个模拟,重点在说明自定义监听器的事件处理流程。
4.1)定义事件监听器如下:
UI编程通常都会伴随事件处理,Android也不例外,它提供了两种方式的事件处理:基于回调的事件处理和基于监听器的事件处理。
对于基于监听器的事件处理而言,主要就是为Android界面组件绑定特定的事件监听器;对于基于回调的事件处理而言,主要做法是重写Android组件特定的回调函数,Android大部分界面组件都提供了事件响应的回调函数,我们主要重写它们就行。
一 基于监听器的事件处理
相比于基于回调的事件处理,这是更具“面向对象”性质的事件处理方式。在监听器模型中,主要涉及三类对象:
1)事件源Event Source:产生事件的来源,通常是各种组件,如按钮,窗口等。
2)事件Event:事件封装了界面组件上发生的特定事件的具体信息,如果监听器需要获取界面组件上所发生事件的相关信息,一般通过事件Event对象来传递。
3)事件监听器Event Listener:负责监听事件源发生的事件,并对不同的事件做相应的处理。
基于监听器的事件处理机制是一种委派式Delegation的事件处理方式,事件源将整个事件委托给事件监听器,由监听器对事件进行响应处理。这种处理方式将事件源和事件监听器分离,有利于提供程序的可维护性。
举例:
View类中的OnLongClickListener监听器定义如下:(不需要传递事件)
public interface OnLongClickListener {
boolean onLongClick(View v);
}
View类中的OnLongClickListener监听器定义如下:(需要传递事件MotionEvent)
public interface OnTouchListener {
boolean onTouch(View v, MotionEvent event);
}
二 基于回调的事件处理
相比基于监听器的事件处理模型,基于回调的事件处理模型要简单些,该模型中,事件源和事件监听器是合一的,也就是说没有独立的事件监听器存在。当用户在GUI组件上触发某事件时,由该组件自身特定的函数负责处理该事件。通常通过重写Override组件类的事件处理函数实现事件的处理。
举例:
View类实现了KeyEvent.Callback接口中的一系列回调函数,因此,基于回调的事件处理机制通过自定义View来实现,自定义View时重写这些事件处理方法即可。
public interface Callback {
// 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于
// 标识该处理函数是否能完全处理该事件
// 返回true,表明该函数已完全处理该事件,该事件不会传播出去
// 返回false,表明该函数未完全处理该事件,该事件会传播出去
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
}
三 比对
基于监听器的事件模型符合单一职责原则,事件源和事件监听器分开实现;
Android的事件处理机制保证基于监听器的事件处理会优先于基于回调的事件处理被触发;
某些特定情况下,基于回调的事件处理机制会更好的提高程序的内聚性。
四 基于自定义监听器的事件处理流程
在实际项目开发中,我们经常需要自定义监听器来实现自定义业务流程的处理,而且一般都不是基于GUI界面作为事件源的。这里以常见的app自动更新为例进行说明,在自动更新过程中,会存在两个状态:下载中和下载完成,而我们的程序需要在这两个状态做不同的事情,“下载中”需要在UI界面上实时显示软件包下载的进度,“下载完成”后,取消进度条的显示。这里进行一个模拟,重点在说明自定义监听器的事件处理流程。
4.1)定义事件监听器如下:
public interface DownloadListener {
public void onDownloading(int progress); //下载过程中的处理函数
public void onDownloaded(); //下载完成的处理函数
}
4.2)实现下载操作的工具类代码如下:
public class DownloadUtils {
private static DownloadUtils instance = null;
private DownloadUtils() {
}
public static synchronized DownloadUtils instance() {
if (instance == null) {
instance = new DownloadUtils();
}
return instance;
}
private boolean isDownloading = true;
private int progress = 0;
// 实际开发中这个函数需要传入url作为参数,以获取服务器端的安装包位置
public void download(DownloadListener listener) throws InterruptedException {
while (isDownloading) {
listener.onDownloading(progress);
// 下载过程的简单模拟
Thread.sleep(1000);
progress += 10;
if (progress >= 100) {
isDownloading = false;
}
}
// 下载完成
listener.onDownloaded();
}
}
4.3)最后在main函数中模拟事件源:
public class DownloadUI {
public static void main(String[] args) {
try {
DownloadUtils.instance().download(new MyDownloadListener());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static class MyDownloadListener implements DownloadListener {
@Override
public void onDownloading(int progress) {
System.out.println("下载进度是:" + progress);
}
@Override
public void onDownloaded() {
System.out.println("下载完成");
}
}
}
基于监听的事件处理分工明确,可维护性高,且会优先触发。
内部类形式:Class MyClickListener implements View.OnClickListener{ 实现的方法,即事件处理器}
外部类形式:将事件监听器类定义成一个外部类:public class SendSmsListener implements OnLongClickListener{容易把业务逻辑和显示逻辑耦合,不利于程序的内聚性:p195}
Activity本身作为事件监听器:让Activity本身实现监听器接口,并实现处理方法。做法简洁,Activity本身应该完成界面的初始化工作,同时包含事件处理比较混乱。public
匿名内部类形式:btn.setOnClickListener( new OnClickListener(){ 实现时间的处理方法 });
绑定到标签:在XML未指定标签绑定事件处理方法,android:onClick = "clickHandler",写一个clickHandler(){ 处理 }方法。
2.回调事件处理
监听事件处理是委托式处理,回调事件处理是实现组件自己特定的方法来处理事件,没有监听事件。
public MyButton extends Button{
public boolean onKeyDown(int KeyCode,KeyEvent event){
super.onKeyDown(KeyCode,event);
Log.v("返回值","true不会向外扩散");
return true;
}
}
MyButton重写了父类的onKeyDown()方法,不需要绑定监听,自己会处理相应的事件。
回调方法的返回值为true表示能完全处理,不会传播出去。返回false,表示未完全处理,会传播出去。 如果没有完成处理(false),则由监听器-->事件回调-->传播到activity,都会出发该方法处理。
3.相应系统设置的事件
Configuration类专门用于描述手机上配置信息,包括用户特定配置和系统动态配置。
Configuration cfg = getResources().getConfiguration();获取了Configuration对象可以使用该对象提供的属性来获取系统配置。
4.Handler消息传递机制
主线程又称为UI线程,在主线程不进行耗时操作,这是需要用到handler。耗时操作会引发ANR异常。
Handler类主要有两个作用:在新启动的线程中发送消息至MessageQueue ;在主线程中获取Looper分给他的消息,处理消息。
Looper :每个线程只能拥有一个looper ,负责管理MessageQueue,不断从消息队列中取出消息,分给对应的Handler处理。
MessageQueue :消息队列,采取先进先出的方式管理message。
HandlerMessage() :Handler接收和处理的消息对象。
在主线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler并收发消息即可;而自己启动的子线程,必须自己创建一个Looper对象并启动它:Looper.prepare() 创建对象并保证只有一个Looper对象。
Android不允许在子线程中更改UI组件,需要在子线程中更改界面组件必须用handler实现。
5.异步任务
实现异步任务的机制有两种:Handler和AsyncTask。
AsyncTask<Params,Progress,Result>
三种泛型类型分别代表“启动任务执行的输入参数”、“后台任务执行的进度”、“后台计算结果的类型”。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void类型代替。
一个异步任务的执行一般包括以下几个步骤:
1.execute(Params... params),执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。
2.onPreExecute(),在execute(Params... params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。
3.doInBackground(Params... params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress... values)来更新进度信息。
4.onProgressUpdate(Progress... values),在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件上。
5.onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。
在使用的时候,有几点需要格外注意:
1.异步任务的实例必须在UI线程中创建。
2.execute(Params... params)方法必须在UI线程中调用。
3.不要手动调用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)这几个方法。
4.不能在doInBackground(Params... params)中更改UI组件的信息。
5.一个任务实例只能执行一次,如果执行第二次将会抛出异常。