詳解Android中獲取軟鍵盤狀態(tài)和軟鍵盤高度
詳解Android中獲取軟鍵盤狀態(tài)和軟鍵盤高度
應(yīng)用場(chǎng)景
在Android應(yīng)用中有時(shí)會(huì)需要獲取軟鍵盤的狀態(tài)(即軟鍵盤是顯示還是隱藏)和軟鍵盤的高度。這里列舉了一些可能的應(yīng)用場(chǎng)景。
場(chǎng)景一
當(dāng)軟鍵盤顯示時(shí),按下返回鍵應(yīng)當(dāng)是收起軟鍵盤,而不是回退到上一個(gè)界面,但部分機(jī)型在返回鍵處理上有bug,按下返回鍵后,雖然軟鍵盤會(huì)自動(dòng)收起,但不會(huì)消費(fèi)返回事件,導(dǎo)致Activity還會(huì)收到這次返回事件,執(zhí)行回退操作,這時(shí)就需要判斷,如果軟鍵盤剛剛由顯示變?yōu)殡[藏狀態(tài),就不執(zhí)行回退操作。
場(chǎng)景二
當(dāng)軟鍵盤彈出后,會(huì)將界面底部到中間的一大部分全部擋住,如果用戶要查看、操作被覆蓋的區(qū)域,必須先收起軟鍵盤,這會(huì)影響用戶交互。所以通常需要在軟鍵盤彈出后,將底部的一些View,例如Button,移到軟鍵盤的上方,方便用戶操作。
API的困境
Android SDK中沒有提供任何API來直接獲取軟鍵盤的狀態(tài)和軟鍵盤的高度,網(wǎng)上很多資料說InputMethodManager的isActive()方法可以獲取軟鍵盤狀態(tài),不過實(shí)際測(cè)試發(fā)現(xiàn),這個(gè)方法并沒有什么用,如果它返回false,可以判斷軟鍵盤一定是隱藏的,但如果它返回true,軟鍵盤既可能是顯示的,也可能是隱藏的。所以并不能通過isActive()方法來判斷軟鍵盤究竟是顯示還是隱藏的。要想獲取軟鍵盤的狀態(tài)和軟鍵盤的高度,只能通過間接方法實(shí)現(xiàn)。
注冊(cè)布局變化監(jiān)聽
在Android中當(dāng)軟鍵盤由隱藏變?yōu)轱@示,或由顯示變?yōu)殡[藏時(shí),會(huì)觸發(fā)當(dāng)前布局中View的全局布局變化。通過監(jiān)聽全局布局的變化就可以得知軟鍵盤的狀態(tài)。
Android框架提供了一個(gè)ViewTreeObserver類,它是一個(gè)View視圖樹的觀察者類。ViewTreeObserver類中定義了一系列的公共接口(public interface)。當(dāng)一個(gè)View attach到一個(gè)窗口上時(shí)就會(huì)創(chuàng)建一個(gè)ViewTreeObserver對(duì)象,這樣當(dāng)一個(gè)View的視圖樹發(fā)生改變時(shí),就會(huì)調(diào)用該對(duì)象的某個(gè)方法,將事件通知給每個(gè)注冊(cè)的監(jiān)聽者。
OnGlobalLayoutListener是ViewTreeObserver中定義的眾多接口中的一個(gè),它用來監(jiān)聽一個(gè)視圖樹中全局布局的改變或者視圖樹中的某個(gè)視圖的可視狀態(tài)的改變。當(dāng)軟鍵盤由隱藏變?yōu)轱@示,或由顯示變?yōu)殡[藏時(shí),都會(huì)調(diào)用當(dāng)前布局中所有存在的View中的ViewTreeObserver對(duì)象的dispatchOnGlobalLayout()方法,此方法中會(huì)遍歷所有已注冊(cè)的OnGlobalLayoutListener,執(zhí)行相應(yīng)的回調(diào)方法,將全局布局改變的消息通知給每個(gè)注冊(cè)的監(jiān)聽者。
向一個(gè)View中的ViewTreeObserver注冊(cè)O(shè)nGlobalLayoutListener的方法如下。
view.getViewTreeObserver().addOnGlobalLayoutListener(listener);
注冊(cè)O(shè)nGlobalLayoutListener時(shí)有一些需要注意的地方。
- 注冊(cè)的監(jiān)聽在不使用時(shí)需要調(diào)用removeOnGlobalLayoutListener或removeGlobalOnLayoutListener來移除監(jiān)聽,不然可能會(huì)導(dǎo)致內(nèi)存泄露。通??梢栽贏ctivity的onCreate()方法中注冊(cè)監(jiān)聽,在onDestory()方法中移除監(jiān)聽。
- 并不是只有顯示和隱藏軟鍵盤會(huì)觸發(fā)OnGlobalLayoutListener中的回調(diào),一個(gè)View在繪制完成,或者消失時(shí)都會(huì)觸發(fā)OnGlobalLayoutListener中的回調(diào)(由于在onCreate中無法獲取一個(gè)View的寬度和高度,很多時(shí)候就是通過注冊(cè)O(shè)nGlobalLayoutListener,在OnGlobalLayoutListener的回調(diào)中來獲取一個(gè)View的寬度和高度)。
為了在OnGlobalLayoutListener的回調(diào)中準(zhǔn)確的判斷是否是由于軟鍵盤狀態(tài)改變引起的,以及獲取軟鍵盤的高度,還需要另外一個(gè)接口。
獲取當(dāng)前窗口可見的顯示區(qū)域大小
在View中提供了一個(gè)方法getWindowVisibleDisplayFrame(),此方法會(huì)返回該view所附著的窗口的可見區(qū)域大小。當(dāng)軟鍵盤顯示時(shí),窗口的可見區(qū)域大小會(huì)被壓縮,當(dāng)軟鍵盤隱藏時(shí),窗口的可見區(qū)域大小會(huì)還原。不過并不是只有軟鍵盤的顯示和隱藏會(huì)影響窗口的可見區(qū)域大小,像大多數(shù)的平板和部分手機(jī)上有一排虛擬按鍵(虛擬的返回鍵,Home鍵等),虛擬按鍵的顯示和隱藏也會(huì)引起窗口可見區(qū)域的變化。不過好在除了軟鍵盤外,其他操作對(duì)窗口可見區(qū)域的影響占整個(gè)屏幕大小的比例都不是很大,通過設(shè)置一個(gè)合理的閾值,就可以較準(zhǔn)確的判斷出是否是軟鍵盤顯示和隱藏引起的布局變化。
此外,getWindowVisibleDisplayFrame()會(huì)返回窗口的可見區(qū)域高度,通過和屏幕高度相減,就可以得到軟鍵盤的高度了。
監(jiān)聽軟鍵盤的狀態(tài)變化
在獲取到軟鍵盤的狀態(tài)和高度后就可以執(zhí)行需要的操作了。如重新布局按鈕位置,設(shè)置變量,記錄當(dāng)前軟鍵盤狀態(tài)和上次軟鍵盤隱藏時(shí)間等。不過如果有多個(gè)類需要根據(jù)軟鍵盤狀態(tài)來執(zhí)行一些操作,如果每個(gè)類中都去這樣做一遍就很麻煩,而且也沒有必要。這時(shí)在可以自行定義一個(gè)接口,在主Activity中對(duì)軟鍵盤狀態(tài)變化進(jìn)行監(jiān)聽,其他對(duì)軟鍵盤狀態(tài)感興趣的類,向主Activity中注冊(cè)軟鍵盤狀態(tài)變化監(jiān)聽。在主Activity中,當(dāng)軟鍵盤狀態(tài)發(fā)生改變時(shí)通知監(jiān)聽者。
完整示例代碼
完整的示例代碼如下。
public interface OnSoftKeyboardStateChangedListener { public void OnSoftKeyboardStateChanged(boolean isKeyBoardShow, int keyboardHeight); } //注冊(cè)軟鍵盤狀態(tài)變化監(jiān)聽 public void addSoftKeyboardChangedListener(OnSoftKeyboardStateChangedListener listener) { if (listener != null) { mKeyboardStateListeners.add(listener); } } //取消軟鍵盤狀態(tài)變化監(jiān)聽 public void removeSoftKeyboardChangedListener(OnSoftKeyboardStateChangedListener listener) { if (listener != null) { mKeyboardStateListeners.remove(listener); } } private ArrayList<OnSoftKeyboardStateChangedListener> mKeyboardStateListeners; //軟鍵盤狀態(tài)監(jiān)聽列表 private OnGlobalLayoutListener mLayoutChangeListener; private boolean mIsSoftKeyboardShowing; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); mIsSoftKeyboardShowing = false; mKeyboardStateListeners = new ArrayList<OnSoftKeyboardStateChangedListener>(); mLayoutChangeListener = new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //判斷窗口可見區(qū)域大小 Rect r = new Rect(); getWindow().getDecorView().getWindowVisibleDisplayFrame(r); //如果屏幕高度和Window可見區(qū)域高度差值大于整個(gè)屏幕高度的1/3,則表示軟鍵盤顯示中,否則軟鍵盤為隱藏狀態(tài)。 int heightDifference = screenHeight - (r.bottom - r.top); boolean isKeyboardShowing = heightDifference > screenHeight/3; //如果之前軟鍵盤狀態(tài)為顯示,現(xiàn)在為關(guān)閉,或者之前為關(guān)閉,現(xiàn)在為顯示,則表示軟鍵盤的狀態(tài)發(fā)生了改變 if ((mIsSoftKeyboardShowing && !isKeyboardShowing) || (!mIsSoftKeyboardShowing && isKeyboardShowing)) { mIsSoftKeyboardShowing = isKeyboardShowing; for (int i = 0; i < mKeyboardStateListeners.size(); i++) { OnSoftKeyboardStateChangedListener listener = mKeyboardStateListeners.get(i); listener.OnSoftKeyboardStateChanged(mIsSoftKeyboardShowing, heightDifference); } } } }; //注冊(cè)布局變化監(jiān)聽 getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(mLayoutChangeListener); } @SuppressWarnings("deprecation") @SuppressLint("NewApi") @Override protected void onDestroy() { //移除布局變化監(jiān)聽 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(mLayoutChangeListener); } else { getWindow().getDecorView().getViewTreeObserver().removeGlobalOnLayoutListener(mLayoutChangeListener); } super.onDestroy(); };
其中screenHeight 是屏幕高度,關(guān)于屏幕高度的獲取方法,網(wǎng)上有很多,這里就不介紹了。
如有疑問請(qǐng)留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
- Android自定義輸入法軟鍵盤
- android 軟鍵盤的POPUP布局的問題解決
- Android禁止EditText自動(dòng)彈出軟鍵盤的方法及遇到問題
- 解決Android軟鍵盤彈出覆蓋h5頁(yè)面輸入框問題
- 淺談關(guān)于android軟鍵盤彈出問題
- 5種方法完美解決android軟鍵盤擋住輸入框方法詳解
- Android開發(fā)之超實(shí)用的系統(tǒng)管理工具類【SD卡,網(wǎng)絡(luò),uri,屏幕,網(wǎng)絡(luò),軟鍵盤,文本,進(jìn)程等】
- Android開發(fā)之彈出軟鍵盤工具類簡(jiǎn)單示例
- Android開發(fā)中軟鍵盤的顯示和隱藏
- Android 實(shí)現(xiàn)數(shù)字九宮格軟鍵盤
相關(guān)文章
Android編程實(shí)現(xiàn)的超炫圖片瀏覽器
這篇文章主要介紹了Android編程實(shí)現(xiàn)的超炫圖片瀏覽器,涉及Android針對(duì)圖片的查看與顯示方法,包含對(duì)圖片的各種常見操作技巧,需要的朋友可以參考下2015-12-12Android webview用法實(shí)例簡(jiǎn)析
這篇文章主要介紹了Android webview用法,結(jié)合實(shí)例形式簡(jiǎn)單分析了Android中webview的功能、用法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-01-01Android NDK開發(fā)的環(huán)境搭建與簡(jiǎn)單示例
本文主要介紹Android NDK的知識(shí),這里整理了相關(guān)資料,來說明如何搭建相應(yīng)環(huán)境和簡(jiǎn)單實(shí)例,幫助大家理解,有興趣的小伙伴可以參考下2016-09-09Flutter實(shí)現(xiàn)心動(dòng)的動(dòng)畫特效
為了追求更好的用戶體驗(yàn),有時(shí)候我們需要一個(gè)類似心跳一樣跳動(dòng)著的控件來吸引用戶的注意力。本文將利用Flutter實(shí)現(xiàn)這一動(dòng)畫特效,需要的可以參考一下2022-04-04Android實(shí)現(xiàn)顯示系統(tǒng)實(shí)時(shí)時(shí)間
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)顯示系統(tǒng)實(shí)時(shí)時(shí)間,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05Android項(xiàng)目實(shí)戰(zhàn)之仿網(wǎng)易頂部導(dǎo)航欄效果
這篇文章主要為大家詳細(xì)介紹了Android項(xiàng)目實(shí)戰(zhàn)之仿網(wǎng)易頂部導(dǎo)航欄效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05Android Studio 4.0新特性及升級(jí)異常問題的解決方案
這篇文章主要介紹了Android Studio 4.0新特性及升級(jí)異常的相關(guān)問題,本文給大家分享解決方案,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06unity5.6 導(dǎo)出gradle工程 Android Studio 導(dǎo)入問題及處理方法
這篇文章主要介紹了unity5.6 導(dǎo)出gradle工程 Android Studio 導(dǎo)入問題及處理方法,需要的朋友可以參考下2017-12-12一個(gè)簡(jiǎn)單的Android定時(shí)任務(wù)
這篇文章主要為大家詳細(xì)介紹了一個(gè)簡(jiǎn)單的Android定時(shí)任務(wù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06