回调机制
如果说事件监听机制是一种委托式的事件处理,那么回调机制则与之相反,对于基于回调的事件处理模型来说,事件源与事件监听器是统一的,换种方法说事件监听器完全消失了,当用户在GUI组件上激发某个事件时,组件自己特定的方法将会负责处理该事件。
使用回调机制类处理GUI组件上所发生的事件时,我们需要为该组件提供所对应的事件处理方法--该事件处理方法通常都是系统预先定义好的,因此通常需要继承GUI组件类,并通过重写该类的处理方法来实现。
为了实现回调机制的事件处理,Android为所有GUI组件都提供了一些事件处理的回调方法,以View为例,该类有如下方法:
- boolean onKeyDown(int keyCode,KeyEvent event);当用户在该组件按下某个按键时触发该方法。
- boolean onKeyLongPress(int keyCode,KeyEvent event);当用户在该组件长按下某个按键时触发该方法。
- boolean onKeyShorcut(int keyCode,KeyEvent event);当一个键盘快捷键事件发生时触发该方法。
- boolean onKeyUp(int keyCode,KeyEvent event);当用户在该组件上松开某个按键时触发该方法。
- boolean onTouchEvent(MotionEvent event);当用户在该组件触发触摸屏事件时触发该方法。
- boolean onTrackballEvent(MotionEvent event);当用户在该组件上触发轨迹球事件时触发该方法。
如下的程序展示了基于回调的事件处理机制。正如前面所提到的,基于回调的事件处理机制可以通过自定义View来实现,自定义View时重写该View的事件处理方法即可。
MyButton.java
1 public class MyButton extends Button 2 { 3 public MyButton(Context context, AttributeSet set) 4 { 5 super(context, set); 6 } 7 @Override 8 public boolean onTouchEvent(MotionEvent event) 9 10 { 11 super.onTouchEvent(event); 12 Log.v("-com.example-", "the onTouchEvent in MyButton"); 13 // 返回true,表明该事件不会向外传播 14 return true; 15 } 16 }
在上面自定义的MyButton类中,我们重写了Button类的onTouchEvent(MotionEvent event)方法,该方法将会负责处理按钮上的用户触碰事件。
部分界面文件如下。
1 <!-- 使用自定义View时应使用全限定类名 --> 2 <org.crazyit.event.MyButton 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" 5 android:text="单击我" />
运行上面的程序在Logcat有如下显示
基于回调的事件传播
几乎所有基于回调的事件处理方法都有一个Boolean类型的返回值,该返回值用于标识该处理方法能否完全处理该事件。
-
如果处理事件返回true,则表明处理方法已经完全处理该事件,该事件不会传播出去。
- 如果处理事件返回false,则表明处理方法并未完全处理该事件,该事件会传播出去。
对于基于回调的事件传播而言某组件上所发生的事件不仅会激发该组件上的回调方法,也会触发该组件所在Activity的回调方法--只要事件能传播到Activity。
如下的程序示范了Android系统中的事件传播,重写Button类的处理方法时没有阻止事件传播,因此能看到事件从Button传播到Activity的情形。
MyButton.java
1 public class MyButton extends Button 2 { 3 public MyButton(Context context, AttributeSet set) 4 { 5 super(context, set); 6 } 7 @Override 8 public boolean onTouchEvent(MotionEvent event) 9 10 { 11 super.onTouchEvent(event); 12 Log.v("-com.example-", "the onTouchEvent in MyButton"); 13 // 返回false,表明该事件会向外传播 14 return false; // ① 15 } 16 }
上面的MyButton子类重写了onTouchEvent方法,当用户触碰该按钮时将会触发该方法,但由于该方法返回了false,这意味着事件还会继续向外传播。
接下来看下Activity类代码
1 public class MainActivity extends Activity 2 { 3 @Override 4 protected void onCreate(Bundle savedInstanceState) 5 { 6 super.onCreate(savedInstanceState); 7 setContentView(R.layout.activity_main); 8 Button bn = findViewById(R.id.bn); 9 bn.setOnTouchListener((view, event) -> 10 { 11 // 只处理按下键的事件 12 if (event.getAction() == MotionEvent.ACTION_DOWN) { 13 Log.v("--Listener--", "the TouchDown in Listener"); 14 } 15 // 返回false,表明该事件会向外传播 16 return true; 17 }); 18 } 19 // 重写onTouchEvent方法,该方法可监听它所包含的所有组件上的触碰事件 20 @Override 21 public boolean onTouchEvent(MotionEvent event) 22 { 23 super.onTouchEvent(event); 24 Log.v("--Activity--", "the onTouchEvent in Activity"); 25 // 返回false,表明该事件会向外传播 26 return false; 27 } 28 }
运行上面的程序,触碰按钮,可以在Logcat中看的如下的输出。
从上图中不难看出,当该组件发生触碰事件时,Android系统最先触发的应该是组件绑定的监听器,然后再触发组件提供的事件回调方法,最后还会传播到该组件所在的Activity。但如果任何一个事件处理方法返回了true,那么该事件都不会继续向外传播。例如改写监听器代码,返回为true,然后再运行并触碰按钮,那么在Logcat上会看的如下的输出。
注:本文部分内容参考《疯狂Android讲义》一书,侵权必删