Android開發 - 獲取系統輸入法高度的正確姿勢

2021-02-13 Android編程精選

作者丨ExampleCode

https://www.jianshu.com/p/6ab9c0507705

在Android應用的開發中,有一些需求需要我們獲取到輸入法的高度,但是官方的API並沒有提供類似的方法,所以我們需要自己來實現。

查閱了網上很多資料,試過以後都不理想。

比如有的方法通過監聽布局的變化來計算輸入法的高度,這種方式在Activity的配置中配置

為"android:windowSoftInputMode="adjustResize""時沒有問題,可以正確獲取輸入法的高度,因為布局此時確實會動態的調整。

但是當Activity配置

為"android:windowSoftInputMode="adjustNothing""時,布局不會在輸入法彈出時進行調整,上面的方式就會撲街。

不過經過一番探索和測試,終於發現了一種方式可以在即使設置為adjustNothing時也可以正確計算高度放方法。

GitHub地址:

https://github.com/siebeprojects/samples-keyboardheight/tree/master/app/src/main/java/com/siebeprojects/samples/keyboardheight

其實也就兩個類,我也做了一些修改,解決了一些問題,這裡也貼出來:

KeyboardHeightObserver.java


public interface KeyboardHeightObserver {

    
    void onKeyboardHeightChanged(int height, int orientation);
}


KeyboardHeightProvider.java


public class KeyboardHeightProvider extends PopupWindow {

    
    private final static String TAG = "sample_KeyboardHeightProvider";

    
    private KeyboardHeightObserver observer;

    
    private int keyboardLandscapeHeight;

    
    private int keyboardPortraitHeight;

    
    private View popupView;

    
    private View parentView;

    
    private Activity activity;

    
    public KeyboardHeightProvider(Activity activity) {
        super(activity);
        this.activity = activity;

        LayoutInflater inflator = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        this.popupView = inflator.inflate(R.layout.keyboard_popup_window, null, false);
        setContentView(popupView);

        setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_RESIZE | LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
        setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);

        parentView = activity.findViewById(android.R.id.content);

        setWidth(0);
        setHeight(LayoutParams.MATCH_PARENT);

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

                @Override
                public void onGlobalLayout() {
                    if (popupView != null) {
                        handleOnGlobalLayout();
                    }
                }
            });
    }

    
    public void start() {

        if (!isShowing() && parentView.getWindowToken() != null) {
            setBackgroundDrawable(new ColorDrawable(0));
            showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0);
        }
    }

    
    public void close() {
        this.observer = null;
        dismiss();
    }

    
    public void setKeyboardHeightObserver(KeyboardHeightObserver observer) {
        this.observer = observer;
    }

    
    private int getScreenOrientation() {
        return activity.getResources().getConfiguration().orientation;
    }

    
    private void handleOnGlobalLayout() {

        Point screenSize = new Point();
        activity.getWindowManager().getDefaultDisplay().getSize(screenSize);

        Rect rect = new Rect();
        popupView.getWindowVisibleDisplayFrame(rect);

        
        
        
        int orientation = getScreenOrientation();
        int keyboardHeight = screenSize.y - rect.bottom;

        if (keyboardHeight == 0) {
            notifyKeyboardHeightChanged(0, orientation);
        }
        else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            this.keyboardPortraitHeight = keyboardHeight; 
            notifyKeyboardHeightChanged(keyboardPortraitHeight, orientation);
        } 
        else {
            this.keyboardLandscapeHeight = keyboardHeight; 
            notifyKeyboardHeightChanged(keyboardLandscapeHeight, orientation);
        }
    }

    private void notifyKeyboardHeightChanged(int height, int orientation) {
        if (observer != null) {
            observer.onKeyboardHeightChanged(height, orientation);
        }
    }
}


此處以在Activity中的使用進行舉例。

實現接口

引入這兩個類後,在當前Activity中實現接口KeyboardHeightObserver:

@Override
public void onKeyboardHeightChanged(int height, int orientation) {
    String or = orientation == Configuration.ORIENTATION_PORTRAIT ? "portrait" : "landscape";
    Logger.d(TAG, "onKeyboardHeightChanged in pixels: " + height + " " + or);
}

定義並初始化

在當前Activity定義成員變量,並在onCreate()中進行初始化

private KeyboardHeightProvider mKeyboardHeightProvider;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    mKeyboardHeightProvider = new KeyboardHeightProvider(this);
    new Handler().post(() -> mKeyboardHeightProvider.start());
}

生命周期處理

