1. 程式人生 > >Android 軟鍵盤在不同場景會應用到的屬性詳解(可用於處理軟鍵盤擠壓佈局的問題)

Android 軟鍵盤在不同場景會應用到的屬性詳解(可用於處理軟鍵盤擠壓佈局的問題)

修正
1.修正了flagNoFullscreen和flagNoExtractUi的說明,以及區別。

背景
1.Android軟鍵盤這塊從我入職到現在,是一個一直糾纏我的問題。

2.從佈局擠壓,到EditText顯示不全,在到彈出時卡頓,在Android軟鍵盤面前我無數次跌倒。

3.因為網上大多數的知識點比較分散而且很雜,所以本篇做一個整合篇。

4.Android軟鍵盤這塊知識點比較密集,瞭解過一次之後,差不多什麼情況都可以找到原因了。

5.感謝Android軟鍵盤的問題,從我入職陪伴我到現在,讓我一個一個不停的解決。

前言
本文將從以下幾個方面進行介紹:

(1)InputMethodService的原始碼解析,從原始碼解讀中告訴你為什麼軟鍵盤彈出的是一個Dialog

(2)Android軟鍵盤顯示時,設定windowSoftInputMode的作用

(3)EditText設定imeOptions屬性對軟鍵盤的影響

(4)軟鍵盤上面的按鍵監聽

(5)橫屏狀態下,不希望軟鍵盤顯示全屏怎麼處理

(6)控制軟鍵盤的彈出和關閉的方法

(7)EditText在軟鍵盤彈出的時候顯示不全,怎麼獲取軟鍵盤彈出和關閉的監聽

(8)軟鍵盤彈出的時候,造成頁面卡頓,這時候如何發現問題並解決問題

(9)Android鍵盤面板衝突,佈局閃動的解決方法

Android軟鍵盤的顯示原理
軟鍵盤其實是一個Dialog

    InputMethodService為我們的輸入法建立了一個Dialog,並且對某些引數進行了設定,使之能夠在底部或者全屏顯示。當我們點選輸入框時,系統會對當前的主視窗進行調整,以便留出相應的空間來顯示該Dialog在底部,或者全屏。

    其實這段話我們經常在各種軟鍵盤部落格所看到,但是大家並不知道Android是怎麼為我們建立的這個Dialog,所以我先帶大家來看下軟鍵盤生成這塊的原始碼,瞭解軟鍵盤的生成流程。

InputMethodService的原始碼解析

因為InputMethodService屬於服務,接下來我們先看一下服務的入口onCreate()方法:

  @Override 
    public void onCreate() {
        //設定主題與xml裡面設定theme是一樣的道理
        mTheme = Resources.selectSystemTheme(mTheme,
                getApplicationInfo().targetSdkVersion,
                android.R.style.Theme_InputMethod,
                android.R.style.Theme_Holo_InputMethod,
                android.R.style.Theme_DeviceDefault_InputMethod,
                android.R.style.Theme_DeviceDefault_InputMethod);
        super.setTheme(mTheme);
        //建立InputMethodMananger
        mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
        mSettingsObserver = SettingsObserver.createAndRegister(this);
        mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0);
        mInflater = (LayoutInflater)getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        /**
         * 這裡注意一下,首先這裡的命名屬於Window,然後我們發現了Gravity.BOTTOM,就更加確定了這個就是
         * 軟鍵盤所建立的Dialog物件
         */
        mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
                WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
        if (mHardwareAccelerated) {
            mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
        }
        initViews();
        mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
    }

通過上面的分析,我們懷疑這裡的SoftInputWindow是軟鍵盤彈出建立的Dialog物件,下面我們看下SoftInputWindow的原始碼。

public class SoftInputWindow extends Dialog{
    ....
}

看到這裡大家就能明白了,為什麼說軟鍵盤就是一個Dialog。而且這裡通過設定Gravity.BOTTOM來控制當前Dialog在Window中的位置。

軟鍵盤的顯示調整(windowSoftInputMode)
在Android中,可以通過給Activity設定windowSoftInputMode這個屬性來控制軟鍵盤與Activity的主視窗的互動方式。

Activity 的主視窗與包含螢幕軟鍵盤的視窗的互動方式,該屬性的設定影響兩個方面:

當Activity成為使用者注意的焦點時軟鍵盤的狀態 - 隱藏還是可見。
對Activity主視窗所做的調整 - 意思是是否將其尺寸調小為軟鍵盤騰出空間,或者當視窗部分被軟鍵盤遮擋時是否平移其內容以使當前焦點可見。
該設定必須是下面所列的值之一,或者是一個“state…”值加上一個“adjust…”值的組合,在任一組中設定多個值(例如,多個“state…”值)都會產生未定義結果。各值之間使用垂直條 (|) 分隔

