Android - StatusBar&&NavigationBar適配

關(guān)于沉浸式

大家有沒有覺得我這個標(biāo)題有點欠妥.網(wǎng)上很多人都叫這種適配叫沉浸式適配.實際上就是對StatusBar 和 navigationBar 的存在進(jìn)行隱藏或者是修改顏色(透明).

我的理解

沉浸式應(yīng)該是說將虛擬按鍵和狀態(tài)欄隱藏掉 達(dá)到全屏的效果.讓用戶達(dá)到一種置身于整個屏幕中的感覺扫腺。

先了解一下讀完這篇文章可以學(xué)會什么東西

屏幕延伸狀態(tài)欄(API19以上)
屏幕延伸導(dǎo)航欄(虛擬按鍵 API19以上)
獲取StatusBar高度
獲取NavigationBar高度
設(shè)置StatusBar顏色(API19以上)
設(shè)置NavigationBar顏色(API21以上)

其實以前一直都有用這類的工具類,但都是別人寫下來的,這次整理是因為發(fā)現(xiàn)項目對全面屏的虛擬按鍵不是很適配才整理了一下的课蔬。

先給大家上一張原始的效果圖


原始效果.png
屏幕延伸狀態(tài)欄(API19以上)

JcUtilScreen.extendStatusBar(this);


延伸至狀態(tài)欄.png

整體布局下移

JcUtilScreen.moveViewDown(binding.textview, JcUtilScreen.getStatusBarHeight(this));


延伸至狀態(tài)欄-布局往下移動.png

屏幕延伸導(dǎo)航欄(虛擬按鍵 API19以上)

JcUtilScreen.extendNavigationBar(this);


延伸狀態(tài)欄,延伸虛擬按鍵-布局往下移動.png

設(shè)置NavigationBar顏色(API21以上)

JcUtilScreen.setNavigationBarColor(this, Color.parseColor("#cc7f7f"));


設(shè)置虛擬按鍵顏色.png

設(shè)置StatusBar顏色(API19以上)

JcUtilScreen.setStatusBarColor(this, Color.parseColor("#cc7f7f"));


設(shè)置狀態(tài)欄顏色.png

package com.hjc.screenadaptation;

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * Created by 賣火柴的小女孩 - Jc on 2018/7/31.
 */

public class JcUtilScreen {

    public static Context mContext;
    /**
     * 屏幕密度
     */
    public static float mDensity;
    /**
     * 屏幕寬度
     */
    public static float mScreenWidth;
    /**
     * 屏幕高度
     */
    public static float mScreenHeight;

    public static void init(Context context) {
        mContext = context; //通過構(gòu)造方法來傳入上下文,這個上下文是Aplication的冀泻,在Aplication創(chuàng)建的時候凛驮,創(chuàng)建了
        initScreenSize();//獲取屏幕尺寸的方法
    }

    /**
     * 屏幕的尺寸
     */
    private static void initScreenSize() {
        DisplayMetrics dm = mContext.getResources().getDisplayMetrics();//上下文裆站,通過資源文件獲取到getDisplayMetrics方法
        mDensity = dm.density; //獲取到屏幕的密度
        mScreenHeight = dm.heightPixels;//獲取到屏幕的高度
        mScreenWidth = dm.widthPixels;//獲取到屏幕的寬度
    }

    //延伸Layout至StatusBar - StatusBar透明
    public static void extendStatusBar(Activity activity) {
        //4.4及以上可以使用沉浸式狀態(tài)欄
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

    }

    //延伸布局至NavigationBar - NavigationBar透明 - 不可單獨使用
    public static void extendNavigationBar(Activity activity) {
        //4.4及以上可以使用沉浸式狀態(tài)欄
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        //這里有個坑 單單 當(dāng)Windows設(shè)置FLAG_TRANSLUCENT_NAVIGATION
        //屏幕的顯示內(nèi)容區(qū)域就會延伸到狀態(tài)欄上,但狀態(tài)欄區(qū)域依舊被狀態(tài)欄掩蓋 -- 顏色是主題顏色 colorPrimary
        //解決方案1:布局文件 android:layout_marginTop="" getStatusBarHeight()
        //解決方案2:extendStatusBar() + 解決方案1
    }

