最近項目中遇到一個需求:新手引導(dǎo)。跟一般的新手引導(dǎo)沒有什么太大區(qū)別,思路都是搞一個帶陰影的遮罩層,然后在上邊兒給一些提示性的文字,由于需求中有些特殊的地方,所以我用了一個全屏的dialog(而且,dialog自帶陰影效果)來做新手引導(dǎo)這個需求。


這個需求中有兩個地方需要考慮:1. Dialog的布局要適應(yīng)軟鍵盤的彈起2. 軟鍵盤彈起和收下的時候都會有不同的引導(dǎo),所以要在Dialog上監(jiān)聽軟鍵盤的彈起。
需求的解決:
一:適應(yīng)鍵盤的彈起。
我們知道,在Activity中如果要讓布局不被軟鍵盤遮擋,方法一般是在清單文件中配置windowSoftInputMode屬性, windowSoftInputMode是Android1.5以后的一個新特性,主要是對軟鍵盤操作的,主要有以下屬性:
- stateUnspecified:軟鍵盤的狀態(tài)并沒有指定,系統(tǒng)將選擇一個合適的狀態(tài)或依賴于主題的設(shè)置
- stateUnchanged:當(dāng)這個activity出現(xiàn)時,軟鍵盤將一直保持在上一個activity里的狀態(tài),無論是隱藏還是顯示
- stateHidden:用戶選擇activity時,軟鍵盤總是被隱藏
- stateAlwaysHidden:當(dāng)該Activity主窗口獲取焦點時,軟鍵盤也總是被隱藏的
- stateVisible:軟鍵盤通常是可見的
- stateAlwaysVisible:用戶選擇activity時,軟鍵盤總是顯示的狀態(tài)
- adjustUnspecified:默認(rèn)設(shè)置,通常由系統(tǒng)自行決定是隱藏還是顯示
- adjustResize:該Activity總是調(diào)整屏幕的大小以便留出軟鍵盤的空間
- adjustPan:當(dāng)前窗口的內(nèi)容將自動移動以便當(dāng)前焦點從不被鍵盤覆蓋和用戶能總是看到輸入內(nèi)容的部分
我們可以根據(jù)需求在清單文件中配置具體的屬性,那么如果軟鍵盤是基于Dialog彈出來的話該怎么辦呢?畢竟我們沒有清單文件來配置Dialog的屬性。其實,在清單中配置的屬性本質(zhì)也是告訴當(dāng)前界面對軟鍵盤這種情況的處理。windowSoftInputMode的屬性如果在清單文件中配置的話是這樣寫的:
android:windowSoftInputMode="stateHidden|adjustPan"
其實在清單文件中的某些配置,我們在Activity也能配置,在Activity中用代碼設(shè)置話是這樣寫的:
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
聰明的你看到這里可能已經(jīng)看出來什么了,沒錯,兩種方式的實質(zhì)就是得到當(dāng)前的窗口實例,來基于當(dāng)前窗口設(shè)置的。我們來進(jìn)入源碼看一下,首先看getWindow()方法:
public Window getWindow() { return mWindow; }
很簡單,得到當(dāng)前的Window實例,再進(jìn)入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);
}
}```
這個方法很短邏輯也很清晰,我們可以看到,倒數(shù)第三行代碼,當(dāng)窗口屬性發(fā)生變化的時候,mCallback會回調(diào)一個方法執(zhí)行某些操作,那么這個,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;
}```
- @param callback The desired Callback interface.
由注釋我們可以看到mCallback是給當(dāng)前窗口設(shè)置的一個回調(diào)接口,當(dāng)窗口發(fā)生某些變化的時候可以通過這個回調(diào)接口執(zhí)行某些操作?;氐絤Callback.onWindowAttributesChanged(attrs)這句代碼,首先我們肯定知道,Activity肯定實現(xiàn)了這個接口,那么,還有其他的實現(xiàn)了這個接口嗎?找到定義這個接口的地方:
This is called whenever the current window attributes change. public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);
由注釋我們也可以清楚的明白這個接口的用途:無論在什么時候都會被調(diào)用當(dāng)窗口屬性發(fā)生變化。在Android Studio中我們可以點擊這個接口定義左邊的向上箭頭查看接口的實現(xiàn)類,我們發(fā)現(xiàn),dialog也實現(xiàn)了這個接口,那么回到第一個需求,解決方法就簡單多了,只需要自定義一個Dialog,然后重寫onCreate方法,如下:
super.onCreate(savedInstanceState); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
}```
其實跟在Activity中設(shè)置是一樣的。
二,基于Dialog監(jiān)聽軟鍵盤的彈起和收下:
其實看完第一個需求,我們可能已經(jīng)猜想到,軟鍵盤也是基于當(dāng)前窗口的,它的彈起和收下肯定會引起當(dāng)前窗口布局的屬性發(fā)生變化,所以解決思路就有了:監(jiān)聽當(dāng)前布局的變化。我這里是比較布局坐標(biāo)中的下坐標(biāo)的,因為如果鍵盤彈起的話,布局的下坐標(biāo)肯定會變小。具體代碼如下:
``` @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))
{//軟件盤關(guān)閉 doSomethinh.. } }```
onLayoutChange這個方法在窗口布局發(fā)生變化的時候會被回調(diào),有興趣的朋友可以去看下源碼,注釋很清楚。這個回調(diào)方法中參數(shù)給我們了改變后view的左上右下的坐標(biāo),以及改變前view的坐標(biāo)。