(1)控制軟鍵盤顯示還是隱藏
1
stateUnspecified-不指定軟鍵盤的狀態(隱藏還是可見) 將由系統選擇合適的狀態,或依賴主題中的設定,這是對軟鍵盤行為的預設設定

stateUnchanged-保留狀態 當 Activity 轉至前臺時保留軟鍵盤最後所處的任何狀態,無論是可見還是隱藏

stateHidden-隱藏軟鍵盤 當用戶確實是向前導航到 Activity,而不是因離開另一Activity 而返回時隱藏軟鍵盤

stateAlwaysHidden-始終隱藏軟鍵盤 當 Activity 的主視窗有輸入焦點時始終隱藏軟鍵盤

stateVisible-顯示軟鍵盤 在正常的適宜情況下(當用戶向前導航到 Activity 的主視窗時)顯示軟鍵盤

stateAlwaysVisible-顯示軟鍵盤 當用戶確實是向前導航到 Activity,而不是因離開另一Activity 而返回時.

(2)在軟鍵盤彈出時,是否需要Activity對此進行調整
1
adjustUnspecified 主視窗的預設行為,不指定 Activity 的主視窗是否調整尺寸以為軟鍵盤騰出空間,或者視窗內容是否進行平移以在螢幕上顯露當前焦點。 系統會根據視窗的內容是否存在任何可滾動其內容的佈局檢視來自動選擇其中一種模式。 如果存在這樣的檢視,視窗將進行尺寸調整,前提是可通過滾動在較小區域內看到視窗的所有內容。

adjustResize 始終調整 Activity 主視窗的尺寸來為螢幕上的軟鍵盤騰出空間。

adjustPan 不調整 Activity 主視窗的尺寸來為軟鍵盤騰出空間, 而是自動平移視窗的內容,使當前焦點永遠不被鍵盤遮蓋,讓使用者始終都能看到其輸入的內容。 這通常不如尺寸調整可取,因為使用者可能需要關閉軟鍵盤以到達被遮蓋的視窗部分或與這些部分進行互動。

adjustNoting 軟鍵盤彈出時,主視窗Activity不會做出任何響應。

windowSoftInputMode 應用場景
下面將通過例子來介紹adjustNoting、adjustUnspecified、adjustResize、adjustPan在軟鍵盤彈出的區別:

adjustUnspecified : 當軟鍵盤彈出時,系統自動指定視窗的調整模式,根據不同的情況會選擇adjustResize或者adjustPan的一種。

adjustPan : 當軟鍵盤彈出時,會將主視窗的平移(translateY),來適應軟鍵盤的顯示。

adjustResize : 當軟鍵盤彈出時,會讓佈局重新繪製,這種一般適應於帶有滑動性質的控制,讓其向下滾動,然後適應軟鍵盤的顯示。

adjustNoting: 軟鍵盤彈出時,主視窗不會做出任何反應。
非滾動佈局xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="請輸入您要輸入的內容1" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="請輸入您要輸入的內容2" />

                    ..........<中間包含無數的EditText>

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="請輸入您要輸入的內容12" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="請輸入您要輸入的內容13" />

</LinearLayout>

點選最下面的EditText12

(1)設定windowSoftInputMode為adjustNoting

從上圖發現,當點選EditText12時,彈出軟鍵盤將主視窗下半部分給遮蓋,並且主視窗沒有做出任何反應。

(2)設定windowSoftInputMode為adjustPan

當設定其屬性為adjustPan時,當軟鍵盤彈出時,主窗口布局會上移至直到顯示EditText12。

(3)設定windowSoftInputMode為adjustUnspecified

當設定其屬性為預設屬性adjustUnspecified時,發現當點選EditText12時,主視窗上移來保持EditText12在軟鍵盤之上,這時adjustUnspecified的表現形式與adjustPan相同,所以在無滑動的控制元件上,預設的指定形式為adjustPan。

(4)設定windowSoftInputMode為adjustResize

設定其屬性為adjustResize時,發現軟鍵盤彈出的狀態與adjustNoting表現一致,當設定adjustResize時,佈局會為了軟鍵盤彈出而重新繪製給軟鍵盤留出空間,而由於控制元件無法滑動,所以表現的形式與adjustNoting一致。

滾動佈局xml