Android 軟鍵盤的顯示和隱藏,這樣操做就對了

1、前言

若是有須要用到輸入的地方,一般會有須要自動彈出或者收起軟鍵盤的需求。開篇明義,本文會講講彈出和收起軟鍵盤的一些細節,最終還會從源碼進行分析。android

想要操做軟鍵盤,須要使用到 InputMethodManager ,它是一個系統服務,可使用 Context.getSystemService() 獲取到它。而不少關鍵的邏輯代碼,都是在 InputMethodManagerService 中實現的。算法

特別說明:本文的全部分析的源碼,都是基於 Android 26 的源碼。c#

2、操做軟鍵盤

2.1 InputMethodManager

前面提到,想要操做軟鍵盤,須要使用 InputMethodManager ,它是一個系統服務,想要獲取它,可使用 getSystemService() ,獲取到它。設計模式

get-server

畢竟是系統服務,使用的時候爲了安全,仍是要判空,避免空指針。安全

2.2 顯示軟鍵盤

在 InputMethodManager 中,有兩個方法 showSoftInput()showSoftInputFromInputMethod() ,而實際上,只有 showSoftInput() 是有效的。ide

它有兩個重載方法,而一般咱們會使用它的兩個參數的方法。源碼分析

showsoftinput2

這裏咱們只須要傳遞兩個參數。它首先須要一個 View ,使用軟鍵盤就是爲了輸入,而輸入就須要有接收輸入內容的 View ,這裏接收輸入的 View ,最好是一個 EditText(但這不是必須的)。佈局

而第二個參數 flags 就是個標誌位,從上面截圖的方法簽名上的文檔上能夠看到,它接收 0 或者 SHOW_INPYT_IMPLICIT 兩個參數,可是實際上,它有第三個參數,另一個是 SHOW_FORCED。post

show-flag

能夠看到 一、2 都是有特殊含義的,實際上它們並不影響顯示,只是在隱藏的時候,會有一些限制,這些後面看源碼的時候再說,通常沒有特別須要的話,咱們直接傳遞 0 就行了。學習

如今,簡單總結一下調用 showSoftInput() 會生效的關鍵點:

一、第一個參數,最好是 EditText 或者它的子類。

考慮到軟鍵盤就是爲了輸入,EditText 就是一個接收輸入的控件。而這不是絕對的,若是不是一個 EditText ,就必需要求這個 View 有兩個屬性,分別是:android:focusable="true"android:focusableInTouchMode="true"

二、第一個參數,必須是可獲取焦點的,而且當前已經獲取到焦點。

EditText 默認是容許獲取焦點的,可是假如佈局中,存在多個可獲取焦點的控件,就須要提早讓咱們傳遞進去的 View 獲取到焦點。獲取焦點可使用 requestFocus() 方法。

三、佈局必須加載完成。

onCreate() 中,若是當即調用 showSoftInput() 是不會生效的。想要在頁面一啓動的時候就彈出鍵盤,能夠在 Activity 上,設置 android:windowSoftInputMode 屬性來完成,或者作一個延遲加載,View.postDelayed() 也是一個解決方案。

因此最終,完整的顯示軟鍵盤的代碼就以下所示了。

static-show

2.3 隱藏軟鍵盤

雖然 showSoftInput() 方法是有效的,可是想要隱藏軟鍵盤,就沒有提供對應的 hideSoftInput() 方法,可是卻有一個 hideSoftInputFromWindow() 方法,能夠用來隱藏軟鍵盤。

先來看看這個方法的簽名,它一樣有兩個方法能夠調用。

hide-softmethod

它接收兩個參數,第一個參數是一個 IBinder ,能夠直接傳遞一個 View.getWindowToken() 的 windowToken 對象就能夠了。而第二個參數,就是隱藏軟鍵盤的標誌位,若是沒有特殊要求的話,直接傳遞 0 就行了。

注意這裏雖然原則上須要傳遞一個以前彈出鍵盤傳遞的時候,傳遞的 View 的 windowToken ,可是實際狀況是你只須要傳遞一個存在於當前佈局 ViewTree 中,隨意一個 View 的 windowToken 就能夠了。

最終隱藏軟件的代碼就是這樣的。

sttaic-hide

2.4 切換鍵盤的彈出和隱藏

在 InputMethodManager 中,還提供了一個 toggleSoftInput() 方法,如同它的名字同樣,它可讓軟鍵盤在顯示和隱藏之間切換。

toggle-method

該方法,接收兩個 flags ,分別是控制 show 和 hide 時候的標識,它們的含義和前面介紹的 showSoftInput()hideSoftInputFromWindow() 一致,因此沒有特殊要求,直接傳遞 0 就行了。

