Android之事件响应的两种模型分析
Android的事件响应处理
一、android事件响应模型的概述
事件响应对于android这种“页程序”而言相当重要,因为这是与界面编程紧紧相关的知识;就如同Activity提供UI,而事件响应就提供具体的界面操作。
既然理解了其重要性,那么我们就具体来了解下Android的事件响应处理吧。Android的事件响应处理其实相对来说比较简单。参考《疯狂Android讲义》这本书中对Android事件响应的理解,其响应方式一共分为两种:
1、基于监听的事件响应模型
2、基于回调的事件响应模型
这两者其实本质上也是无差别的,只是说具体的监听器不同:一个则是绑定指定监听器,并实现其监听函数;一个是重写了View中已经定义过的监听函数。
那么下面我们就来详细的讲解着两种事件响应。
二、基于监听的事件响应模型
对于Android基于监听的事件处理而言,主要就是android的界面组件绑定特定的事件监听器,然后在监听器中实现具体的代码。
在事件监听的处理模型中,主要涉及的是三类对象。(现在扯着三类对象大家可能觉得弄复杂了;但是请慢慢看,这样说,易于后面更深刻的理解)
*Even Source (事件源)…………就是组件
*Even (事件)…………用户操作
*Even Listener (事件监听器)…………事件监听器
当用户按下一个按钮或者单击一个菜单项的时候,这个动作会激发一个相应的事件,而这个事件的事件源就是该按钮或者菜单项。而按下之后产生的相应的变化,如刷新了界面,弹出下拉框或者弹出个Dialog等都是你事件监听器在发挥作用。
以上的解释相信大家就已经相当了解所谓的事件响应的一系列概念了。那么现在就将这三类对象用具体的图来表现出来。
从这张图我们了解,外部操作产生事件源,事件源产生事件,而事件作为参数传递给了事件处理器也就是事件监听器。然后在监听器中就实现响应的处理方法,一阵套流程就走完了。
了解完事件监听的模型,我们能知道,不同的组件(事件源)产生的事件,是需要不同的事件监听器来实现的。这里事件源(组件)是固定的(至少其父类是固定的,就那么几个:Button,ImageView,TextView啊等……),有固定的组件产生的事件那肯定也是固定的,那么也因此事件监听器也就是固定的。不同的在于实现事件监听器的具体函数可能不同,这就是监听的关键。
那么针对刚刚说明的情况,不同的事件源,产生不同的事件,需要不同的事件监听器来监听,那么以View为例,有哪些具体的事件监听器呢?
*View.OnVClickListener…………单击事件的事件监听器
*View.OnCreateContextMenuListener…………创建上下文菜单的事件监听器
*View.OnFocusChangeListener…………焦点改变的事件监听器
*View.OnKeyListener…………按键的事件监听器
*View.OnLongClickListener…………长单击的事件监听器
*View.OnTouchListener…………触摸屏的事件监听器
以上以View为例,介绍了其不同的几个事件监听器;其实本质上事件监听器就是实现了特定接口的Java类的实例,这样理解更深刻一些。现在我们再了解一下事件监听器的几种形式:
1、内部类形式
2、外部类形式
3、Activity本身作为事件监听器类
4、匿名内部类
5、直接绑定到标签
具体介绍着几种形式吧。
1、内部类形式
内部类形式的监听器可以被多次复用,相对来说编程界面够简洁,易理解。
MyListenermyListener = MyListener();MyButton.setOnClickListener(myListener);MyTextView.setOnClickListener(myListener);……Class MyListener extends View.onClickListener{ onClick(View v) { Switch(V.getid()) Case MyButton: …… Case MyTextView: …… }}
也正是因为这样的原因,所以内部类形式的监听器使用范围比较广泛;大量的内部类被用于android的源码中。
2、外部类的形式
外部类形式的监听器比较少见。“疯狂android讲义”上如此分析原因:
1、 事件监听器通常是属于特定的gui界面,而定义成外部类的形式是不利于提高程序间的耦合性的。
2、 外部类其实就是因语言规定方面的原因,访问gui界面的组件并不方便,需要传入具体的控制的ui控件以方便使用,编程不简洁。
这其中又提出,如果一个事件监听器被多个gui界面所共享,而且完成的任务更强调业务逻辑(也就是说与特定的界面组件关系不大,这种情况不强调具体页面),那么可以考虑使用外部类。
3、Activity本身作为事件监听器
Public class ActivityListener extends Activity implements OnClickListener{……@ OverridePublic void onClick(View v){ 具体实现}……}
参照“疯狂android讲义”将这个作为一种监听器方式,其实这并没有什么值得一说的,因为它是在没有什么特色,而且这样写个人认为将使得程序复杂且不易读。
4、匿名内部类形式
其实匿名内部类的形式使用是比较舒适的,这主要是对比内部类,对比内部类中最有价值的“复用”功能。如果监听器不强调“复用”这个时候匿名内部类是很好的选择。实际上android工程中匿名内部类是使用最广泛的一种监听器形式了。
MyButton.setOnClickListener(newView.OnCickListener{ onClick(View v) { 实现具体的监听响应事件 …… }});
5、直接绑定到标签
直接绑定到标签这种监听器的形式确实非常简洁,直接联系了xml语句和java语句;但是个人认为这样做使得源码不易读,增添了麻烦,而且过多的联系xml与java,这并不是一个明智的做法。
<?xml version="1.0"encoding="utf-8"?>
在上面的xml文件中绑定onclick属性并直接命名“clickBandler”,这个命名很重要,将直接在java源码中实现这个命名的点击方法,然后就可以响应这个Button了。 Public class BindingTag extends Activity{ @Override Publicvoid onCreate (Bundle savedInstanceState) { Super.onCreate(savedInstanceState); setContentView(R.layout.main); } //这就是响应clickBandler标签的函数 publicvoid clickBandler(View source) { //响应的具体操作 ……… }}
简单的说完以上的5中监听方式我们可以知道,其实重点还是内部类和匿名内部类,这才是监听的关键;而其余的只需要做了解就可以了。
三、基于回调的事件处理
回调的事件处理时比较简单的,易于理解;对比监听的事件处理,回调其实就是相当于是将事件源与事件监听器放在了同一个控件中了,这样做个人理解是为了更方便的实现面向对象,规定特定的组件响应特定的操作,将属性归类。
为了实现回调机制的事件处理,android为所有的gui组件都提供了回调方法,这里以View为例来说明:
(View该类的毁掉监听方法)
*boolean onKeyDown(int keyCode , KeyEventevent).用户在该组件上按下时触发的
*boolean onKeyLongPress(int keyCode ,keyEvent event).用户在该组件上长按时候触发的
*boolean onkeyShorcut(int keyCode ,keyEvent event);当一个键盘快捷键时触发该事件
*boolean onkeyUp(int keyCode , keyEventevent)用户松开该按键的时候触发的事件
*boolean onTouchEvent(MotionEvent event).用户在该组件上触发触摸屏时候触发该方法
*boolean onTrackballEvent(MotionEventevent).用户在该组件商触发轨迹球的时候触发该方法
其实解释ui控件的回调函数的事件处理机制主要是为了自定义View控件来铺路的,而自定义控件的响应事件具体就要介绍回调事件传播了,因此我们可以通过回调事件的传播来看自定义View的回调机制。
回调事件传播:具体就是指所有基于回调事件处理方法都有一个boolean类型的返回值,而这个返回值就是代表这个回调函数是否已经完全处理了该事件。
*如果返回为true,则表示已经完全处理了该事件,该事件不会再传播出去;
*如果为false,则表示事件还会再逐层的传播出去
这仅是说是基于回调的事件传播,监听的话貌似没有这方面的考虑。原因在于回调的话由于继承(个人是这样理解的),回调函数会逐层逐层的反馈事件是否已经处理掉了,而判断的依据就是return true or? False!!
这样一层一层的回调反馈。
看看这个示例表达的就是这个意思
public class Propagation extends Activity{ @Override public void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Buttonbn = (Button) findViewById(R.id.bn); // 为bn绑定事件监听器 bn.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View source ,intkeyCode, KeyEvent event) { // 只处理按下键的事件 if (event.getAction() ==KeyEvent.ACTION_DOWN) { Log.v("-Listener-","the onKeyDown in Listener"); } // 返回false,表明该事件会向外传播 return true; // ① } }); } // 重写onKeyDown方法,该方法可监听它所包含的所有组件的按键被按下事件 @Override public boolean onKeyDown(int keyCode, KeyEventevent) { super.onKeyDown(keyCode ,event); Log.v("-Activity-", "the onKeyDown in Activity"); //返回false,表明并未完全处理该事件,该事件依然向外扩散 return false; }
根据这个示例,我们还可以一窥监听事件模型和回调事件模型的响应顺序:通过log我们可以看出来,android系统是先触发绑定的事件监听器,然后触发事件回调方法,之后就传播到了该组件所在的Activity。这就是android事件处理模型的基本顺序。而我们若将其中一个事件处理函数renturn true 。那么后续的响应函数将不再执行。
四、总结
其实我们仔细对比这两种事件处理模型就能看出来,基于监听的事件处理模型是有极大的优势的,主要是逻辑清晰,代码易读,容易维护;而且android的事件处理机制还导致基于监听的事件监听器会被优先触发。通过对比我们就基本完全理解了android的事件监听模型了!
更多相关文章
- Android(安卓)创建与解析XML(六)—— 比较与使用
- Android(安卓)创建与解析XML(六)—— 比较与使用
- android中的后退键——onBackPressed()的使用
- 将软键盘变成搜索
- android inputmanager中事件的传递流程
- android ListView嵌套checkbox并取代checkbox点击事件
- EventBus 《三》 事件的具体用法及其讲解
- Android事件处理分析+Android事件处理 +Android输入事件流程
- Android滑动冲突二内部拦截法详情