【轉載】Android 軟鍵盤的全面解析,讓你不再怕控制項被遮蓋

2021-03-02 Qunar技術沙龍

本文轉載自微信公眾號【mobilehub】

作者 | Vander丶

編輯 | 蘇宓

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

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

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

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

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

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

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

Android軟鍵盤顯示時,設置windowSoftInputMode的作用

EditText設置imeOptions屬性對軟鍵盤的影響

軟鍵盤上面的按鍵監聽

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

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

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

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

Android鍵盤面板衝突,布局閃動的解決方法

軟鍵盤其實是一個Dialog

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

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

我們先來看一下InputMethodService的繼承關係:

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)控制軟鍵盤顯示還是隱藏

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

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

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

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

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

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

(2)在軟鍵盤彈出時,是否需要Activity對此進行調整

adjustUnspecified 主窗口的默認行為,不指定 Activity 的主窗口是否調整尺寸以為軟鍵盤騰出空間,或者窗口內容是否進行平移以在屏幕上顯露當前焦點。 系統會根據窗口的內容是否存在任何可滾動其內容的布局視圖來自動選擇其中一種模式。 如果存在這樣的視圖,窗口將進行尺寸調整,前提是可通過滾動在較小區域內看到窗口的所有內容。

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

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

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

下面將通過例子來介紹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

<?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">

    <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">

            <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>

    </ScrollView>

</LinearLayout>

還是同樣的操作,點擊最下面的EditText13

(1)設置windowSoftInputMode為adjustNoting

我們可以看出,當點擊EditText12時,彈出軟鍵盤將主窗口下半部分給遮蓋,並且主窗口沒有做出任何反應,和不加ScrollView是一樣的情況。

(2)設置windowSoftInputMode為adjustResize

我們可以發現,當設置其屬性為adjustResize時,當軟鍵盤彈出時,ScrollView會重新繪製,然後滾動EditText13位置,使其顯示在軟鍵盤之上。

(3)設置windowSoftInputMode為adjustUnspecified

當設置其屬性為默認屬性adjustUnspecified時,可以發現在添加了ScrollView控制項時,布局的窗口並不會上移(這個觀察Toolbar就可以發現),而通過重繪ScrollView,讓其滾動到最低端,並且給軟鍵盤流出控制項,而這個表現即和adjustResize完全一致。

(4)設置windowSoftInputMode為adjustPan

可以發現,在滑動空間下,設置屬性adjustPan時,依舊會將主窗口上移,來使EditText13顯示在軟鍵盤之上,可以通過觀察Toolbar得知。

windowSoftInputMode總結

通過上面的例子,我們可以完全理解adjust系列的各個參數的作用。而軟鍵盤的顯示和隱藏這裡面需要並不多,而且內容並不算複雜,大家回去自己嘗試下就可以。

EditText設置imeOptions屬性對軟鍵盤的影響

在日常開發中,如果需要將軟鍵盤的Enter鍵更改為其他鍵,可以設置其android:imeOptions 屬性,這個屬性可以控制軟鍵盤的Enter鍵,以及橫屏情況下的軟鍵盤顯示狀態。

該設置必須是下面所列的值之一,或者是一個「action…」值加上一個「flag…」值的組合,在action…組中設置多個值(例如,多個「action…」值)都會產生未定義結果,而flag….可以設置多個。各值之間使用垂直條 (|) 分隔

(1)控制軟鍵盤上的Enter鍵

android:imeOptions=」normal」

當android:singleLine=」true」 

輸入框後面還有輸入控制項的時候會顯示next,沒有時會顯示done(完成) 

當android:singleLine=」false」 

輸入框會進行換行操作

android:imeOptions=」actionUnspecified」

該屬性為默認屬性,一般情況下為「normal」的使用情形。

android:imeOptions=」actionNone」

顯示回車鍵,當singleLine為true的時候,會跳到下個可輸出的控制項,否則軟鍵盤消失,輸入完畢。

android:imeOptions=」actionGo」

