Android開(kāi)發(fā)中軟鍵盤(pán)的顯示和隱藏
本篇內(nèi)容通過(guò)操作軟鍵盤(pán)的函數(shù)著手詳細(xì)分析了隱藏或者顯示軟鍵盤(pán)的實(shí)現(xiàn)方法,并且對(duì)其中重要的代碼做了詳細(xì)分析。
一、開(kāi)篇
如果有需要用到輸入的地方,通常會(huì)有需要自動(dòng)彈出或者收起軟鍵盤(pán)的需求。開(kāi)篇明義,本文會(huì)講講彈出和收起軟鍵盤(pán)的一些細(xì)節(jié),最終還會(huì)從源碼進(jìn)行分析。
想要操作軟鍵盤(pán),需要使用到 InputMethodManager ,它是一個(gè)系統(tǒng)服務(wù),可以使用 Context.getSystemService() 獲取到它。而很多關(guān)鍵的邏輯代碼,都是在 InputMethodManagerService 中實(shí)現(xiàn)的。
特別說(shuō)明:本文的所有分析的源碼,都是基于 Android 26 的源碼。
二、操作軟鍵盤(pán)
2.1 InputMethodManager
前面提到,想要操作軟鍵盤(pán),需要使用 InputMethodManager ,它是一個(gè)系統(tǒng)服務(wù),想要獲取它,可以使用 getSystemService() ,獲取到它。

畢竟是系統(tǒng)服務(wù),使用的時(shí)候?yàn)榱税踩?,還是要判空,避免空指針。
2.2 顯示軟鍵盤(pán)
在 InputMethodManager 中,有兩個(gè)方法 showSoftInput() 和 showSoftInputFromInputMethod() ,而實(shí)際上,只有 showSoftInput() 是有效的。
它有兩個(gè)重載方法,而通常我們會(huì)使用它的兩個(gè)參數(shù)的方法。

這里我們只需要傳遞兩個(gè)參數(shù)。它首先需要一個(gè) View ,使用軟鍵盤(pán)就是為了輸入,而輸入就需要有接收輸入內(nèi)容的 View ,這里接收輸入的 View ,最好是一個(gè) EditText(但這不是必須的)。
而第二個(gè)參數(shù) flags 就是個(gè)標(biāo)志位,從上面截圖的方法簽名上的文檔上可以看到,它接收 0 或者 SHOW_INPYT_IMPLICIT 兩個(gè)參數(shù),但是實(shí)際上,它有第三個(gè)參數(shù),另外一個(gè)是 SHOW_FORCED。

可以看到 1、2 都是有特殊含義的,實(shí)際上它們并不影響顯示,只是在隱藏的時(shí)候,會(huì)有一些限制,這些后面看源碼的時(shí)候再說(shuō),一般沒(méi)有特別需要的話(huà),我們直接傳遞 0 就好了。
現(xiàn)在,簡(jiǎn)單總結(jié)一下調(diào)用 showSoftInput() 會(huì)生效的關(guān)鍵點(diǎn):
1、第一個(gè)參數(shù),最好是 EditText 或者它的子類(lèi)。
考慮到軟鍵盤(pán)就是為了輸入,EditText 就是一個(gè)接收輸入的控件。而這不是絕對(duì)的,如果不是一個(gè) EditText ,就必須要求這個(gè) View 有兩個(gè)屬性,分別是:android:focusable="true" 和android:focusableInTouchMode="true"。
2、第一個(gè)參數(shù),必須是可獲取焦點(diǎn)的,并且當(dāng)前已經(jīng)獲取到焦點(diǎn)。
EditText 默認(rèn)是允許獲取焦點(diǎn)的,但是假如布局中,存在多個(gè)可獲取焦點(diǎn)的控件,就需要提前讓我們傳遞進(jìn)去的 View 獲取到焦點(diǎn)。獲取焦點(diǎn)可以使用 requestFocus() 方法。
3、布局必須加載完成。
在 onCreate() 中,如果立即調(diào)用 showSoftInput() 是不會(huì)生效的。想要在頁(yè)面一啟動(dòng)的時(shí)候就彈出鍵盤(pán),可以在 Activity 上,設(shè)置 android:windowSoftInputMode 屬性來(lái)完成,或者做一個(gè)延遲加載,View.postDelayed() 也是一個(gè)解決方案。
所以最終,完整的顯示軟鍵盤(pán)的代碼就如下所示了。

