1. 程式人生 > >軟鍵盤顯示隱藏事件監聽

軟鍵盤顯示隱藏事件監聽

軟鍵盤顯示隱藏事件監聽

現在的Android裝置很少才會有硬鍵盤,絕大多數都是軟鍵盤,而SDK和API中卻沒有軟鍵盤隱藏變化的相關事件,沒有直接支援不代表做不到。我們通過其他的方式還是可以做到監聽軟鍵盤顯示與隱藏狀態變化的。
override onKeyPreIme

當EditText獲得焦點時,或者使用者點選時,都會把軟鍵盤彈起來(2.x版本長按MENU也可以強制彈出軟鍵盤)。但是,隱藏軟鍵盤一般都是BACK鍵,或者鍵盤自身提供隱藏的按扭,再有就是用程式碼強制隱藏。對於BACK鍵還是可以處理的,因為這屬於事件(KeyEvent),是能監聽到的。

核心原理

子例化EditText,並覆寫方法onKeyPreIme)。這個方法能在輸入法前面攔掉事件,從而可以做一些事情:

public class KeyPreImeEditText extends EditText {
    public KeyPreImeEditText(Context context) {
        super(context);
    }

    public KeyPreImeEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyPreImeEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            Log.e("keyboard", "onKeyPreIme we got back");
        }
        return super.onKeyPreIme(keyCode, event);
    }
}

優點
簡單粗暴,都是SDK支援的方法和事件,所以不會有相容性等蛋疼的問題。
缺點
這僅在輸入法前攔截到BACK事件,而前面提到BACK僅是能讓軟鍵盤隱藏掉的一個方式而已,所以這個方法是不能夠完全做到監聽軟鍵盤隱藏狀態變化的。這個方法僅適用於想攔截BACK,做一些其他事情的場景。
override根佈局的onMeasure

另外的思路就是觀察軟鍵盤引起的佈局變化,比如軟鍵盤彈起時Activity的整體佈局都會發生變化。
核心原理
子例化Activity的根佈局(比如LinearLayout或者RelativeLayout,然後覆寫其onMeasure)方法,在其中判斷View的當前高度與其本應有的高度,如果當前高度小於本應有的高度,則表明軟鍵盤在:

public class KeyboardAwareLinearLayout extends LinearLayout {
    public KeyboardAwareLinearLayout(Context context) {
        super(context);
    }

    public KeyboardAwareLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyboardAwareLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int proposedHeight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();

        if (actualHeight > proposedHeight) {
            Log.e("keyboard", "guess keyboard is shown");
        } else {
            Log.e("keyboard", "guess keyboard has been hidden");
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}
優點
能夠真正實現對軟鍵盤隱藏和顯示的監聽,也不算複雜。
缺點
這個基於的原理是鍵盤對Activity的佈局產生影響的情況,而這又受到其他條件控制。activity的屬性windowSoftInputMode控制著軟鍵盤與佈局之間的影響關係,對於adjustPan以及全屏模式的adjustResize這種方法就失效了,因為這二種情況軟鍵盤彈起時,佈局是不會發生變化的,二種height值是一樣的,自然無法分辨。
監聽GlobalLayout

與上面的思路差不多,只不是監聽GlobalLayout變化,然後根據佈局高度與螢幕高度之差來判斷。
注意:要記得把註冊的GlobalLayoutListener再反註冊掉。
計算根佈局的高度差
判斷的依據是根佈局與DectorView之間的差值,在正常情況下應該等於status bar高度與action bar高度之和。當軟鍵盤彈起時則會大於此值。
核心原理
private boolean mKeyboardUp;

private void setListenerToRootView() {
        final View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
        rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                final int headerHeight = getActionBarHeight() + getStatusBarHeight();
                int heightDiff = rootView.getRootView().getHeight() - rootView.getHeight();
                if (heightDiff > headerHeight) {
                    Log.e("keyboard", "keyboard is up");
                    if (!mKeyboardUp) {
                        mKeyboardUp = true;
                    }
                } else if (mOpen) {
                    Log.e("keyboard", "keyboard is hidden");
                    mKeyboardUp = false;
                }
            }
        });
    }
這段程式碼需要好好解釋下:
android.R.id.content 通過這個id可以獲得一個View的根佈局,而不必要知道它具體的id。可以參考這個討論。
rootView 這個View是Activity的根佈局,除去了actionbar的部分,是一個FrameLayout,注意這個並不是setContentView中設定的佈局。rootView的第一個子View(rootView.getChildAt(0))就是setContentView()設定的佈局。可以參考這個討論。
優點
不用子例化,不依賴於現有程式碼中的成員,可以直接插入到任何程式碼中。
缺點
本質上這跟上一個方法是一樣的。因此對於adjustPan和全屏的adjustResize二種情況是無效的。針對這二種情況heightDiff不會變化。
計算根佈局的的底部空隙
其實所有的方法都是為了發現軟鍵盤對佈局的影響,從而判斷軟鍵盤的顯示和隱藏。還有一種方法就判斷根佈局的可視區域與螢幕底部的差值,如果這個差大於某個值,可以認定鍵盤彈起了。
核心原理
private boolean isKeyboardShown(View rootView) {
        final int softKeyboardHeight = 100;
        Rect r = new Rect();
        rootView.getWindowVisibleDisplayFrame(r);
        DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
        int heightDiff = rootView.getBottom() - r.bottom;
        return heightDiff > softKeyboardHeight * dm.density;
}
得到的Rect就是根佈局的可視區域,而rootView.bottom是其本應的底部座標值,如果差值大於我們預設的值,就可以認定鍵盤彈起了。這個預設值是鍵盤的高度的最小值。這個rootView實際上就是DectorView,通過任意一個View再getRootView就能獲得。
優點
適用所有情況,包括adjustPan和全屏的adjustResize也能準確判斷出來。
結論
如果真的需要監聽軟鍵盤顯示與隱藏的事件就可以通過上面的提到的最後一種方式來實現,簡單方便且可靠,唯一要注意的就是要反註冊掉所註冊的GlobalLayoutListener.