    //獲取StatusBar的高度
    public static int getStatusBarHeight(Context context) {
        try {
            Class<?> c = Class.forName("com.android.internal.R$dimen");
            Object obj = c.newInstance();
            Field field = c.getField("status_bar_height");
            int x = Integer.parseInt(field.get(obj).toString());
            return context.getResources().getDimensionPixelSize(x);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    //檢查設(shè)備是否具有NavigationBar - (全面屏返回 Ture)
    public static boolean checkDeviceHasNavigationBar(Activity activity) {
        boolean hasNavigationBar = false;
        Resources rs = activity.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;

    }

    public static int getNavigationBar(Context context) {
        return getScreenHeight(context) - getScreenShowHeight(context);
    }

    //屏幕的全高
    public static int getScreenHeight(Context context) {
        int dpi = 0;
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = windowManager.getDefaultDisplay();
        DisplayMetrics displayMetrics = new DisplayMetrics();
        @SuppressWarnings("rawtypes")
        Class c;
        try {
            c = Class.forName("android.view.Display");
            @SuppressWarnings("unchecked")
            Method method = c.getMethod("getRealMetrics", DisplayMetrics.class);
            method.invoke(display, displayMetrics);
            dpi = displayMetrics.heightPixels;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dpi;
    }

    //屏幕顯示內(nèi)容的高
    public static int getScreenShowHeight(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrics);
        return outMetrics.heightPixels;
    }


    public static void moveViewDown(View view) {
        //sdk 4.4: KITKAT
        int distance = getStatusBarHeight(view.getContext());
        // 父控件
        ViewParent parent = view.getParent();
        if (parent instanceof LinearLayout) {
            // 通過marginTop屬性讓控件往下偏移一段距離
            // 相當(dāng)于:android:layout_marginTop="24dp"
            ((LinearLayout.LayoutParams) view.getLayoutParams()).topMargin = distance;
        } else if (parent instanceof RelativeLayout) {
            ((RelativeLayout.LayoutParams) view.getLayoutParams()).topMargin = distance;
        } else if (parent instanceof FrameLayout) {
            ((FrameLayout.LayoutParams) view.getLayoutParams()).topMargin = distance;
        }
        return;
    }

    public static void moveViewDown(View view, int downHeight) {
        //sdk 4.4: KITKAT
        // 父控件
        ViewParent parent = view.getParent();
        if (parent instanceof LinearLayout) {
            // 通過marginTop屬性讓控件往下偏移一段距離
            // 相當(dāng)于:android:layout_marginTop="24dp"
            ((LinearLayout.LayoutParams) view.getLayoutParams()).topMargin = downHeight;
        } else if (parent instanceof RelativeLayout) {
            ((RelativeLayout.LayoutParams) view.getLayoutParams()).topMargin = downHeight;
        } else if (parent instanceof FrameLayout) {
            ((FrameLayout.LayoutParams) view.getLayoutParams()).topMargin = downHeight;
        }
        return;
    }

    //-- Color 設(shè)置

    //設(shè)置 NavigationBar 顏色 5.0 一下機(jī)型不支持
    public static void setNavigationBarColor(Activity activity, int navigationBarColor) {
        //API21 以上才能設(shè)置
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
            activity.getWindow().setNavigationBarColor(navigationBarColor);
    }

    public static void setStatusBarColor(Activity activity, int statusBarColor) {
        //設(shè)置的extendStatusBar將無效
        //  [5.0 以上
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setStatusBarColor5_0(activity, statusBarColor);
        }

        // [4.4 - 5.0}
        else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            setStatusBarColor4_4_5_0(activity, statusBarColor);
        }
    }

    private static void setStatusBarColor5_0(Activity activity, int statusBarColor) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            //取消狀態(tài)欄透明
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            //添加Flag把狀態(tài)欄設(shè)為可繪制模式
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            //設(shè)置狀態(tài)欄顏色
            window.setStatusBarColor(statusBarColor);
            //設(shè)置系統(tǒng)狀態(tài)欄處于可見狀態(tài)
            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            //讓view不根據(jù)系統(tǒng)窗口來調(diào)整自己的布局
            ViewGroup mContentView = (ViewGroup) window.findViewById(Window.ID_ANDROID_CONTENT);
            View mChildView = mContentView.getChildAt(0);
            if (mChildView != null) {
                ViewCompat.setFitsSystemWindows(mChildView, false);
                ViewCompat.requestApplyInsets(mChildView);
            }
        }
    }

    private static View mStatusBarView;

    private static void setStatusBarColor4_4_5_0(Activity activity, int statusBarColor) {
        //4.4 - 5.0 不支持直接設(shè)置statusBarHeight
        //思路:全屏 - 添加一個View置頂 高為getStatusBarHeight
        Window window = activity.getWindow();
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        //獲取狀態(tài)欄的高度
        int statusBarHeight = getStatusBarHeight(activity);
        //將頂部空間的top padding設(shè)置為和狀態(tài)欄一樣的高度,以此達(dá)到預(yù)期的效果
        ViewGroup root = (ViewGroup) activity.findViewById(android.R.id.content);
        if (mStatusBarView == null) {
            mStatusBarView = new View(activity);
            mStatusBarView.setBackgroundColor(statusBarColor);
        } else {
            // 先解除父子控件關(guān)系黔夭,否則重復(fù)把一個控件多次
            // 添加到其它父控件中會出錯
            ViewParent parent = mStatusBarView.getParent();
            if (parent != null) {
                ViewGroup viewGroup = (ViewGroup) parent;
                if (viewGroup != null)
                    viewGroup.removeView(mStatusBarView);
            }
        }
        ViewGroup.LayoutParams param = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                statusBarHeight);
        root.addView(mStatusBarView, param);
        return;
    }

    //-- 單位 轉(zhuǎn)換

    /**
     * dp轉(zhuǎn)px
     *
     * @param context
     * @param dpVal
     * @return
     */
    public static int dp2px(Context context, float dpVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics());
    }

    /**
     * sp轉(zhuǎn)px
     *
     * @param context
     * @param spVal
     * @return
     */
    public static int sp2px(Context context, float spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                spVal, context.getResources().getDisplayMetrics());
    }

    /**
     * px轉(zhuǎn)dp
     *
     * @param context
     * @param pxVal
     * @return
     */
    public static float px2dp(Context context, float pxVal) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (pxVal / scale);
    }

    /**
     * px轉(zhuǎn)sp
     *
     * @param context
     * @param pxVal
     * @return
     */
    public static float px2sp(Context context, float pxVal) {
        return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);
    }
}

