Android軟鍵盤控制方法、以及開發(fā)中遇到的一些問題。
Android 提供了 windowSoftInputMode 屬性來控制輸入法軟鍵盤窗口和 Activity 主窗口的交互,分為 窗口尺寸調(diào)整系列 和 輸入法軟鍵盤顯示控制系列。
窗口尺寸調(diào)整系列:
該系列參數(shù)用來控制當(dāng)軟鍵盤彈起時,Activity 主窗口的調(diào)整策略,因為如果不調(diào)整主窗口,很可能會導(dǎo)致當(dāng)前輸入的控件被軟鍵盤遮擋。
adjustPan:
Activity的主窗口并不會重新調(diào)整大小來為輸入法騰出空間,而是窗口的內(nèi)容會自動上下晃動來保證當(dāng)前獲得焦點的控件不會被鍵盤遮擋住,然后用戶可以看見自己輸入的內(nèi)容。相比于 adjustResize 模式而言,它并不是很令人滿意,因為用戶必須關(guān)閉輸入法來和被輸入法遮擋住的控件進(jìn)行交互。
adjustResize:
Activity的主窗口會重新調(diào)整大小來為輸入法騰出空間。
adjustUnspecified:
當(dāng)前模式并不會明確指定 Activity 使用adjustPan 或者 adjustResize ,系統(tǒng)會自動選擇一個模式,選擇結(jié)果是,如果當(dāng)前 Activity 的 Window 中,有可以滾動自身內(nèi)容的控件,比如 ScrollView,那么選擇結(jié)果就是 adjustResize,因為它認(rèn)為滾動可以使 Window 中的內(nèi)容即使在一個很小的區(qū)域中也可以被看見。Activity 的默認(rèn)模式就是這個模式。
adjustNoting:
Activity 的 Window 沒有任何變化。
兩種模式下,屏幕 – 主窗口 – 主窗口內(nèi)容 – 軟鍵盤 的關(guān)系如下:
具體效果如下:
沒有滾動內(nèi)容,adjustPan:
沒有滾動內(nèi)容,adjustResize:
沒有滾動內(nèi)容,adjustUnspecified == adjustPan
有滾動內(nèi)容(ScrollView),adjustPan
有滾動內(nèi)容(ScrollView),adjustResize
有滾動內(nèi)容(ScrollView),adjustUnspecified == adjustResize
輸入法軟鍵盤顯示控制系列:
該系列參數(shù)用來控制當(dāng)一個包含 Window 的事物(Activity、Dialog等)展示在屏幕最前端時,軟鍵盤的顯示或者隱藏策略。
stateUnspecified:
系統(tǒng)根據(jù)當(dāng)前具體情況,選擇相應(yīng)的模式。
stateUnchanged:
軟鍵盤保持它的上一個狀態(tài)(上一個Activity 或者 Dialog 在屏幕最前端時,軟鍵盤的狀態(tài)),不做變化,不管上一個狀態(tài)是顯示還是隱藏。
stateHidden:
當(dāng)用戶主動進(jìn)入當(dāng)前界面時,軟鍵盤隱藏。離開上一個 界面,返回當(dāng)前界面,不能算作 “主動進(jìn)入”。被動進(jìn)入時,保持上一個狀態(tài)。
stateAlwaysHidden:
只要是用戶進(jìn)入該界面,就隱藏軟鍵盤,不管是主動進(jìn)入(新啟動該界面),還是被動進(jìn)入(離開上一個界面,返回到當(dāng)前界面)。
stateVisible:
當(dāng)用戶主動進(jìn)入當(dāng)前界面時,顯示軟鍵盤。離開上一個 界面,返回當(dāng)前界面,不能算作 “主動進(jìn)入”。被動進(jìn)入時,保持上一個狀態(tài)。
stateAlwaysVisible:
只要是用戶進(jìn)入該界面,就顯示軟鍵盤,不管是主動進(jìn)入(新啟動該界面),還是被動進(jìn)入(離開上一個界面,返回到當(dāng)前界面)。
關(guān)于某些華為手機(jī)的一個Bug
今天遇到一個Bug,是這樣的,在某些華為手機(jī)上面,除了***次點擊輸入框,adjustPan 參數(shù)會生效(軟鍵盤可以正常彈起輸入框),后面從第二次開始,怎么點擊,adjustPan 參數(shù)都無效。
布局模型大致如下:
- <RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/activity_main"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="cn.hjf.inputtest.MainActivity">
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <View
- android:layout_width="match_parent"
- android:layout_height="400dp"
- android:background="#2b532b"/>
- <EditText
- android:layout_width="90dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:background="@null"
- android:gravity="center"
- android:inputType="numberDecimal"
- android:maxLength="8"
- android:minWidth="60dp"
- android:padding="5dp"
- android:text="0.00"/>
效果是這樣的:
經(jīng)過漫長時間的排除(很辛苦),終于找到了Bug的觸發(fā)臨界點,就是下面這兩句話:
- android:gravity="center"
- android:inputType="numberDecimal"
又經(jīng)過了漫長時間的驗證,得出一個結(jié)論:在某些華為機(jī)型上面,在這種布局模型下,EditText 中如果設(shè)置了 inputType 參數(shù)(不為 none),那么,在 gravity 取值為某些值得時候,會導(dǎo)致該問題的發(fā)生。大致情況如下:
紅色表示失效,綠色表示有效(可以正常工作)。當(dāng)這兩個參數(shù)不指定的時候,可以工作,因為這兩個屬性默認(rèn)值的組合是可以工作的,上圖中的藍(lán)色塊。(備注:沒有完全匹配所有情況,如有相似情況,可以按需匹配,查看結(jié)果)
在源碼中可以找到這兩個屬性的默認(rèn)值:
attrs.xml
themes.xml
- <item name="editTextStyle">@style/Widget.EditText</item>
attrs.xml
- <!-- The type of data being placed in a text field, used to help an
- input method decide how to let the user enter text. The constants
- here correspond to those defined by
- {@link android.text.InputType}. Generally you can select
- a single value, though some can be combined together as
- indicated. Setting this attribute to anything besides
- <var>none</var> also implies that the text is editable. -->
- <attr name="inputType">
- <!-- There is no content type. The text is not editable. -->
- <flag name="none" value="0x00000000" />
- /**
- * Special content type for when no explicit type has been specified.
- * This should be interpreted to mean that the target input connection
- * is not rich, it can not process and show things like candidate text nor
- * retrieve the current text, so the input method will need to run in a
- * limited "generate key events" mode, if it supports it. Note that some
- * input methods may not support it, for example a voice-based input
- * method will likely not be able to generate key events even if this
- * flag is set.
- */
- public static final int TYPE_NULL = 0x00000000;
一個需求
遇到一個需求,大致模型為:有一個界面,里面有一個 輸入框 和 兩個按鈕,分別控制輸入框中數(shù)字的加減,如下圖所示:
每次輸入框數(shù)字變化,不管是手動輸入還是按鈕控制,都會出發(fā)刷新工作,刷新時會彈出一個對話框。
然后需求是這樣的,當(dāng)手動輸入的時候,這時觸發(fā)刷新時,輸入法是顯示的,但是對話框顯示然后消失后,輸入法就被隱藏了。當(dāng)用按鈕控制的時候,是沒問題的,因為整個過程,輸入法都是隱藏的。所以需求就是,在對話框顯示然后消失后,輸入法能保持出發(fā)刷新時的狀態(tài)。
解決問題:當(dāng)把當(dāng)前 Activity 設(shè)置為 stateUnchanged 的時候,并沒有達(dá)到理想的效果,因為dialog所在的window,沒有設(shè)置該屬性,所以需要把dialog的window也設(shè)置為stateUnchanged,方法為:
- getWindow().setSoftInputMode()
然后就可以了。