顯示為Go(前往)按鈕,需要配合android:singleLine使用,否則為回車鍵起換行作用,並且需要自己寫事件。

android:imeOptions=」actionSearch」

顯示搜索(Search)按鈕,需要配合android:singleLine使用,否則為回車鍵起換行作用,並且需要自己寫事件。

android:imeOptions=」actionSend」

顯示send(發送)按鈕,需要配合android:singleLine使用,否則為回車鍵起換行作用,並且需要自己寫事件。

android:imeOptions=」actionNext」

顯示next(下一步)按鈕,作用是跳到下一個可輸入的控制項,需要配合android:singleLine使用,否則為回車鍵起換行作用。

android:imeOptions=」actionDone」

顯示done(完成)按鈕,作用編輯完成,讓軟鍵盤消失.需要配合android:singleLine使用,否則為回車鍵起換行作用。

android:imeOptions=」actionPrevious」

顯示上一步按鈕,如果前面有輸入控制項,點擊後會回到前一個控制項獲取焦點,.需要配合android:singleLine使用,否則為回車鍵起換行作用。

可能各個輸入法的顯示圖標不一樣,但是效果是一樣的,這裡用的是搜狗輸入法。

(2)橫屏下控制軟鍵盤

android:imeOptions=」flagNoFullscreen」

橫屏下設置輸入法不填充全屏。

android:imeOptions=」flagNoExtractUi」

橫屏下設置輸入法不填充全屏.看了API也不太懂與flagNoFullscreen的區別。

android:imeOptions=」flagNavigatePrevious」

橫屏下設置輸入法全屏,設置輸入框上的按鈕為(previous)上一個的作用。

android:imeOptions=」flagNavigateNext」

橫屏下設置輸入法全屏,設置輸入框上的按鈕為(Next)下一個作用。

android:imeOptions=」flagNoAccessoryAction」

橫屏下設置輸入法全屏,並且使其輸入框上的按鈕隱藏。

android:imeOptions=」flagNoEnterAction」

橫屏下設置輸入法全屏,輸入框內的按鈕為完成(Done)狀態.編輯完畢,點完成,軟鍵盤消失。

總結一下:

這裡大部分的屬性,已經介紹完畢,如果英語好的同學,可以去看下官方文檔,可以更好的理解,並且本文以搜狗輸入法為實踐,可能其他的輸入法與其顯示的不同,但是功能應該都是一樣的。

如果需要監聽軟鍵盤的右下角的按鍵,需要為EditText設置setOnEditorActionListener()監聽:

        mMainEt = (EditText) findViewById(R.id.main_et);

        mMainEt.setOnEditorActionListener(new TextView.OnEditorActionListener() {

            @Override

            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {

                switch (actionId) {

                    //點擊GO鍵

                    case EditorInfo.IME_ACTION_GO:

                        return true;

                    //點擊Done

                    case EditorInfo.IME_ACTION_DONE:

                        return true;

                    //點擊Next

                    case EditorInfo.IME_ACTION_NEXT:

                        return true;

                    //點擊Previous

                    case EditorInfo.IME_ACTION_PREVIOUS:

                        return true;

                    //點擊None

                    case EditorInfo.IME_ACTION_NONE:

                        return true;

                    //點擊Send

                    case EditorInfo.IME_ACTION_SEND:

                        return true;

                }

                return false;

            }

        });

上面的方式,只是展示了如何監聽各個按鍵的方法,如果需要消費事件,則需要return true。

實際案例1:橫屏下軟鍵盤不全屏顯示

其實已經在第二方面介紹了,只要設置EditText 的android:imeOptions屬性為 flagNoFullScreen 和 flagNoExtraUI即可。

實際案例2:控制軟鍵盤彈出和關閉

一般來說,在實際項目中,都會使用工具類來控制軟鍵盤的顯示或者關閉。

(1)顯示軟鍵盤

InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

if (imm != null) {

    view.requestFocus();

    imm.showSoftInput(view, 0);

}

