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);


通过组合的方式利用外部对象来处理交互事件,其耦合性低,使每类控件都具有更好的可复用度,无需为了处理事件而构造新的控件。但与重写事件函数不同的是,事件监听对象不能修改原有控件对象对事件处理的实现逻辑。在实际开发中,需要根据具体场景,选择合适的方式进行事件处理。