至于我寫這個工具類的時候并沒有使用到全屏(標(biāo)題欄懸浮)的效果,所以我就沒有寫這個方法上去了宏胯。

public static void fullScreen(Activity activity) {
        activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }

這個方法呢.跟extendStatusBar差不多。extendStatusBar的狀態(tài)欄任然顯示只不過是透明的而已本姥。
做視頻播放的時候可以考慮使用一下這個fullScreen肩袍。禁止不動狀態(tài)欄自動隱藏 , 觸摸屏幕 狀態(tài)欄顯示幾秒鐘鳖擒。

關(guān)于全面屏的NavigationBar

全面屏都沒有Home鍵 (實際觸摸按鍵),所以理論上都支持NavigationBar的,我自己的小米8是支持的闷营。當(dāng)你打開全面屏模式的時候,去checkDeviceHasNavigationBar() ,他是返回True回來的皮仁。
關(guān)于適配通過 Settings.Secure.getInt() 去拿到各大廠商不一樣的設(shè)置值,判斷用戶手機(jī)是否開啟了虛擬按鍵功能剃根。

我個人覺得適配狀態(tài)欄和導(dǎo)航欄,這個類已經(jīng)夠用了.有補(bǔ)充的小伙伴.歡迎提出.代碼有Bug,歡迎找我.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市艰管,隨后出現(xiàn)的幾起案子滓侍,更是在濱河造成了極大的恐慌,老刑警劉巖牲芋,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撩笆,死亡現(xiàn)場離奇詭異,居然都是意外死亡缸浦,警方通過查閱死者的電腦和手機(jī)夕冲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裂逐,“玉大人耘擂,你說我怎么就攤上這事⌒跄罚” “怎么了醉冤?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長篙悯。 經(jīng)常有香客問我蚁阳,道長,這世上最難降的妖魔是什么鸽照? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任螺捐,我火速辦了婚禮,結(jié)果婚禮上矮燎,老公的妹妹穿的比我還像新娘定血。我一直安慰自己,他們只是感情好诞外,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布澜沟。 她就那樣靜靜地躺著,像睡著了一般峡谊。 火紅的嫁衣襯著肌膚如雪茫虽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天既们,我揣著相機(jī)與錄音濒析,去河邊找鬼。 笑死啥纸,一個胖子當(dāng)著我的面吹牛号杏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斯棒,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼盾致,長吁一口氣:“原來是場噩夢啊……” “哼莹妒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绰上,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤旨怠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蜈块,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鉴腻,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年百揭,在試婚紗的時候發(fā)現(xiàn)自己被綠了爽哎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡器一,死狀恐怖课锌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情祈秕,我是刑警寧澤渺贤,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站请毛,受9級特大地震影響志鞍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜方仿,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一固棚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧仙蚜,春花似錦此洲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至艳丛,卻和暖如春匣掸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氮双。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留霎匈,地道東北人戴差。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像铛嘱,于是被迫代替她去往敵國和親暖释。 傳聞我的和親對象是個殘疾皇子袭厂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容

  • 為了更好的閱讀體驗,請轉(zhuǎn)到我的個人站點:Windy'Journal 關(guān)于全面屏 全面屏是手機(jī)業(yè)界對于超高屏占比手機(jī)...
    Windy_816閱讀 19,504評論 8 43
  • 1球匕、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,971評論 3 119
  • 父親節(jié)小記 緊張又忙碌的加班終于暫時告一段落纹磺,換來半天休假。下午隨小珺姐一起看了一場文藝演出亮曹,之后在KFC陪著小黃...
    Miss鹿琳閱讀 248評論 0 0
  • 上古橄杨。夜,肯定比今天特別特別漫長照卦。因為沒有電視式矫,沒有WIFI,更沒有夜生活役耕。 一天的狩獵采转、捕魚、捉鳥瞬痘。身軀疲憊故慈,心...
    白魚書簡閱讀 151評論 0 0
  • 每日推薦: 每日一歌――樸樹《獵戶星座》 每日一詩――宋·鄭思肖《畫菊》 花開不并百花叢, 獨立疏籬趣未窮框全。 寧可...
    薩拉芯雪閱讀 608評論 0 1