(2)關閉軟鍵盤

InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

if (imm != null) {

    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);

}

而如果對於這一塊有什麼不明白的,可以參考這篇博客。

實際案例3:EditText顯示不全,並且需要監聽軟鍵盤彈出和關閉

現在有一個常見的需求,EditText被布局包裹,然後需要將原生的EditText背景給替換掉或者直接設置為null(或者其他),然後和布局上下存在間距,然後整體與底部對齊。

這時候彈出軟鍵盤,請看圖:

從圖中可以發現,當原生的EditText背景被替換之後,軟鍵盤會遮蓋掉自定義區域,並且直接顯示在EditText之下,正常情況下,我們是希望軟鍵盤顯示在整個外層的Layout之下的。

當時對於這個問題,我沒有頭緒好一陣子,現在來看,那時候挺年輕的。

而這個問題,就屬於軟鍵盤遮擋布局的問題:

引用塊內容

(1)軟鍵盤遮蓋焦點:

當軟鍵盤彈出的時候,將EditText等輸入類的控制項的焦點遮蓋時,這時候可以設置adjustPan或者adjustResize可以很好的解決。

這裡如果理解好本博文第一塊內容就能很好解決這個問題。

(2)軟鍵盤遮蓋沒有遮蓋焦點,但是遮蓋了需要顯示的控制項:

這時候設置通過設置屬性adjustPan或者adjustResize還不足以解決問題,因為軟鍵盤並沒有遮蓋了EditText的焦點,所以單獨設置這兩個屬性是對軟鍵盤或者界面是無法產生改變的。

這時我們必須從另外一個角度來考慮這個問題,就是來監聽軟鍵盤的彈出和關閉來操作布局,來解決這個問題。

這時候普遍會有以下的幾種解決方案:

(1)設置adjustResize屬性,當軟鍵盤彈出的時候會重繪布局,然後設置根布局的OnLayoutChangeListener的監聽,來監聽布局的變化。

 mScrollView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {

 @Override

 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {

                Log.d("new change ", "left : " + left + "top : " + top + "right : " + right + "bottom : " + bottom);

                Log.d("old change ", "oldLeft : " + oldLeft + "oldTop : " + oldTop + "oldRight : " + oldRight + "oldBottom : " + oldBottom);

            }

        });

//當軟鍵盤彈出

07-09 21:16:22.911 23653-23653/com.xiucai.softdemo D/new change: left : 0top : 0right : 1080bottom : 817

07-09 21:16:22.911 23653-23653/com.xiucai.softdemo D/old change: oldLeft : 0oldTop : 0oldRight : 1080oldBottom : 1692

//當軟鍵盤關閉

07-09 21:16:44.457 23653-23653/com.xiucai.softdemo D/new change: left : 0top : 0right : 1080bottom : 1692

07-09 21:16:44.457 23653-23653/com.xiucai.softdemo D/old change: oldLeft : 0oldTop : 0oldRight : 1080oldBottom : 817

我們可以通過上述列印結果發現,根布局的bottom發生變化了,從1692變化到817。我們可以通過這種方式來監聽軟鍵盤的彈出和收回,然後實現平移根布局或者其他的操作來保證軟鍵盤不會遮蓋你所需要的顯示的控制項。

(2)通過getViewTreeObserver().addOnGlobalLayoutListener()監聽窗體的可見區域,來判斷軟鍵盤是否彈出。

/** 

 * @param root 最外層布局,需要調整的布局 

 * @param scrollToView 被鍵盤遮擋的scrollToView,滾動root,使scrollToView在root可視區域的底部 

 */  

