天天看點

Android: 實作類似QQ、微信的表情輸入鍵盤

需求

最近在寫北郵人論壇用戶端時,有一個需求是實作像手機QQ、微信那樣的表情輸入鍵盤,效果圖:

Android: 實作類似QQ、微信的表情輸入鍵盤

表情鍵盤本身并不難做,無非就是一個帶SlidingTab的ViewPager,困擾我的地方在于,如何正确處理系統軟鍵盤與表情鍵盤之間的顯隐關系。

Google了一下,大概有這麼幾種思路:

第一種:動态改變SoftInputMode

這篇博文是國内網上轉載比較多的方法,軟鍵盤顯示時将SoftInputMode設定為「stateVisible|adjustResize」,表情鍵盤顯示時調整為「adjustPan」。

但在我實際使用過程中效果并不理想,一是我需要在一個ListView的底部實作表情鍵盤,這樣動态更改SoftInputMode會導緻ListView上下跳動;二是切換到别的界面再切換回來時軟鍵盤的顯隐狀态偶爾會有沖突,最終我放棄了這種方法。

第二種:Dialog

Emoticons-Keyboard,這個項目的實作方法是直接在軟鍵盤上覆寫顯示一個Dialog,避開了大部分的顯示邏輯操作,思路非常獨特,可惜我編譯運作後發現顯示效果并不好,除了動畫效果,最大的問題仍然是是從别的界面切換過來時,與軟鍵盤的顯示有沖突

基本思路

上面提到的兩個項目給了我很大的啟發,我反複嘗試了微信、微網誌、手機QQ等應用的表情鍵盤邏輯,發現它們切換鍵盤并不會導緻ListView跳動,如果沒有别的什麼黑科技的話,基本可以斷定使用的SoftInputMode就是adjustPan。(SoftInputMode各個屬性值的意義)

既然是adjustPan就好說了,軟鍵盤顯示的時候不會導緻ListView跳動,那麼Activity的底部必然有一個跟軟鍵盤相同高度的View被軟鍵盤覆寫了,這個View其實就是表情輸入鍵盤,這樣點選表情按鈕的時候隻需要顯示隐藏軟鍵盤,背後的表情框就顯示出來了。

思路有了,接下來就是梳理一下所需要的技術點:

  • 如何檢測軟鍵盤高度(用于動态設定表情鍵盤的高度)?
  • 在代碼中如何手動顯示/隐藏軟鍵盤?
  • 如何防止從别的界面切換過來時,軟鍵盤狀态改變了有可能導緻的顯示沖突?

如果這三個問題解決了,需求就基本實作了。

檢測軟鍵盤的高度

直接上代碼:

private int getSupportSoftInputHeight() {
    Rect r = new Rect();
    mActivity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
    int screenHeight = mContext.getWindow().getDecorView().getRootView().getHeight();
    int softInputHeight = screenHeight - r.bottom;
    if (Build.VERSION.SDK_INT >= 20) {
        // When SDK Level >= 20 (Android L), the softInputHeight will contain the height of softButtonsBar (if has)
        softInputHeight = softInputHeight - getSoftButtonsBarHeight();
    }
    return softInputHeight;
}

           

這裡的原理是通過目前的Activity拿到其RootView的高度,減去Activity本身實際的高度,就等于軟鍵盤的高度了。但在實際應用過程中發現,某些Android版本下,沒有顯示軟鍵盤時減出來的高度總是144,而不是零,經過反複研究,最後發現這個高度是包括了虛拟按鍵欄的,是以在API Level高于18時,我們需要減去底部虛拟按鍵欄的高度(如果有的話)。

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private int getSoftButtonsBarHeight() {
    DisplayMetrics metrics = new DisplayMetrics();
    mContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
    int usableHeight = metrics.heightPixels;
    mContext.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
    int realHeight = metrics.heightPixels;
    if (realHeight > usableHeight) {
        return realHeight - usableHeight;
    } else {
        return ;
    }
}
           

将高度設定給表情鍵盤就比較簡單了:

LinearLayout.LayoutParams linearParams = (LinearLayout.LayoutParams) mEmotionLayout.getLayoutParams();
linearParams.height = getSupportSoftInputHeight();
           

在代碼中手動顯示、隐藏軟鍵盤

也是直接上代碼了,這兩個方法也比較容易查到:

private void showSoftInput() {
    mInputManager.showSoftInput(mEditText, );
}

private void hideSoftInput() {
    mInputManager.hideSoftInputFromWindow(mEditText.getWindowToken(), );
}
           

解決切換程式時的顯示沖突

在預設狀态(StateUnspecified)下,在程式内打開軟鍵盤然後點選Home鍵或多任務鍵切換出去時,軟鍵盤會收起。再次進入程式界面也不會打開,前文提到的兩個項目就是在這種情況下會出現問題。如何保證軟鍵盤和表情鍵盤的同步,直覺反應就是監聽軟鍵盤的高度變化,查了一下,果然可以監聽:

mEditText.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        int softInputHeight = getSupportSoftInputHeight();
        if (softInputHeight != lastSoftInputHeight) {
            // do Something
        }
    }
});
           

實際測試中,這個函數在運作時會調用很多次,我們隻需要在高度變化時做處理即可。

Android: 實作類似QQ、微信的表情輸入鍵盤

如上圖,一共有三種狀态,表情鍵盤的狀态分别為:gone、invisible和visible。分别判斷這三個狀态之間的轉化關系,然後動态的設定Visiblity即可:

public void onGlobalLayout() {
    int softInputHeight = getSupportSoftInputHeight();
    if (softInputHeight != lastSoftInputHeight) {
        if (softInputHeight <= 0) {
            lastSoftInputHeight = softInputHeight;
            if (!notHideEmojiLayout) {
                mEmotionLayout.setVisibility(View.GONE);
            } else {
                notHideEmojiLayout = false;
            }
        } else {
            lastSoftInputHeight = softInputHeight;
            LinearLayout.LayoutParams linearParams = (LinearLayout.LayoutParams) mEmotionLayout.getLayoutParams();
            linearParams.height = softInputHeight;
            mEmotionLayout.setVisibility(View.INVISIBLE);
            if (linearParams.height == softInputHeight) {
                mEmotionLayout.setVisibility(View.INVISIBLE);
            } else {
                linearParams.height = softInputHeight;
            }

            sp.edit().putInt(SHARE_PREFERENCE_TAG, softInputHeight).apply();
        }
    }
}
           

一點小Bug

由于Android裝置的多樣性,軟鍵盤高度不一緻,是以需要動态的設定表情鍵盤的高度,然而程式在第一次軟鍵盤彈出後才能檢測到軟鍵盤高度,但這時由于表情鍵盤高度與軟鍵盤不一緻,會導緻顯示有點異常。是以程式會将檢測到的高度儲存到SharedPreference中,在Activity加載時讀出高度即可。

不過即使是這樣,在整個程式第一次進入這個界面時還是會顯示異常,暫時的解決辦法是在其他軟鍵盤彈出的頁面檢測一次軟鍵盤高度

如果你有更好的辦法,請留言交流~

完整代碼

本文的完整的代碼在我的Github上:Android-EmotionInputDetector,支援Gradle調用,喜歡的話不妨給個Star~

本文首發http://www.dss886.com,轉載請注明