沉浸式狀態欄4.4及5.0以上通用

Android從4.4開始有沉浸式狀態欄效果,5.0以前的實現很簡單:

如果手機版本大於等於4.4,就實現沉浸式狀態欄效果:

//4.4版本及以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    window.setFlags(
            WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
            WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
但是5.0及其以上的,還是有個小藍條在最上面,沒有達到沉浸式狀態欄效果,對於5.0及其以上系統的解決辦法:

//5.0版本及以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
            | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
    window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    window.setStatusBarColor(Color.TRANSPARENT);
    window.setNavigationBarColor(Color.BLACK);
}

綜上,可以寫個基類,讓其他activity繼承此基類即可:

public class BaseActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        immersiveStatusBar();
    }

    private void immersiveStatusBar() {
        Window window = getWindow();
        //4.4版本及以上
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            window.setFlags(
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
        //5.0版本及以上
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
                    | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);
            window.setNavigationBarColor(Color.BLACK);
        }
        if (AndroidWorkaround.checkDeviceHasNavigationBar(this)) {
            AndroidWorkaround.assistActivity(findViewById(android.R.id.content));
        }
    }
}

這樣,所有系統均可適配沉浸式狀態欄了。

注:使用沉浸式狀態欄後,有的華爲手機下面的虛擬按鍵會把項目的底部導航(如果有的話)給遮蓋,解決辦法:

一、概述

在項目中,測試發現在一些華爲手機的屏幕適配上出現了問題,主要是因爲華爲Mate等一些系列的手機有一個虛擬按鍵的設計.當這些虛擬按鍵由用戶手勢滑出,或默認顯示的話,就會遮擋我們本身的應用佈局.比如歡迎界面過後是四個Fragment,那麼底部的四個tab就會被虛擬的導航欄遮住,非常難看.

這裏寫圖片描述

當然,歡迎頁的圖片適配也同樣會出現問題. 
Google後得出第一個問題的解決方案.第二個圖片的問題則用自己摸索的方式解決,當然也非常簡單.

二、佈局由於虛擬按鍵導致導航欄頂上去的解決方法

在我們的項目中加載Fragment的MainActivity,以及其他一般的Activity繼承的BaseActivity中的onCreate方法中添加如下代碼:

if (AndroidWorkaround.checkDeviceHasNavigationBar(this)) {
   AndroidWorkaround.assistActivity(findViewById(android.R.id.content));
}

其中AndroidWorkaround使我們爲了解決該問題而封裝的類,也可以看作是一個特定的工具類:

/**
* 解決底部屏幕按鍵適配
* Created by Mercury on 2016/10/25.
*/
public class AndroidWorkaround {

    public static void assistActivity(View content) {
        new AndroidWorkaround(content);
    }

    private View mChildOfContent;
    private int usableHeightPrevious;
    private ViewGroup.LayoutParams frameLayoutParams;

    private AndroidWorkaround(View content) {
        mChildOfContent = content;
        mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                possiblyResizeChildOfContent();
            }
        });
        frameLayoutParams = mChildOfContent.getLayoutParams();
    }

    private void possiblyResizeChildOfContent() {
        int usableHeightNow = computeUsableHeight();
        if (usableHeightNow != usableHeightPrevious) {

            frameLayoutParams.height = usableHeightNow;
            mChildOfContent.requestLayout();
            usableHeightPrevious = usableHeightNow;
        }
    }

    private int computeUsableHeight() {
        Rect r = new Rect();
        mChildOfContent.getWindowVisibleDisplayFrame(r);
        return (r.bottom);
    }

    public static boolean checkDeviceHasNavigationBar(Context context) {
        boolean hasNavigationBar = false;
        Resources rs = context.getResources();
        int id = rs.getIdentifier("config_showNavigationBar", "bool", "android");
        if (id > 0) {
            hasNavigationBar = rs.getBoolean(id);
        }
        try {
            Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
            Method m = systemPropertiesClass.getMethod("get", String.class);
            String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys");
            if ("1".equals(navBarOverride)) {
                hasNavigationBar = false;
            } else if ("0".equals(navBarOverride)) {
                hasNavigationBar = true;
            }
        } catch (Exception e) {

        }
        return hasNavigationBar;

    }

}

重新測試,發現無論是否彈出虛擬按鍵,都不會再次遮擋tab按鈕。

三、原理

上面的代碼需要在setContentView後面執行。其最初的解決方案是stackoverflow上有人爲了適配軟鍵盤在全屏下的佈局問題。 
開始先判斷該設備上是否存在導航欄。爲什麼用findViewById(Android.R.id.content)呢?因爲android.R.id.content這個id代表的就是所在頁面的根佈局,而並不需要特別指定一個id給該佈局。可以通過調用系統API返回的結果,也可以通過判斷該手機是否爲華爲手機,操作系統屬於哪種類型來來判斷。
一旦確定該設備存在導航欄,將對該佈局進行重新測量。首先mChildOfContent得到其視圖樹,對全局高度實現監聽。

OnGlobalLayoutListener 是ViewTreeObserver的內部類,當一個視圖樹的佈局發生改變時,可以被ViewTreeObserver監聽到,這是一個註冊監聽視圖樹的觀察者(observer),在視圖樹的全局事件改變時得到通知。ViewTreeObserver不能直接實例化,而是通過getViewTreeObserver()獲得。

接着得到視圖目前可用的總高度,將其賦值給mChildOfContent的佈局高度。調用requestLayout,讓mChildOfContent要求自己的parent view對自己重新設置位置。

四、全屏圖片的適配

解決了佈局的問題,再來看歡迎頁啓動時候全屏圖片的適配問題。發現該方法對於圖片不適用。如下圖,當虛擬按鍵彈出時,圖片照樣被遮擋了底部的一小部分。 
這裏寫圖片描述 
如果隱藏虛擬按鍵,圖片大小恢復正常 
這裏寫圖片描述 
仔細想想,對於一個ImageView直接佔據一個layout的情況,是沒有必要再去寫一些代碼進行適配的。到佈局裏一看,發現ImageView的屬性 android:scaleType=”centerCrop” 
將其改爲 android:scaleType=”fitXY”就可以解決了。這樣圖片可能高度會隨着虛擬鍵的彈出而壓縮,但是很好的適配了佈局高度的變化而不會被遮擋。


發佈了78 篇原創文章 · 獲贊 116 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章