初始化完成後,我們要在Activity中的生命周期中也要進行處理,以免內存洩露。

@Override
protected void onResume() {
    super.onResume();
    mKeyboardHeightProvider.setKeyboardHeightObserver(this);
}

@Override
protected void onPause() {
    super.onPause();
    mKeyboardHeightProvider.setKeyboardHeightObserver(null);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    mKeyboardHeightProvider.close();
}


此時我們就可以正確獲取的當前輸入法的高度了,即使android:windowSoftInputMode="adjustNothing"時也可以正確獲取到,這正是這個方法的強大之處,利用這個方法可以實現比如類似微信聊天的界面,流暢切換輸入框,表情框等。

 推薦↓↓↓ 

涵蓋:程式設計師大咖、源碼共讀、程式設計師共讀、數據結構與算法、黑客技術和網絡安全、大數據科技、編程前端、Java、Python、Web編程開發、Android、iOS開發、Linux、資料庫研發、幽默程式設計師等。

相關焦點

  • Android如何獲取WebView內容高度
    我最近做了技術預研,難度主要是實時獲取WebView的高度。效果1 . 分頁加載2 . 動態獲取高度,點擊閱讀更多,會將幾個隱藏的div,顯示出來,造成WebView內容高度變化。>問題一,如果獲取WebView高度1.通過js獲取以下是js獲取高度的方法網頁可見區域寬:document.body.clientWidth網頁可見區域高:document.body.clientHeight網頁可見區域寬:document.body.offsetWidth (包括邊線的寬)網頁可見區域高:document.body.offsetHeight
  • 獲取來源IP位址的正確姿勢
    每次和客戶對接,我都花很長的時間跟對方的技術人員解釋如何正確地獲取來源IP位址,但是每家公司的情況都有所差別,沒有一個標準方法。也有一些公司,由於相關的代碼已經存在了很久,沒有人維護,而業務系統的架構已經變更了多次,原有的代碼,獲取出來的IP都是錯的。想像一下,每天都有人在問你:127.0.0.1這個IP是啥?這個IP怎麼發了那麼多請求?這是不是個基站?
  • 深入理解Android輸入法框架(一)
    IInputMethod: 這個是輸入法進程向系統進程暴露的接口。用於系統進程和輸入法進程通信。IInputMethodSession:  這個是輸入法進程向外暴露的第二個接口,用於客戶端進程和輸入法進程通信。IInputContext:這個是客戶端進程向輸入法進程暴露的接口。用於輸入法進程和客戶端進程通信。
  • 基於Android輸入法開發,製作一個微信鬥圖APP
    那麼問題的關鍵就在於如何構建一個輸入法項目,最後為了操作更方便,可以使用輔助功能提升用戶體驗。簡單來說,開發一個輸入法,只需要用到一個核心類和幾個可有可無的輔助類。核心類是InputMethodService,一個輸入法幾乎所有的功能都是由它來實現的,包括鍵盤界面的搭建、鍵盤語言的切換、拼音漢字的轉換、候選詞的展示、文字的上屏等各種邏輯都通過這個類來實現。
  • Android通過藍牙獲取通訊錄
    我們的目標是,在 android 端運行程序,該程序通過某個藍牙協議通過無線的方式獲取另一個手機上的通訊錄。這裡,「另一個手機」可以是 android 設備也可以是 ios 設備。這也是我們選擇採用藍牙協議的一個原因:在某種程度上跨平臺。
  • 深入理解Android輸入法框架(二)輸入法服務的啟動流程剖析
    SERVICE_INTERFACE的輸入法服務, 安裝的第三方輸入法查詢不到 // 假的系統默認輸入法應用為GBoard, 那麼只能找到com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME // SERVICE_INTERFACE = android.view.InputMethod
  • 正確打字-10
    ,有一個舒服且健康的打字姿勢是必要的,不然長期打字會對身體造成傷害,小編給大家演示正確的打字姿勢。1.頭正、頸直、身體挺直、雙腳平踏在地2.身體正對屏幕,調整屏幕,使眼睛舒服3.眼睛平視屏幕,保持30~40釐米的距離4.每隔10分鐘視線從屏幕上移開一次5.手肘高度和鍵盤平行
  • Android獲取WiFi列表的正確用法
    最近有個需求,需要做一個獲取WiFi列表的功能,也在網上找了一些資料,但有些資料是有問題的,然後經過自己的摸索,總結如下:
  • 【安卓】Android 系統必懂的硬知識
    訪問登記屬性:android.permission.ACCESS_CHECKIN_PROPERTIES ,讀取或寫入登記check-in資料庫屬性表的權限獲取錯略位置:android.permission.ACCESS_COARSE_LOCATION,通過WiFi或移動基站的方式獲取用戶錯略的經緯度信息,定位精度大概誤差在30~1500米
  • Android 設備信息獲取詳解
    如需了解更多 系統安全權限的內容,請看 之前寫的文章 Android 系統權限使用詳解四、 獲取手機廠商名、產品名、手機品牌、手機型號、主板名、設備名的方法獲取手機廠商名、產品名、手機品牌、手機型號、主板名、設備名的方法如下: /** * 獲取廠商名 * **/ public static String
  • 機友分享 | 導入機智雲Android開源項目的正確姿勢
    因此只要我們將源碼工程文件成功導入Android Studio,那麼我們便可以自由定製我們的應用程式,那麼如何正確導入到Android Studio中,編譯成功並在真機上運行呢?在機智雲官網,我們定義好數據點後,在服務->應用開發子菜單中,下載工程文件。
  • Android系統開發中Android.mk的多種寫法
    Android.mk在android的系統開發中毫無疑問是很重要的,它的作用比較強大,可以將c/c++代碼編譯成可執行程序,動態庫,靜態庫,當然也可以將
  • 懸浮式鍵盤 -- WI微逸輸入法 #Android
  • 深入理解Android輸入法框架之輸入法如何與第三方APP通信
    ,從傳遞的參數中獲取了輸入法端的交互接口IInputMethodSession,在InputMethodManager實例化的時候獲取了系統服務IInputMethodManager的應用。InputMethodService,這個是輸入法端的核心類,實現了輸入法和系統交互邏輯的默認實現,為第三方輸入法實現提供了輸入法調用的生命周期,降低了第三方開發的難度。那麼在運行時,這些對象是怎麼初始化和傳遞的呢?
  • 方法 | 正確獲取用戶權限
    此外,有些申請授權的方式卻能讓用戶很好的接受,本期談論一下如何正確獲取用戶權限。現下我們在很多產品中會見到通過系統提示窗來讓用戶授權,比起應用授權的好處是,用戶只用點擊一下就可以授權成功,步驟簡單的多。
  • Android自動化之adb模擬操作(可實現按鍵精靈和手機輸入法)
    在android應用裡,也可以執行adb命令,因此,這些操作是可以不藉助電腦、直接在某一android應用裡實現的。按鍵精靈就是通過該技術實現的。Android上執行adb命令首先,應用執行adb命令需要先進入su帳戶;而進入su帳戶,需要手機已被完全root過。類似於在PC上adb shell之後的su(超級管理員)。
  • EditText不彈出輸入法,焦點問題的總結
    )——不一樣的技術分享,不一樣的學習體驗,跟威哥一起學Java,你一定可以!每天早上六點半,我們不見不散。今天偶然瀏覽到一個關於EditText彈出輸入法和焦點的問題,就總結一下跟大家進行分享。首先呢獲取焦點是獲取焦點,彈輸入法是彈輸入法,獲取焦點後並不一定會彈出輸入法。首先我
  • 防範xss的正確姿勢
    來自:Michael-J作者:Michael Jiang連結:http://michael-j.xyz/2016/04/12/防範xss的正確姿勢解決的方法就是針對分享的數據進行全局轉義,事實上已經很多模版系統已經幫我們考慮了這部分問題,例如Django和Jinja2的模版就是默認開啟自動轉義的。如果是前後端分離的場景,也可以有前端來進行escape。我推薦使用「入庫不轉義讀轉義」還有一個原因,那就是前期轉義格式的不確定性和後期輸出的多樣性。如果你正在正在開發一個rest伺服器,你與App使用json格式通信。
  • Taro多端開發的正確姿勢:打造三端統一的網易嚴選(小程序、H5、React Native)
    Taro 多端開發的正確姿勢。筆者曾在 2018 iWeb 峰會 - 廈門站做過《多端統一開發實踐》的分享,提到用 Taro 開發 RN 端的坑與大體思路,並加以實踐。結合趣店 FED 在過去小半年的實踐經驗,我們開發了首個 Taro 三端統一應用:taro-yanxuan(高仿網易嚴選微信小程序),用以探討本文的重點:Taro 開發多端應用的正確姿勢。
  • Android Q 音頻獲取指南
    作者: Don Turner, 開發技術推廣工程師, Android Media 團隊Android Q 新引入的