2.3 隱藏軟鍵盤(pán)
雖然 showSoftInput() 方法是有效的,但是想要隱藏軟鍵盤(pán),就沒(méi)有提供對(duì)應(yīng)的 hideSoftInput() 方法,但是卻有一個(gè) hideSoftInputFromWindow() 方法,可以用來(lái)隱藏軟鍵盤(pán)。
先來(lái)看看這個(gè)方法的簽名,它同樣有兩個(gè)方法可以調(diào)用。

它接收兩個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè) IBinder ,可以直接傳遞一個(gè) View.getWindowToken() 的 windowToken 對(duì)象就可以了。而第二個(gè)參數(shù),就是隱藏軟鍵盤(pán)的標(biāo)志位,如果沒(méi)有特殊要求的話(huà),直接傳遞 0 就好了。
注意這里雖然原則上需要傳遞一個(gè)之前彈出鍵盤(pán)傳遞的時(shí)候,傳遞的 View 的 windowToken ,但是實(shí)際情況是你只需要傳遞一個(gè)存在于當(dāng)前布局 ViewTree 中,隨意一個(gè) View 的 windowToken 就可以了。
最終隱藏軟件的代碼就是這樣的。

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

該方法,接收兩個(gè) flags ,分別是控制 show 和 hide 時(shí)候的標(biāo)識(shí),它們的含義和前面介紹的 showSoftInput() 和 hideSoftInputFromWindow() 一致,所以沒(méi)有特殊要求,直接傳遞 0 就好了。
toggleSoftInput() 方法不要求傳遞一個(gè) View 或者 windowToken,所以它并沒(méi)有 showSoftInput() 中的一些限制,但是依然還有需要在布局繪制完成之后調(diào)用才會(huì)有效果。

雖然這個(gè)方法,限制很少,但是我們基本上不會(huì)使用它。主要原因在于,它是一個(gè)開(kāi)關(guān)的方法,會(huì)根據(jù)當(dāng)前的狀態(tài)做相反的操作。這就導(dǎo)致很多時(shí)候,我們?cè)诖a中,無(wú)法直接根據(jù) InputMethodManager 提供的方法判斷當(dāng)前軟鍵盤(pán)的顯示狀態(tài),這樣也就無(wú)法確定調(diào)用它的時(shí)候的效果了。
三、源碼分析
3.1 flag 的細(xì)節(jié)
前面的一些方法,都需要傳遞一個(gè) flag 值,文檔中描述的并不詳細(xì),我們就從源碼的角度,來(lái)分析一下這些 flag 的含義。
先來(lái)看看 showSoftInput() 方法。

它最終會(huì)調(diào)用 mService.showSoftInput() 方法,最終的源碼,就需要查看 InputMethodManagerService 中的代碼了。而 showSoftInput() 方法,最終會(huì)調(diào)用 showCurrentInputLocked()。
可以看到,flag 會(huì)影響兩個(gè)字段,mShowExplicitlyRequested 和 mShowForced,而 SHOW_FORCED 會(huì)更強(qiáng)勢(shì)一點(diǎn)。
hideSoftInputFromWindow() 方法,最終也會(huì)調(diào)用 InputMethodManagerService 中的 hideCurrentInputLocked() 方法。

從 DEBUG == true 會(huì)輸出的 Log 中,已經(jīng)可以看到含義了。這里會(huì)根據(jù)顯示和隱藏傳遞的兩個(gè) flag 來(lái)進(jìn)行比對(duì),也就是說(shuō),如果 flag 使用不正確,可能導(dǎo)致這里直接返回 false ,從而無(wú)法隱藏軟鍵盤(pán),這些細(xì)節(jié)對(duì)照代碼就清晰了,就不在文章里屢這些細(xì)節(jié)了。
所以這就是為什么前面提到,如果沒(méi)有特殊要求,直接傳遞 0 就好了,可以規(guī)避這個(gè)限制。
3.2 如何判斷軟鍵盤(pán)是否彈出
既然 toggleSoftInput() 可以根據(jù)當(dāng)前軟鍵盤(pán)的狀態(tài),進(jìn)行不同的操作,那么肯定是有辦法確定當(dāng)前軟鍵盤(pán)的狀態(tài)的。
那我們繼續(xù)追蹤 toggleSoftInput() 的方法源碼。
該方法,最終會(huì)調(diào)用到 InputMethodService 的 onToggleSoftInput() 方法。