private void controlKeyboardLayout(final View root, final View scrollToView) {  

    root.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() {  

        @Override  

        public void onGlobalLayout() {  

            Rect rect = new Rect();  

            //獲取root在窗體的可視區域  

            root.getWindowVisibleDisplayFrame(rect);  

            //獲取root在窗體的不可視區域高度(被其他View遮擋的區域高度)  

            int rootInvisibleHeight = root.getRootView().getHeight() - rect.bottom;  

            //若不可視區域高度大於100,則鍵盤顯示  

            if (rootInvisibleHeight > 100) {  

                int[] location = new int[2];  

                //獲取scrollToView在窗體的坐標  

                scrollToView.getLocationInWindow(location);  

                //計算root滾動高度,使scrollToView在可見區域  

                int srollHeight = (location[1] + scrollToView.getHeight()) - rect.bottom;  

                root.scrollTo(0, srollHeight);  

            } else {  

                //鍵盤隱藏  

                root.scrollTo(0, 0);  

            }  

        }  

    });  

}  

上面代碼是借鑑android 解決輸入法鍵盤遮蓋布局問題 這篇文章而來,目的讓大家了解這種方式是怎麼判斷軟鍵盤的彈出和隱藏。

裡面需要平移的View,不一定是ScrollView,其他View也可,一般來說作為XML的根布局即可。

平常一般來說,我會使用這個工具類

package com.xiucai.common.manager;

import android.graphics.Rect;

import android.util.Log;

import android.view.View;

import android.view.ViewTreeObserver;

import java.util.LinkedList;

import java.util.List;

/**

 * Created by SuperD on 2017/5/12.

 */

public class SoftKeyBroadManager  implements ViewTreeObserver.OnGlobalLayoutListener{

    public interface SoftKeyboardStateListener {

        void onSoftKeyboardOpened(int keyboardHeightInPx);

        void onSoftKeyboardClosed();

    }

    private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>();

    private final View activityRootView;

    private int lastSoftKeyboardHeightInPx;

    private boolean isSoftKeyboardOpened;

    public SoftKeyBroadManager(View activityRootView) {

        this(activityRootView, false);

    }

    public SoftKeyBroadManager(View activityRootView, boolean isSoftKeyboardOpened) {

        this.activityRootView = activityRootView;

        this.isSoftKeyboardOpened = isSoftKeyboardOpened;

        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);

    }

    @Override

    public void onGlobalLayout() {

        final Rect r = new Rect();

        //r will be populated with the coordinates of your view that area still visible.

        activityRootView.getWindowVisibleDisplayFrame(r);

        final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);

        Log.d("SoftKeyboardStateHelper", "heightDiff:" + heightDiff);

        if (!isSoftKeyboardOpened && heightDiff > 500) { // if more than 100 pixels, its probably a keyboard...

            isSoftKeyboardOpened = true;

            notifyOnSoftKeyboardOpened(heightDiff);

            //if (isSoftKeyboardOpened && heightDiff < 100)

        } else if (isSoftKeyboardOpened && heightDiff < 500) {

            isSoftKeyboardOpened = false;

            notifyOnSoftKeyboardClosed();

        }

    }

    public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {

        this.isSoftKeyboardOpened = isSoftKeyboardOpened;

    }

    public boolean isSoftKeyboardOpened() {

        return isSoftKeyboardOpened;

    }

    /**

     * Default value is zero (0)

     *

     * @return last saved keyboard height in px

     */

    public int getLastSoftKeyboardHeightInPx() {

        return lastSoftKeyboardHeightInPx;

    }

    public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {

        listeners.add(listener);

    }

    public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {

        listeners.remove(listener);

    }

    private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {

        this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;

        for (SoftKeyboardStateListener listener : listeners) {

            if (listener != null) {

                listener.onSoftKeyboardOpened(keyboardHeightInPx);

            }

        }

    }

    private void notifyOnSoftKeyboardClosed() {

        for (SoftKeyboardStateListener listener : listeners) {

            if (listener != null) {

                listener.onSoftKeyboardClosed();

            }

        }

    }

}

不管是全屏或者其他情況,使用這個工具類來監聽軟鍵盤就可以了,具體的用法:

SoftKeyBroadManager mManager =new SoftKeyBroadManager ("根布局");

