Android是一個針對觸摸屏專門設計的操做系統,當點擊編輯框,系統自動爲用戶彈出軟鍵盤,以便用戶進行輸入。
那麼,彈出軟鍵盤後必然會形成原有佈局高度的減小,那麼系統應該如何來處理佈局的減小?咱們可否在應用程序中進行自定義的控制?這些是本文要討論的重點。android
1、軟鍵盤顯示的原理
軟件盤的本質是什麼?軟鍵盤實際上是一個Dialog!
InputMethodService爲咱們的輸入法建立了一個Dialog,而且將該Dialog的Window的某些參數(如Gravity)進行了設置,使之可以在底部或者全屏顯示。當咱們點擊輸入框時,系統對活動主窗口進行調整,從而爲輸入法騰出相應的空間,而後將該Dialog顯示在底部,或者全屏顯示。
2、活動主窗口調整
android定義了一個屬性,名字爲windowSoftInputMode, 用它可讓程序能夠控制活動主窗口調整的方式。咱們能夠在AndroidManifet.xml中對Activity進行設置。如:android:windowSoftInputMode="stateUnchanged|adjustPan"
該屬性可選的值有兩部分,一部分爲軟鍵盤的狀態控制,另外一部分是活動主窗口的調整。前一部分本文不作討論,請讀者自行查閱android文檔。
模式一,壓縮模式
windowSoftInputMode的值若是設置爲adjustResize,那麼該Activity主窗口老是被調整大小以便留出軟鍵盤的空間。
咱們經過一段代碼來測試一下,當咱們設置了該屬性後,彈出輸入法時,系統作了什麼。
重寫Layout佈局:ide
源碼打印?1. public class ResizeLayout extends LinearLayout{ 2. private static int count = 0; 3. 4. public ResizeLayout(Context context, AttributeSet attrs) { 5. super(context, attrs); 6. } 7. 8. @Override 9. protected void onSizeChanged(int w, int h, int oldw, int oldh) { 10. super.onSizeChanged(w, h, oldw, oldh); 11. 12. Log.e("onSizeChanged " + count++, "=>onResize called! w="+w + ",h="+h+",oldw="+oldw+",oldh="+oldh); 13. } 14. 15. @Override 16. protected void onLayout(boolean changed, int l, int t, int r, int b) { 17. super.onLayout(changed, l, t, r, b); 18. Log.e("onLayout " + count++, "=>OnLayout called! l=" + l + ", t=" + t + ",r=" + r + ",b="+b); 19. } 20. 21. @Override 22. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 23. super.onMeasure(widthMeasureSpec, heightMeasureSpec); 24. 25. Log.e("onMeasure " + count++, "=>onMeasure called! widthMeasureSpec=" + widthMeasureSpec + ", heightMeasureSpec=" + heightMeasureSpec); 26. }
咱們的佈局設置爲:函數
源碼打印?1. <com.winuxxan.inputMethodTest.ResizeLayout 2. xmlns:android="http://schemas.android.com/apk/res/android" 3. android:id="@+id/root_layout" 4. android:layout_width="fill_parent" 5. android:layout_height="fill_parent" 6. android:orientation="vertical" 7. > 8. 9. <EditText 10. android:layout_width="fill_parent" 11. android:layout_height="wrap_content" 12. /> 13. 14. <LinearLayout 15. android:id="@+id/bottom_layout" 16. android:layout_width="fill_parent" 17. android:layout_height="fill_parent" 18. android:orientation="vertical" 19. android:gravity="bottom">s 20. 21. <TextView 22. android:layout_width="fill_parent" 23. android:layout_height="wrap_content" 24. android:text="@string/hello" 25. android:background="#77777777" 26. /> 27. </LinearLayout> 28. </com.winuxxan.inputMethodTest.ResizeLayout>
AndroidManifest.xml的Activity設置屬性:android:windowSoftInputMode = "adjustResize"
運行程序,點擊文本框,查看調試信息:
E/onMeasure 6(7960): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec = 1073742024
E/onMeasure 7(7960): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec = 1073742025
E/onSizeChanged 8(7960): =>onSizeChanged called! w=320,h=201,oldw=320,oldh=377
E/onLayout 9(7960): =>OnLayout called! l=0, t=0,r=320,b=201
從調試結果咱們能夠看出,當咱們點擊文本框後,根佈局調用了onMeasure,onSizeChanged和onLayout。
實際上,當設置爲adjustResize後,軟鍵盤彈出時,要對主窗口布局從新進行measure和layout,而在layout時,發現窗口的大小發生的變化,所以調用了onSizeChanged。
從下圖的運行結果咱們也能夠看出,本來在下方的TextView被頂到了輸入法的上方。佈局
模式二,平移模式
windowSoftInputMode的值若是設置爲adjustPan,那麼該Activity主窗口並不調整屏幕的大小以便留出軟鍵盤的空間。相反,當前窗口的內容將自動移動以便當前焦點從不被鍵盤覆蓋和用戶能老是看到輸入內容的部分。這個一般是不指望比調整大小,由於用戶可能關閉軟鍵盤以便得到與被覆蓋內容的交互操做。
上面的例子中,咱們將AndroidManifest.xml的屬性進行更改:android: windowSoftInputMode = "adjustPan"
從新運行,並點擊文本框,查看調試信息:
E/onMeasure 6(8378): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec=1073742200
E/onMeasure 7(8378): =>onMeasure called! widthMeasureSpec=1073742144, heightMeasureSpec=1073742201
E/onLayout 8(8378): =>OnLayout called! l=0, t=0,r=320,b=377
咱們看到:系統也從新進行了measrue和layout,可是咱們發現,layout過程當中onSizeChanged並無調用,這說明輸入法彈出先後並無改變原有佈局的大小。
從下圖的運行結果咱們能夠看到,下方的TextView並無被頂到輸入法上方。
測試
事實上,當輸入框不會被遮擋時,該模式沒有對佈局進行調整,然而當輸入框將要被遮擋時,窗口就會進行平移。也就是說,該模式始終是保持輸入框爲可見。以下圖,整個窗口,包括標題欄均被上移,以保證文本框可見。spa
模式三 自動模式
當屬性windowSoftInputMode被設置爲adjustUspecified時,它不被指定是否該Activity主窗口調整大小以便留出軟鍵盤的空間,或是否窗口上的內容獲得屏幕上當前的焦點是可見的。系統將自動選擇這些模式中一種主要依賴因而否窗口的內容有任何佈局視圖可以滾動他們的內容。若是有這樣的一個視圖,這個窗口將調整大小,這樣的假設可使滾動窗口的內容在一個較小的區域中可見的。這個是主窗口默認的行爲設置。
也就是說,系統自動決定是採用平移模式仍是壓縮模式,決定因素在於內容是否能夠滾動。操作系統
3、偵聽軟鍵盤的顯示隱藏
有時候,藉助系統自己的機制來實現主窗口的調整並不是咱們想要的結果,咱們可能但願在軟鍵盤顯示隱藏的時候,手動的對佈局進行修改,以便使軟鍵盤彈出時更加美觀。這時就須要對軟鍵盤的顯示隱藏進行偵聽。
直接對軟鍵盤的顯示隱藏偵聽的方法本人沒有找到,若是哪位找到的方法請務必告訴本人一聲。還有本方法針對壓縮模式,平移模式不必定有效。
咱們能夠藉助軟鍵盤顯示和隱藏時,對主窗口進行了從新佈局這個特性來進行偵聽。若是咱們設置的模式爲壓縮模式,那麼咱們能夠對佈局的onSizeChanged函數進行跟蹤,若是爲平移模式,那麼該函數可能不會被調用。
咱們能夠重寫根佈局,由於根佈局的高度通常狀況下是不發生變化的。
假設跟佈局爲線性佈局,模式爲壓縮模式,咱們寫一個例子,當輸入法彈出時隱藏某個view,輸入法隱藏時顯示某個view。
設計
源碼打印? 1. public class ResizeLayout extends LinearLayout{ 2. private OnResizeListener mListener; 3. 4. public interface OnResizeListener { 5. void OnResize(int w, int h, int oldw, int oldh); 6. } 7. 8. public void setOnResizeListener(OnResizeListener l) { 9. mListener = l; 10. } 11. 12. public ResizeLayout(Context context, AttributeSet attrs) { 13. super(context, attrs); 14. } 15. 16. @Override 17. protected void onSizeChanged(int w, int h, int oldw, int oldh) { 18. super.onSizeChanged(w, h, oldw, oldh); 19. 20. if (mListener != null) { 21. mListener.OnResize(w, h, oldw, oldh); 22. } 23. } 24. }
在咱們的Activity中,經過以下方法調用: 調試
源碼打印? 1. public class InputMethodTestActivity extends Activity { 2. private static final int BIGGER = 1; 3. private static final int SMALLER = 2; 4. private static final int MSG_RESIZE = 1; 5. 6. private static final int HEIGHT_THREADHOLD = 30; 7. 8. class InputHandler extends Handler { 9. @Override 10. public void handleMessage(Message msg) { 11. switch (msg.what) { 12. case MSG_RESIZE: { 13. if (msg.arg1 == BIGGER) { 14. findViewById(R.id.bottom_layout).setVisibility(View.VISIBLE); 15. } else { 16. findViewById(R.id.bottom_layout).setVisibility(View.GONE); 17. } 18. } 19. break; 20. 21. default: 22. break; 23. } 24. super.handleMessage(msg); 25. } 26. } 27. 28. private InputHandler mHandler = new InputHandler(); 29. 30. /** Called when the activity is first created. */ 31. @Override 32. public void onCreate(Bundle savedInstanceState) { 33. super.onCreate(savedInstanceState); 34. setContentView(R.layout.main); 35. 36. ResizeLayout layout = (ResizeLayout) findViewById(R.id.root_layout); 37. layout.setOnResizeListener(new ResizeLayout.OnResizeListener() { 38. 39. public void OnResize(int w, int h, int oldw, int oldh) { 40. int change = BIGGER; 41. if (h < oldh) { 42. change = SMALLER; 43. } 44. 45. Message msg = new Message(); 46. msg.what = 1; 47. msg.arg1 = change; 48. mHandler.sendMessage(msg); 49. } 50. }); 51. } 52. }
這裏特別須要注意的是,不能直接在OnResizeListener中對要改變的View進行更改,由於OnSizeChanged函數其實是運行在View的layout方法中,若是直接在onSizeChange中改變view的顯示屬性,那麼極可能須要從新調用layout方法才能顯示正確。然而咱們的方法又是在layout中調用的,所以會出現錯誤。所以咱們在例子中採用了Handler的方法。code