toggleSoftInput() 方法不要求傳遞一個 View 或者 windowToken,因此它並無 showSoftInput() 中的一些限制,可是依然還有須要在佈局繪製完成以後調用纔會有效果。

static-toggle

雖然這個方法,限制不多,可是咱們基本上不會使用它。主要緣由在於,它是一個開關的方法,會根據當前的狀態作相反的操做。這就致使不少時候,咱們在代碼中,沒法直接根據 InputMethodManager 提供的方法判斷當前軟鍵盤的顯示狀態,這樣也就沒法肯定調用它的時候的效果了。

3、源碼分析

3.1 flag 的細節

前面的一些方法,都須要傳遞一個 flag 值,文檔中描述的並不詳細,咱們就從源碼的角度,來分析一下這些 flag 的含義。

先來看看 showSoftInput() 方法。

showSoftInput

它最終會調用 mService.showSoftInput() 方法,最終的源碼,就須要查看 InputMethodManagerService 中的代碼了。而 showSoftInput() 方法,最終會調用 showCurrentInputLocked()

這個方法的代碼很長,咱們只關心和 flag 相關的代碼。

code-show-current

能夠看到,flag 會影響兩個字段,mShowExplicitlyRequested 和 mShowForced,而 SHOW_FORCED 會更強勢一點。

hideSoftInputFromWindow() 方法,最終也會調用 InputMethodManagerService 中的 hideCurrentInputLocked() 方法。

code-hide

DEBUG == true 會輸出的 Log 中,已經能夠看到含義了。這裏會根據顯示和隱藏傳遞的兩個 flag 來進行比對,也就是說,若是 flag 使用不正確,可能致使這裏直接返回 false ,從而沒法隱藏軟鍵盤,這些細節對照代碼就清晰了,就不在文章裏屢這些細節了。

因此這就是爲何前面提到,若是沒有特殊要求,直接傳遞 0 就行了,能夠規避這個限制。

3.2 如何判斷軟鍵盤是否彈出

既然 toggleSoftInput() 能夠根據當前軟鍵盤的狀態,進行不一樣的操做,那麼確定是有辦法肯定當前軟鍵盤的狀態的。

那咱們繼續追蹤 toggleSoftInput() 的方法源碼。

該方法,最終會調用到 InputMethodService 的 onToggleSoftInput() 方法。

toggle-soft

在這個方法中,是根據 isInputViewShow() 方法來斷定當前軟鍵盤是否處於顯示彈出的狀態。可是咱們並無辦法,直接和 InputMethodService 進行交互,咱們也就沒辦法直接拿到當前鍵盤是否顯示。

若是想要監聽鍵盤的彈出和收起,可使用 ViewTreeObserver.OnGlobalLayoutListener 這個監聽,來監聽佈局的調整,從而判斷出鍵盤的彈出和隱藏。這些細節有時間再聊。

4、KeyboardUtils

既然已經清楚了軟鍵盤的收起和彈出的方法細節,那咱們來寫一個幫助類,來解決這個問題。讓大家拿到就可用。

這裏提供一下 Java 版和 Kotlin 版。

4.1 Java 版

public class KeyboardUtils {

    public static void showKeyboard(View view) {
        InputMethodManager imm = (InputMethodManager) view.getContext()
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null) {
            view.requestFocus();
            imm.showSoftInput(view, 0);
        }
    }

    public static void hideKeyboard(View view){
        InputMethodManager imm = (InputMethodManager) view.getContext()
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null) {
            imm.hideSoftInputFromWindow(view.getWindowToken(),0);
        }
    }
    public static void toggleSoftInput(View view){
        InputMethodManager imm = (InputMethodManager) view.getContext()
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null) {
            imm.toggleSoftInput(0,0);
        }
    }
}
複製代碼

4.2 Kotlin 版

object KeyboardktUtils{
    fun showKeyboard(view: View) {
        val imm = view.context
                .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        if (imm != null) {
            view.requestFocus()
            imm.showSoftInput(view, 0)
        }
    }

    fun hideKeyboard(view: View) {
        val imm = view.context
                .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm?.hideSoftInputFromWindow(view.windowToken, 0)
    }

    fun toggleSoftInput(view: View) {
        val imm = view.context
                .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm?.toggleSoftInput(0, 0)
    }
}
複製代碼

今天在承香墨影公衆號的後臺,回覆『成長』。我會送你一些我整理的學習資料,包含:Android反編譯、算法、設計模式、虛擬機、Linux、Kotlin、Python、爬蟲、Web項目源碼。

推薦閱讀:

相關文章
相關標籤/搜索