Android自適應軟鍵盤的Dialog以及監聽軟鍵盤彈起
最近項目中遇到一個需求:新手引導。跟一般的新手引導沒有什么太大區別,思路都是搞一個帶陰影的遮罩層,然后在上邊兒給一些提示性的文字,由于需求中有些特殊的地方,所以我用了一個全屏的dialog(而且,dialog自帶陰影效果)來做新手引導這個需求。
彈出鍵盤
鍵盤上方具體位置顯示引導
這個需求中有兩個地方需要考慮:
1. Dialog的布局要適應軟鍵盤的彈起
2. 軟鍵盤彈起和收下的時候都會有不同的引導,所以要在Dialog上監聽軟鍵盤的彈起。
需求的解決
一、適應鍵盤的彈起
我們知道,在Activity中如果要讓布局不被軟鍵盤遮擋,方法一般是在清單文件中配置windowSoftInputMode屬性, windowSoftInputMode是Android1.5以后的一個新特性,主要是對軟鍵盤操作的,主要有以下屬性:
- stateUnspecified:軟鍵盤的狀態并沒有指定,系統將選擇一個合適的狀態或依賴于主題的設置
- stateUnchanged:當這個activity出現時,軟鍵盤將一直保持在上一個activity里的狀態,無論是隱藏還是顯示
- stateHidden:用戶選擇activity時,軟鍵盤總是被隱藏
- stateAlwaysHidden:當該Activity主窗口獲取焦點時,軟鍵盤也總是被隱藏的
- stateVisible:軟鍵盤通常是可見的
- stateAlwaysVisible:用戶選擇activity時,軟鍵盤總是顯示的狀態
- adjustUnspecified:默認設置,通常由系統自行決定是隱藏還是顯示
- adjustResize:該Activity總是調整屏幕的大小以便留出軟鍵盤的空間
- adjustPan:當前窗口的內容將自動移動以便當前焦點從不被鍵盤覆蓋和用戶能總是看到輸入內容的部分
我們可以根據需求在清單文件中配置具體的屬性,那么如果軟鍵盤是基于Dialog彈出來的話該怎么辦呢?畢竟我們沒有清單文件來配置Dialog的屬性。其實,在清單中配置的屬性本質也是告訴當前界面對軟鍵盤這種情況的處理。windowSoftInputMode的屬性如果在清單文件中配置的話是這樣寫的:
android:windowSoftInputMode="stateHidden|adjustPan"
其實在清單文件中的某些配置,我們在Activity也能配置,在Activity中用代碼設置話是這樣寫的:
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
聰明的你看到這里可能已經看出來什么了,沒錯,兩種方式的實質就是得到當前的窗口實例,來基于當前窗口設置的。我們來進入源碼看一下,首先看getWindow()方法:
public Window getWindow()
{
return mWindow;
}
很簡單,得到當前的Window實例,再進入setSoftInputMode這個方法:
public void setSoftInputMode(int mode) {
final WindowManager.LayoutParams attrs = getAttributes();
if (mode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
attrs.softInputMode = mode;
mHasSoftInputMode = true;
} else {
mHasSoftInputMode = false;
}
if (mCallback != null) {
mCallback.onWindowAttributesChanged(attrs);
}
}
這個方法很短邏輯也很清晰,我們可以看到,倒數第三行代碼,當窗口屬性發生變化的時候,mCallback會回調一個方法執行某些操作,那么這個,mCallback是什么呢?在源碼中搜索一下你會找到以下代碼和注釋:
/** * Set the Callback interface for this window, used to intercept key
* events and other dynamic operations in the window.
* * @param callback The desired Callback interface.
*/
public void setCallback(Callback callback) {
mCallback = callback;
}
由注釋我們可以看到mCallback是給當前窗口設置的一個回調接口,當窗口發生某些變化的時候可以通過這個回調接口執行某些操作。回到mCallback.onWindowAttributesChanged(attrs)這句代碼,首先我們肯定知道,Activity肯定實現了這個接口,那么,還有其他的實現了這個接口嗎?找到定義這個接口的地方:
This is called whenever the current window attributes change.
public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);
由注釋我們也可以清楚的明白這個接口的用途:無論在什么時候都會被調用當窗口屬性發生變化。在Android Studio中我們可以點擊這個接口定義左邊的向上箭頭查看接口的實現類,我們發現,dialog也實現了這個接口,那么回到第一個需求,解決方法就簡單多了,只需要自定義一個Dialog,然后重寫onCreate方法,如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
}
其實跟在Activity中設置是一樣的。
二、基于Dialog監聽軟鍵盤的彈起和收下
其實看完第一個需求,我們可能已經猜想到,軟鍵盤也是基于當前窗口的,它的彈起和收下肯定會引起當前窗口布局的屬性發生變化,所以解決思路就有了:監聽當前布局的變化。我這里是比較布局坐標中的下坐標的,因為如果鍵盤彈起的話,布局的下坐標肯定會變小。具體代碼如下:
@Override
public void onLayoutChange(View v, int left, int top,
int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
if (oldBottom != 0 && bottom != 0 && (oldBottom - bottom > 0)) {
//軟鍵盤彈起 doSomethinh..
} else if (oldBottom != 0 && bottom != 0 && (bottom - oldBottom > 0)) {
//軟件盤關閉 doSomethinh..
}
}
onLayoutChange這個方法在窗口布局發生變化的時候會被回調,有興趣的朋友可以去看下源碼,注釋很清楚。這個回調方法中參數給我們了改變后view的左上右下的坐標,以及改變前view的坐標。
來自:http://www.jianshu.com/p/9a71a46c77ab