//添加軟鍵盤的監聽,然後和上面一樣的操作即可.

mSoftKeyBroadManager.addSoftKeyboardStateListener();

//注意銷毀時,得移除監聽

mSoftKeyBroadManager.removeSoftKeyboardStateListener();

最後如果感覺上面的方案不可行,Github也有一個現成方案,但是博主本人沒試過,原理都是一樣的。大家可以自行取捨。

實際案例4: 軟鍵盤彈出來卡頓,找不到原因?

正常來說博文實差不多到這裡就應該結束了,但是博主在實際開發中,也會遇到一些詭異的現象,例如軟鍵盤彈出卡頓,但是這種情況下,根本無法定位到卡頓原因。

博主遇到這個問題時,懷疑了設置屬性錯誤,懷疑了線程XX沒關,懷疑了布局太過於複雜,總之該想的博主都想了,但是無論怎麼試都是徒勞的。

因為博主犯了一個大錯

在沒找到原因之前,胡亂猜測,可能是這塊?是不是那個的問題,而不確定問題的來源這個問題我感覺大家都會遇到,不從事情的本質上下手,這樣會多花很多時間用在無用的地方,使自己的開發效率很低。

還原下我當時遇到的問題:

當時我在做一個直播間的功能,直播間從主頁跳轉進入的,給大家截一張圖,大家就懂了。

當時做直播間其他功能的時,發現直播間軟鍵盤在彈出和關閉的時候卡頓。

當時懷疑:

(1)什麼動畫沒停,什麼線程沒關。

(2)軟鍵盤 彈出的時候是不是加載的布局太多.

(3)直播間的布局太過於複雜,導致軟鍵盤彈出時繪製卡頓。

在長期的測試發現一個現象,就是在高端機型上這種狀態不明顯,而在底端機型問題比較嚴重,有時候彈出軟鍵盤卡頓很長時間。

過了半個月博主思路換了,想想軟鍵盤彈出卡頓,能不能從卡頓原因下手,來解決問題,後來找到了BlockCanary,接入使用後發現:

原因:竟然是 主頁在軟鍵盤每次彈出或者關閉的時候重新繪製.因為當時BlockCanary當時指向主頁的RecyclerView重繪,我當時想是不可能的.

最後,博主憑直覺認為和主頁SingleTask有關,因為沒有確切的理由,這裡只是提出自己的觀點,而最後問題也解決了。

如果沒有BlockCanary我永遠發現不了,卡頓的原因是在看不見的主頁。

後來我給主頁設置成adjustNothing因為主頁不需要彈出軟鍵盤。

實際案例5:Android鍵盤面板衝突,布局閃動的解決方法

這個問題是我在找Android軟鍵盤相關的問題的時候發現的,這塊我也沒遇到這個問題,所以給大家兩個相關的介紹的地址,希望能對大家有幫助。

解決Android軟鍵盤,布局閃動的相關博文:https://blog.dreamtobe.cn/2015/09/01/keyboard-panel-switch/

解決Android軟鍵盤布局閃動的Demo:https://github.com/angeldevil/KeyboardAwareDemo

開源的Android軟鍵盤布局閃動的解決方案:https://github.com/Jacksgong/JKeyboardPanelSwitch

(1)第一次寫這麼長的博客,感覺會有一些不足,各位看官如果有不合理的地方,或者有誤的地方請直接指出。

(2)本來想整理成一個Demo的,後來簡單看來下,該有的幾乎都貼出來了,有需要的可以按需複製就可以。

(3)寫完這篇博客之後,感覺博客乾貨還是不多,所以定位這篇文章算是總結性質加上實際案例性質的博客。

(4)Android軟鍵盤的總結就差不多到這裡,希望各位看官,如果看到這裡有收穫,就點點讚,灌灌水,頂一波,這樣博主才有寫下去的動力。

(5)感謝小輝同學的校驗,調整了文章中不通順的地方。

想要閱讀更多作者的文章,請點擊【閱讀原文】。

