本文会详细分析Android KeyEvent分发过程,以及相关api的作用
一、背景
Activity和View有三个override的方法dispatchKeyEvent、onKeyUp、onKeyDown,其中View还有一个setOnKeyListener方法。只知道这几个方法是设置处理key event的,但是一直不太理解这几个方法的作用和相互影响。所以准备写个demo彻底搞清楚这几个方法的作用和原理。
二、测试准备
新建三个类
public class KeyDispatchDemo extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_key_dispatch);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.d("Activity", "dispatchKeyEvent: " + event.getAction() + " | " + event.getKeyCode());
return super.dispatchKeyEvent(event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
Log.d("Activity", "onKeyUp: " + keyCode);
return super.onKeyUp(keyCode, event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.d("Activity", "onKeyDown: " + keyCode);
return super.onKeyDown(keyCode, event);
}
}
public class KeyDispatchLinearLayout extends LinearLayout {
public KeyDispatchLinearLayout(Context context) {
super(context);
}
public KeyDispatchLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public KeyDispatchLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.d("ViewGroup", "dispatchKeyEvent: " + event.getAction() + " | " + event.getKeyCode());
return super.dispatchKeyEvent(event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
Log.d("ViewGroup", "onKeyUp: " + keyCode);
return super.onKeyUp(keyCode, event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.d("ViewGroup", "onKeyDown: " + keyCode);
return super.onKeyDown(keyCode, event);
}
}
public class KeyDispatchView extends View {
public KeyDispatchView(Context context) {
super(context);
}
public KeyDispatchView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public KeyDispatchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.d("View", "dispatchKeyEvent: " + event.getAction() + " | " + event.getKeyCode());
return super.dispatchKeyEvent(event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
Log.d("View", "onKeyUp: " + keyCode);
return super.onKeyUp(keyCode, event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.d("View", "onKeyDown: " + keyCode);
return super.onKeyDown(keyCode, event);
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.lhl.common.keyevent.KeyDispatchLinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.lhl.common.keyevent.KeyDispatchView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/blue" android:focusable="true" /> </com.lhl.common.keyevent.KeyDispatchLinearLayout> </LinearLayout>
三、Key event分发流程分析
首先在关键方法上都打上断点,按下键盘,发出一个key event事件。
1、dispatchKeyEvent的执行流程
dispatchKeyEvent的分发主要是:DecorView——>Activity——>ViewGroup——>View
一个Key event是首先传到DecorView中,DecorView通过下面的代码分发给Activity:
if (!mWindow.isDestroyed()) {
final Window.Callback cb = mWindow.getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
if (handled) {
return true;
}
}
其中mWindow.getCallback()就是目标Activity
在Activity的dispatchKeyEvent方法里面,传递给了PhoneWindow的superDispatchKeyEvent方法
PhoneWindow的superDispatchKeyEvent方法会调到ViewGroup的dispatchKeyEvent方法:
ViewGroup会一层一层的把key event传递给持有焦点的child:
总结:在这个dispatchKeyEvent链中,只要有一个返回true,就是没调super.dispatchKeyEvent方法,那么key event的传递就会终止。
2、onKeyDown的执行流程
view.onKeyDown的执行:
和dispatch的流程一致,key event从DecorView分发到View的dispatchKeyEvent
在View.dispatchKeyEvent分发到KeyEvent.dispatch:
最后分发到View.onKeyDown
如果View.onKeyDown没有返回true,那么key event会继续流传给Activity:
最后会传给Activity.onKeyDown
总结:View.onKeyDown如果返回true,表示View消耗了这个事件,那么这个事件不会再流转到Activity
3、onKeyUp的执行流程
onKeyUp的流程和onKeyDown类似,不再累述。
总结:View.onKeyUp如果返回true,表示View消耗了这个事件,那么这个事件不会再流转到Activity。并且onKeyUp的流转和onKeyDown是独立的,不会相互影响。