Activity中KeyEvent的传递
2019独角兽企业重金招聘Python工程师标准>>>
我们先来写个测试应用,主要文件如下:
MainActivity.java
package com.test.keyevent;import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {Log.d("KEYEVENT", "MainActivity:onKeyDown");return super.onKeyDown(keyCode, event);}@Overridepublic boolean onKeyUp(int keyCode, KeyEvent event) {Log.d("KEYEVENT", "MainActivity:onKeyUp");return super.onKeyUp(keyCode, event);}@Overridepublic void onUserInteraction() {Log.d("KEYEVENT", "MainActivity:onUserInteraction");super.onUserInteraction();}@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {Log.d("KEYEVENT", "MainActivity:dispatchKeyEvent");return super.dispatchKeyEvent(event);}}
2. MyFrameLayout.java
package com.test.keyevent;import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.FrameLayout;public class MyFrameLayout extends FrameLayout {public MyFrameLayout(Context context) {super(context, null);}public MyFrameLayout(Context context, AttributeSet attrs) {super(context, attrs, 0);}public MyFrameLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {Log.d("KEYEVENT", "MyFrameLayout:dispatchKeyEvent");return super.dispatchKeyEvent(event);}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {// TODO Auto-generated method stubLog.d("KEYEVENT", "MyFrameLayout:onKeyDown");return super.onKeyDown(keyCode, event);}@Overridepublic boolean onKeyUp(int keyCode, KeyEvent event) {// TODO Auto-generated method stubLog.d("KEYEVENT", "MyFrameLayout:onKeyUp");return super.onKeyUp(keyCode, event);}}
3. MyEditText.java
package com.test.keyevent;import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.EditText;public class MyEditText extends EditText {public MyEditText(Context context) {super(context, null);// TODO Auto-generated constructor stub}public MyEditText(Context context, AttributeSet attrs) {super(context, attrs, 0);// TODO Auto-generated constructor stub}public MyEditText(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// TODO Auto-generated constructor stub}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {Log.d("KEYEVENT", "MyEditText:onKeyDown");return super.onKeyDown(keyCode, event);}@Overridepublic boolean onKeyUp(int keyCode, KeyEvent event) {Log.d("KEYEVENT", "MyEditText:onKeyUp");return super.onKeyUp(keyCode, event);}@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {Log.d("KEYEVENT", "MyEditText:dispatchKeyEvent");return super.dispatchKeyEvent(event);}}
4. activity_main.xml
<com.test.keyevent.MyFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/black"><com.test.keyevent.MyEditTextandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:background="@android:color/white"android:textColor="@android:color/black"android:hint="请输入..." />
</com.test.keyevent.MyFrameLayout>
安装并打开应用。
在adb shell中,输入input keyevent 29 往系统注入一个KeyEvent(29是a的键值,可以在系统源码KeyEvent.java中查到),我们得到如下日志:
03-02 20:18:48.991: D/KEYEVENT(5840): MainActivity:dispatchKeyEvent
03-02 20:18:48.991: D/KEYEVENT(5840): MainActivity:onUserInteraction
03-02 20:18:48.991: D/KEYEVENT(5840): MyFrameLayout:dispatchKeyEvent
03-02 20:18:48.991: D/KEYEVENT(5840): MyEditText:dispatchKeyEvent
03-02 20:18:48.992: D/KEYEVENT(5840): MyEditText:onKeyDown
03-02 20:18:49.040: D/KEYEVENT(5840): MainActivity:dispatchKeyEvent
03-02 20:18:49.040: D/KEYEVENT(5840): MainActivity:onUserInteraction
03-02 20:18:49.040: D/KEYEVENT(5840): MyFrameLayout:dispatchKeyEvent
03-02 20:18:49.040: D/KEYEVENT(5840): MyEditText:dispatchKeyEvent
03-02 20:18:49.040: D/KEYEVENT(5840): MyEditText:onKeyUp
03-02 20:18:49.040: D/KEYEVENT(5840): MainActivity:onKeyUp
下面我们一步一步分析。
1. MainActivity:dispatchKeyEvent
首先被调用的是MainActivity的dispatchKeyEvent的函数。KeyEvent是如何传递到Acitivity中的,这是比较复杂的,可以参看http://blog.csdn.net/luoshengyang/article/details/6882903这篇博文。但要注意,这篇文章是更具android2.3来写的,对于我们常见的4.2以上的系统,经过我分析比较,是有一些不同的。
4.2系统中,native层InputManager不是在windowManagerService中创建的,而是在SystemServer中创建了个InputManagerService,并在里面创建初始化了InputManager。InputManager的启动也是由SystemServer来启动的。并且InputReaderThread的loopOnce()支持一次读取多条event,优化了性能。还有很多其他的不同的地方,但是大体逻辑上还是一样的。
这块好应用开发关系不太大,就不细说了。最终,Activity的ViewRootImpl的deliverKeyEventPostIme方法中会调用 DecorView的dispatchKeyEvent方法,而DecorView的dispatchKeyEvent方法,可以看下源码,它回调了Activity的 dispatchKeyEvent方法,于是,我们看到了这行日志。
2.MainActivity:onUserInteraction
我们看Activity的dispatchKeyEvent方法,
public boolean dispatchKeyEvent(KeyEvent event) {onUserInteraction();Window win = getWindow();if (win.superDispatchKeyEvent(event)) {return true;}View decor = mDecor;if (decor == null) decor = win.getDecorView();return event.dispatch(this, decor != null? decor.getKeyDispatcherState() : null, this);}
里面调用了onUserInteraction方法。我们可以覆写这个方法,这样就可以在KeyEvent被派发之前,做一些操作,默认是什么都不做。
3. MyFrameLayout:dispatchKeyEvent
从日志看出,事件从Activity又回到了我们的View中。为什么要从Activity中走一下,这样给Activity一个机会,可以控制KeyEvent的派发。
如何回去的,我们接着看Acitivity.dispatchKeyEvent方法
Window win = getWindow();if (win.superDispatchKeyEvent(event)) {return true;}
win就是Activity的mWindow对象,是一个PhoneWindow。我们看PhoneWindow的superDispatchKeyEvent(KeyEvent event)方法:
public boolean superDispatchKeyEvent(KeyEvent event) {return mDecor.superDispatchKeyEvent(event);}
mDecor就是我们熟悉的DecorView对象啦,看它的superDispatchKeyEvent:
public boolean superDispatchKeyEvent(KeyEvent event) {if (super.dispatchKeyEvent(event)) {return true;}......}
因为DecorView是FrameLayout子类,FrameLayout是ViewGroup的子类,所以super.dispatchKeyEvent(event)会调用到ViewGroup的dispatchKeyEvent方法:
public boolean dispatchKeyEvent(KeyEvent event) {if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onKeyEvent(event, 1);}if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {// 如果自己有焦点,则调用父类View的dispatchKeyEventif (super.dispatchKeyEvent(event)) {return true;}} else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)== PFLAG_HAS_BOUNDS) {// 子视图有焦点,则调用子视图if (mFocused.dispatchKeyEvent(event)) {return true;}}if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);}return false;}
焦点在MyEditText上,而MyEditText包含于MyFrameLayout中,所以最终mFocused.dispatchKeyEvent(event)会调用到MyFrameLayout.dispatchKeyEvent方法。
4. MyEditText:dispatchKeyEvent
MyFrameLayout也是FrameLayout子类,所以MyFrameLayout.dispatchKeyEvent和上面一样,会调用ViewGroup的dispatchKeyEvent。这次mFocused就是MyEditText,调用它的dispatchKeyEvent方法。
5.MyEditText:onKeyDown
MyEditText是View子类,会调用View的dispatchKeyEvent方法:
public boolean dispatchKeyEvent(KeyEvent event) {......if (event.dispatch(this, mAttachInfo != null? mAttachInfo.mKeyDispatchState : null, this)) {return true;}......return false;}
View.dispatchKeyEvent会调用KeyEvent的dispatch方法:
public final boolean dispatch(Callback receiver, DispatcherState state,Object target) {switch (mAction) {case ACTION_DOWN: {......boolean res = receiver.onKeyDown(mKeyCode, this);......}}
receiver就是之前的View,也就是MyEditText,也就回调了MyEditText的onKeyDown方法。
至此,ACTION_DOWN的KeyEvent分析完毕。
ACTION_UP的KeyEvent和Down的基本一样,只是应为View的onKeyUp方法返回了false,所以最终会调用到Acitivity的onKeyUp方法,所以才会有最后一条日志的输出。
转载于:https://my.oschina.net/u/262208/blog/381671
Activity中KeyEvent的传递相关推荐
- 【Android 事件分发】事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )
Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...
- Android中Activity之间的数据传递(Intent和Bundle)
当一个Activity启动另一个Activity时,常常会有一些数据传过去,对于Activity之间的数据交换更简单,因为两个Activity之间进行数据传递交换更简单,因为两个Activity之间本 ...
- ANDROID中ACTIVITY间的数据传递
效果:有两个Activity分别为A和B,从A中采用Bundle封装数据向B中传递数据,然后使用startActivityForResult在B中修改后回传数据. 第一个Activity的layout ...
- 【转】android之在activity中控制另一个activity的UI更新_如何在activity之间传递handler...
来自:http://blog.csdn.net/jason0539/article/details/18055259 遇到一个问题,需要在一个activity中控制另一个acitivity做一些更新, ...
- Android 中activity中传递数据的方式
方式一 通过Intent进行数据传递(日后更新) 方式二 通过剪切板进行数据的传递 首先在第一个Activity中,设置所要进行传递的数据 /*** 通过剪贴板传递字符串数据的操作**/private ...
- Android中的两个Activity用Intent来传递java bean实体
先定义java bean实体: public class MC_bean implements Serializable {private int code;private String messag ...
- 点击事件如何传递到Activity中
1.首先,当我们触摸屏幕时,通过Android消息机制,从Looper从MessageQueue中取出该事件,发送给WindowInputEventReceiver. 2.WindowInputEve ...
- android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...
转载:http://blog.csdn.net/xiabing082/article/details/48781489 1. 大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...
- Android中事件的传递
Android中事件的传递 首先来张图看看 1. 核心方法返回值的含义 dispatchTrackballEvent 分发事件 public boolean dispatchTrackballE ...
最新文章
- 致被套基民:老基民的四点教训七点经验(ZT)
- Windows下编辑的(脚本)文本copy到linux下带个^M结尾
- 概念模型让产品更简单
- 01-MyBatis入门程序
- javafx响应式布局_JavaFX的响应式设计
- 如何获取url中的参数并传递给iframe中的报表
- mysql安装数据自定义_mysql数据库自定义怎么安装
- 云小课 | IPv4枯了,IPv6来了
- Maven 国内镜像
- Datagrid,DataList,Repeate等的数据格式设置表达式
- 大数据存储峰会4月9日深圳开幕
- Springboot启动报错:Failed to process import candidates for configuration class...
- JavaScript 计算标准体重的公式
- U3D常用介绍,搭建一个简单的三维效果
- 1038: 绝对值最大
- SEO基础知识简介(一)
- 使用VS2012进行单元测试
- 11家共享单车可通过支付宝免押骑车,这种省钱的方法你get了吗?
- 跟随小米等手机厂商的 IoT 步伐,OPPO 推出子品牌“智美心品”
- Fiddler 抓包工具总结(转)