天天看點

Android适配底部傳回鍵等虛拟鍵盤的完美解決方案

這個問題來來回回困擾了我很久,一直沒能妥善解決。

場景1:華為手機遮擋了螢幕底部。

場景2:進入應用時,虛拟鍵自動縮回,留下空白區域。

需求:

  • 需要安卓能自适應底部虛拟按鍵,使用者隐藏虛拟按鍵時應用要占滿整個螢幕,當使用者啟用虛拟鍵時,應用能往上收縮,等于是被底部虛拟按鍵頂上來。
  • 需求很簡單,實作起來卻困難重重。

完美解決方案:

解釋一下下面的代碼,就是監聽某個視圖的變化,當可以看見的高度發生變化時,就對這個視圖重新布局,保證視圖不會被遮擋,也不會浪費螢幕空間。這一點尤其可用在像華為手機等可以隐藏和顯示虛拟鍵盤上導緻螢幕變化的手機上。

  • 首先添加工具類AndroidBug54971Workaround
package com.xxxx.xxxx;

import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;

/**
 * Created by win7 on 2016/12/14.
 */

public class AndroidBug54971Workaround {
    // For more information, see https://code.google.com/p/android/issues/detail?id=5497
    // To use this class, simply invoke assistActivity() on an Activity that already has its content view set.

    /**
     * 關聯要監聽的視圖
     *
     * @param viewObserving
     */
    public static void assistActivity(View viewObserving) {
        new AndroidBug54971Workaround(viewObserving);
    }

    private View mViewObserved;//被監聽的視圖
    private int usableHeightPrevious;//視圖變化前的可用高度
    private ViewGroup.LayoutParams frameLayoutParams;

    private AndroidBug54971Workaround(View viewObserving) {
        mViewObserved = viewObserving;
        //給View添加全局的布局監聽器
        mViewObserved.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                resetLayoutByUsableHeight(computeUsableHeight());
            }
        });
        frameLayoutParams = mViewObserved.getLayoutParams();
    }

    private void resetLayoutByUsableHeight(int usableHeightNow) {
        //比較布局變化前後的View的可用高度
        if (usableHeightNow != usableHeightPrevious) {
            //如果兩次高度不一緻
            //将目前的View的可用高度設定成View的實際高度
            frameLayoutParams.height = usableHeightNow;
            mViewObserved.requestLayout();//請求重新布局
            usableHeightPrevious = usableHeightNow;
        }
    }

    /**
     * 計算視圖可視高度
     *
     * @return
     */
    private int computeUsableHeight() {
        Rect r = new Rect();
        mViewObserved.getWindowVisibleDisplayFrame(r);
        return (r.bottom - r.top);
    }
}
           
  • 然後在你需要解決這個問題的Activity的onCreate方法的setContentView(R.layout.content_frame);後面添加上
setContentView(R.layout.content_frame);
 AndroidBug54971Workaround.assistActivity(findViewById(android.R.id.content));
           

如果你看的懂代碼,你肯定知道assistActivity方法裡放入的View是你 要調整高度的視圖。

其他不完美方案:或多或少在某些情況上會起不到作用

我一種方法:

android:fitsSystemWindows=”true”

這句話寫在layout的根目錄下,看名字就知道是自适應系統視窗。估計能解決很大一部分手機了,可是在同僚的nexus 4下并沒有什麼用。

第二種方法:

我去掉了每個布局的android:fitsSystemWindows=”true”

在style檔案中添加了這句話。

注意: 你會發現系統報錯,這是因為這句話是在API-19後才有的,是以你可以複制你的style檔案,把它放到API-19的檔案夾下。這樣的用途就是如果手機大于等于API19,就會用API-19的檔案夾下的内容。否則用原來的style檔案。你在API19檔案夾下的style檔案的根主題中添加上面這句話就OK啦。

本來我以為是完美解決了我的問題。可是被打臉了。剛進入App時會出現上面的場景2的情況。

我一看MainActivity中的onCreate方法的setContentView(R.layout.xxxx);之前有下面的代碼

//控制底部虛拟鍵盤
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
//                        | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                        | View.SYSTEM_UI_FLAG_IMMERSIVE);
           

估計是這個項目以前的仁兄為了解決這個問題添加的。

經過多次調試,我添加了一句話

//控制底部虛拟鍵盤
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
//                        | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                        | View.SYSTEM_UI_FLAG_IMMERSIVE);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
           

場景2的情況解決了。這是在虛拟鍵一直存在的情況下沒有問題了,因為nexus不能手動隐藏虛拟鍵盤,是以我也不清楚是否能在華為等手機上正常運作。TODO。

另外如果想要一直隐藏虛拟鍵盤,點選螢幕也不會出現的話,将上面的代碼換成:

//讓虛拟鍵盤一直不顯示
        Window window = getWindow();
        WindowManager.LayoutParams params = window.getAttributes();
        params.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_IMMERSIVE;
        window.setAttributes(params);