Activity中KeyEvent的傳遞

咱們先來寫個測試應用,主要文件以下:java

  1. MainActivity.javaandroid

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 {
     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
 
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.d("KEYEVENT", "MainActivity:onKeyDown");
        return super.onKeyDown(keyCode, event);
    }
 
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        Log.d("KEYEVENT", "MainActivity:onKeyUp");
        return super.onKeyUp(keyCode, event);
    }
 
    @Override
    public void onUserInteraction() {
        Log.d("KEYEVENT", "MainActivity:onUserInteraction");
        super.onUserInteraction();
    }
 
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        Log.d("KEYEVENT", "MainActivity:dispatchKeyEvent");
        return super.dispatchKeyEvent(event);
    }
 
 
}

2. MyFrameLayout.javashell

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);
    }
 
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        Log.d("KEYEVENT", "MyFrameLayout:dispatchKeyEvent");
        return super.dispatchKeyEvent(event);
    }
 
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        // TODO Auto-generated method stub
        Log.d("KEYEVENT", "MyFrameLayout:onKeyDown");
        return super.onKeyDown(keyCode, event);
    }
 
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        // TODO Auto-generated method stub
        Log.d("KEYEVENT", "MyFrameLayout:onKeyUp");
        return super.onKeyUp(keyCode, event);
    }
     
}

3. MyEditText.javaapp

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
    }
 
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.d("KEYEVENT", "MyEditText:onKeyDown");
        return super.onKeyDown(keyCode, event);
    }
 
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        Log.d("KEYEVENT", "MyEditText:onKeyUp");
        return super.onKeyUp(keyCode, event);
    }
 
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        Log.d("KEYEVENT", "MyEditText:dispatchKeyEvent");
        return super.dispatchKeyEvent(event);
    }
     
     
     
}

4. activity_main.xmlide

<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.MyEditText
        android: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中查到),咱們獲得以下日誌:oop

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的dispatchKeyEvent
            if (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方法,因此纔會有最後一條日誌的輸出。

相關文章
相關標籤/搜索