7.1.2 Android交互事件传输
交互事件,是指当用户通过按键、触摸、滑动等操作与应用进行交互时触发的相关事件。通过上一小节的介绍可知,在Android控件中,交互事件都是沿着控件树自顶向下传播的。
当位于控件树上层的父控件收到交互事件后,会先行判定该事件的目标控件对象,如果该事件正是自己所需要的,则会截获事件进行处理,否则就尝试将事件向下分发给对应的子控件,并递推地逐级向下传播事件,直至该事件被处理或者忽略。
Android在View类中定义了一系列命名为View.onXXX的事件函数用来接收和处理各类交互事件,比如通过View.onKeyDown函数可以接收到用户的按键操作,使用View.onTouchEvent函数可以获取用户触摸屏幕的相关事件,等等。每个派生自View类的子控件都可以通过重载这些事件函数,来处理该控件所需的事件。
例如,如果一个控件需要处理用户按返回键的操作,则可以通过重载View.onKeyDown函数来实现:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event){
//监听和处理返回操作
if(keyCode==KeyEvent.KEYCODE_BACK){
handleBackEvent();
return true;
}
return false;
}
从这个例子中可以看到,事件函数的返回值是控制事件传播的重要手段。如果事件函数返回true,则说明该控件已经接收并完成了该事件的处理,无须将该事件进一步传递;反之,如果事件函数返回false,则说明该控件对象未能处理该事件(或虽然做过处理,但仍需要进一步处理),需要继续传递以寻找能够处理它的控件对象。
对于容器控件ViewGroup来说,它的一个职责就是将交互事件传播到其子控件中。针对不同的事件,ViewGroup可以选择不同的传播方式。例如,如果是触摸事件,ViewGroup对象需要判定该事件发生的区域位于哪个子控件的“领土”之上,从而将该事件分配给该子控件进行处理;如果是按键事件,ViewGroup则会尝试将它转发给其中具有焦点的子控件,示例代码如下:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event){
//判断是否包含具有焦点的子控件
if(focused!=null){
return focused.onKeyDown(keyCode, event);
}
return false;
}
通过继承的方式来进行事件处理并不够灵活,会导致系统中出现大量的子控件类型,并且各个控件的复用性都较差。在面向对象的设计中,提倡使用“组合”来代替“继承”。基于此思想,View类中提供了一系列配套的事件监听函数供开发者处理对应事件。Android控件的事件监听模型,是典型的观察者模式。开发者可以构造外部观察者对象与控件对象的事件监听接口绑定,获取事件消息。
还是以处理按键事件为例,通过监听者进行处理的实现如下:
//定义按键事件监听对象
final View.OnKeyListener listener=
new View.onKeyListener(){
@Override
public boolean onKey(View view,
int keyCode, KeyEvent event){
//处理返回键事件
if(keyCode==KeyEvent.KEYCODE_BACK){
handleBackEvent();
return true;
}
return true;
}
};
//将该按键事件监听对象,与对应的控件对象viewForListen绑定
viewForListen.setOnKeyListener(listener);
通过组合的方式利用外部对象来处理交互事件,其耦合性低,使每类控件都具有更好的可复用度,无需为了处理事件而构造新的控件。但与重写事件函数不同的是,事件监听对象不能修改原有控件对象对事件处理的实现逻辑。在实际开发中,需要根据具体场景,选择合适的方式进行事件处理。