在這個(gè)方法中,是根據(jù) isInputViewShow() 方法來(lái)判定當(dāng)前軟鍵盤(pán)是否處于顯示彈出的狀態(tài)。但是我們并沒(méi)有辦法,直接和 InputMethodService 進(jìn)行交互,我們也就沒(méi)辦法直接拿到當(dāng)前鍵盤(pán)是否顯示。
如果想要監(jiān)聽(tīng)鍵盤(pán)的彈出和收起,可以使用 ViewTreeObserver.OnGlobalLayoutListener 這個(gè)監(jiān)聽(tīng),來(lái)監(jiān)聽(tīng)布局的調(diào)整,從而判斷出鍵盤(pán)的彈出和隱藏。這些細(xì)節(jié)有時(shí)間再聊。
四、KeyboardUtils
既然已經(jīng)清楚了軟鍵盤(pán)的收起和彈出的方法細(xì)節(jié),那我們來(lái)寫(xiě)一個(gè)幫助類(lèi),來(lái)解決這個(gè)問(wèn)題。讓你們拿到就可用。
這里提供一下 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)
}
}
以上就是本文的全部?jī)?nèi)容,如果大家學(xué)習(xí)了以后還有任何不明白的地方,可以在下方的留言區(qū)進(jìn)行討論。
- Android自定義輸入法軟鍵盤(pán)
- android 軟鍵盤(pán)的POPUP布局的問(wèn)題解決
- Android禁止EditText自動(dòng)彈出軟鍵盤(pán)的方法及遇到問(wèn)題
- 解決Android軟鍵盤(pán)彈出覆蓋h5頁(yè)面輸入框問(wèn)題
- 淺談關(guān)于android軟鍵盤(pán)彈出問(wèn)題
- 5種方法完美解決android軟鍵盤(pán)擋住輸入框方法詳解
- Android開(kāi)發(fā)之超實(shí)用的系統(tǒng)管理工具類(lèi)【SD卡,網(wǎng)絡(luò),uri,屏幕,網(wǎng)絡(luò),軟鍵盤(pán),文本,進(jìn)程等】
- Android開(kāi)發(fā)之彈出軟鍵盤(pán)工具類(lèi)簡(jiǎn)單示例
- 詳解Android中獲取軟鍵盤(pán)狀態(tài)和軟鍵盤(pán)高度
- Android 實(shí)現(xiàn)數(shù)字九宮格軟鍵盤(pán)
相關(guān)文章
Android自定義view實(shí)現(xiàn)輸入控件
這篇文章主要為大家詳細(xì)介紹了Android自定義view實(shí)現(xiàn)輸入控件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
淺析Android位置權(quán)限以及數(shù)組尋找索引的坑
這篇文章給大家分享了Android位置權(quán)限以及數(shù)組尋找索引的坑的相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友可以參考學(xué)習(xí)下。2018-07-07
Android仿蘋(píng)果關(guān)機(jī)界面實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了Android仿蘋(píng)果關(guān)機(jī)界面的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
ListView嵌套GridView使用詳解及注意事項(xiàng)
ListView嵌套GridView即ListView的每個(gè)Item中都包含一個(gè)GridView;需要注意的是由于ListView和GridView都是可滑動(dòng)的控件. 所以需要自定義GridView,重寫(xiě)其onMeasure()方法2013-06-06
Android給自定義按鍵添加廣播和通過(guò)廣播給當(dāng)前焦點(diǎn)輸入框賦值
這篇文章主要介紹了Android給自定義按鍵添加廣播和通過(guò)廣播給當(dāng)前焦點(diǎn)輸入框賦值的相關(guān)資料,需要的朋友可以參考下2016-10-10
Android WebView如何判斷是否滾動(dòng)到底部
大家好,本篇文章主要講的是Android WebView如何判斷是否滾動(dòng)到底部,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下2022-01-01
Android SharedPreferences存儲(chǔ)的正確寫(xiě)法
這篇文章主要介紹了Android SharedPreferences存儲(chǔ)的正確寫(xiě)法的相關(guān)資料,需要的朋友可以參考下2017-06-06
Kotlin?協(xié)程的取消機(jī)制詳細(xì)解讀
這篇文章主要為大家介紹了Kotlin?協(xié)程的取消機(jī)制詳細(xì)解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10

