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

來源:互聯網
上載者:User

標籤:boa   log   otl   監聽   blog   原因   解決方案   ==   contex   

一、前言

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

想要操作軟鍵盤,需要使用到 InputMethodManager ,它是一個系統服務,可以使用 Context.getSystemService() 擷取到它。而很多關鍵的邏輯代碼,都是在 InputMethodManagerService 中實現的。

特別說明:本文的所有分析的源碼,都是基於 Android 26 的源碼。

二、操作軟鍵盤2.1 InputMethodManager

前面提到,想要操作軟鍵盤,需要使用 InputMethodManager ,它是一個系統服務,想要擷取它,可以使用 getSystemService() ,擷取到它。

畢竟是系統服務,使用的時候為了安全,還是要判空,避免null 指標。

2.2 顯示軟鍵盤

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

它有兩個重載方法,而通常我們會使用它的兩個參數的方法。

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

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

可以看到 1、2 都是有特殊含義的,實際上它們並不影響顯示,只是在隱藏的時候,會有一些限制,這些後面看源碼的時候再說,一般沒有特別需要的話,我們直接傳遞 0 就好了。

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

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

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

2、第一個參數,必須是可擷取焦點的,並且當前已經擷取到焦點。

EditText 預設是允許擷取焦點的,但是假如布局中,存在多個可擷取焦點的控制項,就需要提前讓我們傳遞進去的 View 擷取到焦點。擷取焦點可以使用 requestFocus() 方法。

3、布局必須載入完成。

onCreate() 中,如果立即調用 showSoftInput() 是不會生效的。想要在頁面一啟動的時候就彈出鍵盤,可以在 Activity 上,設定 android:windowSoftInputMode 屬性來完成,或者做一個消極式載入,View.postDelayed() 也是一個解決方案。

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

2.3 隱藏軟鍵盤

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

先來看看這個方法的簽名,它同樣有兩個方法可以調用。

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

注意這裡雖然原則上需要傳遞一個之前彈出鍵盤傳遞的時候,傳遞的 View 的 windowToken ,但是實際情況是你只需要傳遞一個存在於當前布局 ViewTree 中,隨意一個 View 的 windowToken 就可以了。

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

2.4 切換鍵盤的彈出和隱藏

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

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

toggleSoftInput() 方法不要求傳遞一個 View 或者 windowToken,所以它並沒有 showSoftInput() 中的一些限制,但是依然還有需要在布局繪製完成之後調用才會有效果。

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

三、源碼分析3.1 flag 的細節

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

先來看看 showSoftInput() 方法。

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

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

可以看到,flag 會影響兩個欄位,mShowExplicitlyRequested 和 mShowForced,而 SHOW_FORCED 會更強勢一點。

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

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

所以這就是為什麼前面提到,如果沒有特殊要求,直接傳遞 0 就好了,可以規避這個限制。

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

既然 toggleSoftInput() 可以根據當前軟鍵盤的狀態,進行不同的操作,那麼肯定是有辦法確定當前軟鍵盤的狀態的。

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

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

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

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

四、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項目源碼。

推薦閱讀:

  • 認真聊聊陰影那些事兒
  • 利用 StateListAnimator 為你的點擊加個動畫吧!
  • 好的代碼可以自己說話
  • 關於如何編寫 Clean Code 的 6 個簡單技巧
  • 手寫你的第一個 Dalvik 版的 HelloWorld !

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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.