徹底搞定Android開發中軟鍵盤的常見問題:

http://blog.csdn.net/mynameishuangshuai/article/details/51567357

Android UI(EditText)詳解:

http://blog.csdn.net/qq_28057577/article/details/51919965?locationNum=12&fps=1

微信軟鍵盤布局閃動問題:

https://blog.dreamtobe.cn/2015/09/01/keyboard-panel-switch/

相關焦點

  • Android 軟鍵盤的全面解析,讓你不再怕控制項被遮蓋
    本文將從以下幾個方面進行介紹:InputMethodService的源碼解析,從源碼解讀中告訴你為什麼軟鍵盤彈出的是一個DialogAndroid軟鍵盤顯示時,設置windowSoftInputMode的作用EditText設置imeOptions屬性對軟鍵盤的影響軟鍵盤上面的按鍵監聽橫屏狀態下,不希望軟鍵盤顯示全屏怎麼處理
  • Android 軟鍵盤控制方法、以及開發中遇到的一些問題全套解決方案
    360開源全面插件化方案RePlugin—讓你像玩樂高一樣開發APPAndroid 提供了 windowSoftInputMode 屬性來控制輸入法軟鍵盤窗口和 Activity 主窗口的交互,分為 窗口尺寸調整系列 和 輸入法軟鍵盤顯示控制系列。
  • 當Espresso遇見Android單元測試
    工程中使用Espresso實現自動化測試只需要三步:1、添加依賴:androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'androidTestCompile'com.android.support.test:runner:0.5'2、build.gradle
  • 可能是目前最全的《Android面試題及解析》(379頁)
    趁著這段時間,小夥伴們可以參考這份可能是市面上最全面的安卓面試題解析大全!從基礎到架構進階,包含了騰訊、百度、小米、阿里、樂視、美團、58、獵豹、360、新浪、搜狐等一線網際網路公司面試被問到的題目,涵蓋了初中高級安卓技術點。文章中所列主要為大綱部分,詳細內容可以在文末自行獲取哈!
  • android 排列 - CSDN
    它被定製為你屏幕上的一個空白備用區域,之後你可以在其中填充一個單一對象 — 比如,一張你要發布的圖片。所有的子元素將會固定在屏幕的左上角;你不能為FrameLayout中的一個子元素指定一個位置。後一個子元素將會直接在前一個子元素之上進行覆蓋填充,把它們部份或全部擋住(除非後一個子元素是透明的)。
  • android 布局 覆蓋 - CSDN
    項目中listview中嵌套checkbox,將父控制項設置為android:descendantFocusability="blocksDescendants",這樣設置為的是:會覆蓋子類控制項而直接獲得焦點,即點擊listview的item區域即可選中checkbox。
  • 是時候讓 Android Tools 屬性拯救你了
    如果存在像 TextView 或者 ImageView 這種基礎控制項,你是不是還在通過諸如 android:text="xxx" 和 android:src="@drawable/xxx" 的方式來測試和預覽UI效果?
  • android水平布局和垂直布局 - CSDN
    權重:android:layout_weight="1"通過設置控制項的layout_weight屬性以控制各個控制項在布局中的相對大小,線性布局會根據該控制項layout_weight值與其所處布局中所有控制項layout_weight值之和的比值為該控制項分配佔用的區域。
  • 聊聊Android應用Preference組件那點事
    B可用,則A可用;B不可用,則A不可用;android:layout ---- 自定義布局Layout,注意:Layout布局裡的id需要用系統id,比如使用TextView控制項,id為title,以此類推。
  • Android面試題-機型適配,例如三星、小米、華為、魅族等
    [問答] 你在工作中遇到的最複雜的問題或者bug是什麼?你是怎麼搞定的?華為P6和P7場景:使用MIPush,在華為部分手機上無法推送成功。機型:[華為P6,華為P7]解決方案:P6和P7是華為的高端機型,不允許推送,防止騷擾用戶,無解。
  • 【學習經驗】android開發的學習路線
    第二階段:Java Web開發1.Java解析XML文件DOM4J。2.MySql資料庫的應用、多表連接查詢的應用。3.Jsp和Servlet應用。4.Http協議解析。5.Tomcat伺服器的應用配置。6.WebService服務配置應用。
  • Android 4.0新增Space及GridLayout初談
    這裡,當Email address這個標籤的文本發生變化時,既要保持跟其右邊控制項的下部的基線對齊,又要保持跟下面的控制項的右邊緣對齊,而用嵌套布局的方式是不能實現的,因為不能夠同時在X,Y軸上進行控制項的對齊。
  • android 中常用的五種布局 - CSDN
    相對布局特有的屬性:值是某個控制項和布局的idandroid:layout_below 在某控制項的下方android:layout_above 在某控制項的的上方android:layout_toLeftOf 在某控制項的左邊android:layout_toRightOf 在某控制項的右邊android:layout_alignTop 本控制項的上邊緣和某控制項的的上邊緣對齊
  • android基礎入門
    Android工程師在這幾年都是非常搶手的,其實Android的學習卻並沒有我們想像中那麼難,我們通過幾天的學習就可以做出一個簡單的Android應用程式了,但是如果要深入了解Android,還是要花點時間在基礎上面,在這個階段的學習中,將會給大家介紹Android的概況、開發環境的搭建、系統架構、應用程式的核心模塊、應用程式的生命周期、基本的UI控制項
  • android 不同大小的屏幕專題及常見問題 - CSDN
    轉載請註明出處:http://blog.csdn.net/guolin_blog/article/details/8830286原文地址為:http://developer.android.com/training/multiscreen/screensizes.html
  • Android統一風格 —— 主題
    要求所有控制項採用App的總體風格,不過儘管這樣,一個上百人的開發團隊。並不能保證所有的人都能做出一樣的風格,總是會存在這裡或者那裡的細小差別。就拿簡單的文本框來說,文字排版、大小、顏色、字體、內邊距和外邊距等等,在不同的層級中都是有不同的要求。如果稍不注意,在後面的調整中都很費勁。當時做法其實是從一開始都要求統一風格,儘量使用公共控制項。
  • Android ConstraintLayout約束布局可視化工具使用~
    這個演示是解除約束操作:點擊控制項然後滑鼠放到圓點上會變成紅色的圓點,點擊一下就解除了約束,當然你點擊那個紅色的叉就能夠解除所有約束了,還有一種操作就是點擊快捷操作欄的那個叉,這個還是有個小問題:你如果要調整上下的偏移,那麼這個控制項的上下邊的約束必須要有,才能控制偏移。
  • [JAVA] 85天 精通JAVAEE+Android 黑馬程式設計師JavaEE+Android培訓課程60G
    階段案例郵箱帳號激活與簡歷自動篩選工具:原創實戰課堂告訴你如何將學到的技術運用到真實的項目中,了解簡歷自動篩選工具是怎樣寫的,為你的簡歷提高競爭力,讓找工作更有效率。網上在線支付模塊:本系統是為網際網路客戶提供在線支付功能,可立即應用於項目中電子商務項目 – 在線圖書銷售系統:本系統實現網絡的圖書管理 圖書銷售等功能。
  • 使用Junit對Android應用進行單元測試
    簡單解析一下這個界面設計,我們使用了LinearLayout,以使得控制項能在垂直方向豎向排列。界面中包括了顯示標題「Unit Testing Sample」的textview,兩個輸入數字的edittext控制項,一個FrameLayout控制項中包含了一個水平的LinearLayout,在這個LinearLayout包含了一個顯示結果的textview以及其提示文字「Result」,注意的是FrameLayout的背景顏色設置為紅色,而LinearLayou設置成了黑色背景。
  • 【乾貨】學好android為什麼一定要學好java|電子書分享
    【公眾號回復「1024」,送你一個特別推送】聲明原創|本文為codeGoogler授權發布,未經允許請勿轉載