Android劉海屏適配精煉詳解

一、前期基礎(chǔ)知識儲備

image

話不多說病瞳,這么多劉海屏手機今年集中爆發(fā),所以盡管劉海屏不好看悲酷,但是還是要適配套菜。

2017年蘋果X開啟了劉海屏?xí)r代,2018年集中爆發(fā)设易,紛紛采取劉海屏這一策略來實現(xiàn)全面屏的概念(看36氪中的新聞逗柴,明年是5G元年,同時三星推出了折疊屏顿肺,未來的手機主流趨勢是否會發(fā)生改變暫不得而知戏溺,但劉海屏不會退出市場,淡出視野這一點是確定的)屠尊,所以Android手機對于劉海屏的適配也是比較重要的旷祸。所謂適配劉海屏,其實就是處理與劉海齊平的手機屏幕部分讼昆,這也是所有劉海屏手機系統(tǒng)自帶的一個可選項:是否顯示劉海屏托享,以華為劉海屏為例,是否顯示劉海屏前后效果如下:

??

image
image

從上面的圖中我們可以發(fā)現(xiàn)這幾個重要的適配信息:

①與劉海屏齊平的手機屏幕部分實際上是手機的狀態(tài)欄浸赫;

②顯示劉海屏闰围,劉海部分顯示的狀態(tài)背景色為APP應(yīng)用背景色,狀態(tài)欄文字圖標(biāo)部分變?yōu)楹谏?/p>

③不顯示劉海屏掺炭,則劉海部分顯示的狀態(tài)欄為手機原始狀態(tài)欄,電量標(biāo)志凭戴、事件涧狮、運營商信息都是白字;

所以適配劉海屏的關(guān)鍵在于:

①判斷是否是劉海屏,不是劉海屏就隱藏狀態(tài)欄者冤,是劉海屏則顯示狀態(tài)欄肤视,同時對狀態(tài)欄做出相應(yīng)處理;

②如果是劉海屏涉枫,則顯示的狀態(tài)欄顏色變?yōu)锳PP應(yīng)用本身的背景色邢滑;

③其次狀態(tài)欄中的圖標(biāo)、文字等信息是否需要變色(應(yīng)用為深色背景色時定為白色愿汰,應(yīng)用為淺色背景色時定為黑色)

二困后、上代碼 具體實現(xiàn)

1)判斷是否是劉海屏手機 工具類 judgeNotchUtils


    /**
    * 判斷是否是劉海屏 寫在Activity基類BaseActivity onCreate()方法中或者單獨設(shè)置

    * 國內(nèi)主流手機小米 華為 VIVO OPPO劉海屏判斷

    * @return

    */
    public static boolean hasNotchScreen(Activity activity){
        if (getInt("ro.miui.notch",activity) == 1 || hasNotchInHuawei(activity) || hasNotchInVivo(activity)
                || hasNotchInOppo(activity) || hasNotchInXiaomi(activity)){
            return true;
        }
        return false;
    }

    /**
    * Android P 版本判斷 需要應(yīng)用的CompileSDKVersion設(shè)為28
    * 其他劉海屏手機判斷
    * @param activity
    * @return
    */
    public static DisplayCutout isAndroidP(Activity activity){
        View decorView = activity.getWindow().getDecorView();
        if (decorView != null && android.os.Build.VERSION.SDK_INT >= 28){
            WindowInsets windowInsets = decorView.getRootWindowInsets();
          if (windowInsets != null)
                return windowInsets.getDisplayCutout();
        }
      return null;
    }

    /**
    * 小米劉海屏判斷
    * @return 0 if it is not notch ; return 1 means notch
    * @throws IllegalArgumentException if the key exceeds 32 characters
    */
    public static int getInt(String key,Activity activity) {
        int result = 0;
        if (isXiaomi()){
            try {
                ClassLoader classLoader = activity.getClassLoader();
                @SuppressWarnings("rawtypes")
                Class SystemProperties = classLoader.loadClass("android.os.SystemProperties");
                @SuppressWarnings("rawtypes")
                Class[] paramTypes = new Class[2];
                paramTypes[0] = String.class;
                paramTypes[1] = int.class;
                Method getInt = SystemProperties.getMethod("getInt", paramTypes);
                Object[] params = new Object[2];
                params[0] = new String(key);
                params[1] = new Integer(0);
                result = (Integer) getInt.invoke(SystemProperties, params);
            } catch (Exception e) {
                return result;
            }
        }
        return result;
    }
    public static boolean hasNotchInXiaomi(Context context) {
        if (isXiaomi()) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                int result = 0;
                int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
                if (resourceId > 0) {
                    result = context.getResources().getDimensionPixelSize(resourceId);
                }
                if (result > 0) {
                    return true;
                } else {
                    return false;
                }
            }
        }
        return false;
    }

    /**
    * 華為劉海屏判斷
    * @return
    */
    public static boolean hasNotchInHuawei(Context context) {
        boolean hasNotch = false;
        try {
            ClassLoader cl = context.getClassLoader();
            Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
            Method hasNotchInScreen = HwNotchSizeUtil.getMethod("hasNotchInScreen");
            if(hasNotchInScreen != null) {
                hasNotch = (boolean) hasNotchInScreen.invoke(HwNotchSizeUtil);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return hasNotch;
    }

    /**
    * VIVO劉海屏判斷
    * @return
    */
    public static boolean hasNotchInVivo(Context context) {
        boolean hasNotch = false;
        try {
            ClassLoader cl = context.getClassLoader();
            Class ftFeature = cl.loadClass("android.util.FtFeature");
            Method[] methods = ftFeature.getDeclaredMethods();
            if (methods != null) {
                for (int i = 0; i < methods.length; i++) {
                    Method method = methods[i];
                    if(method != null) {
                        if (method.getName().equalsIgnoreCase("isFeatureSupport")) {
                            hasNotch = (boolean) method.invoke(ftFeature, 0x00000020);
                            break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            hasNotch = false;
        }
        return hasNotch;
    }

    /**
    * OPPO劉海屏判斷
    * @return
    */
    public static boolean hasNotchInOppo(Context context) {
        return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
    }
    public static boolean isXiaomi() {
        return "Xiaomi".equals(Build.MANUFACTURER);
    }

使用代碼:

if(judgeNotchUtils.hasNotchScreen(BaseActivity.this)){
        // 有劉海屏的處理
        // 顯示狀態(tài)欄
        // 狀態(tài)欄文字、圖標(biāo)顏色控制
        } else {
        // 無劉海屏的處理   
        // 隱藏狀態(tài)欄
        hideStatusBar();
    }

    public void hideStatusBar() {
        if (Build.VERSION.SDK_INT < 30) {     getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);          getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        } else {
            View decorView = getWindow().getDecorView();
           int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
            decorView.setSystemUiVisibility(uiOptions);
        }
    }

2)狀態(tài)欄文字圖標(biāo)顏色控制 工具類 StatusBarUtils


public class StatusBarUtils {
/**
* 修改狀態(tài)欄為全透明
* @param activity
*/
@TargetApi(19)
public static void transparencyBar(Activity activity){
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Window window = activity.getWindow();
  window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
       window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(Color.TRANSPARENT);
    } else
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        Window window =activity.getWindow();        window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,                WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    }
}

/**
* 修改狀態(tài)欄顏色衬廷,支持4.4以上版本
* @param activity
* @param colorId
*/
public static void setStatusBarColor(Activity activity,int colorId) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Window window = activity.getWindow();
//      window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);         window.setStatusBarColor(activity.getResources().getColor(colorId));
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        //使用SystemBarTint庫使4.4版本狀態(tài)欄變色摇予,需要先將狀態(tài)欄設(shè)置為透明
        transparencyBar(activity);
        SystemBarTintManager tintManager = new SystemBarTintManager(activity);
        tintManager.setStatusBarTintEnabled(true);
        tintManager.setStatusBarTintResource(colorId);
    }
}

/**
*狀態(tài)欄亮色模式,設(shè)置狀態(tài)欄黑色文字吗跋、圖標(biāo)侧戴,
* 適配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android
* @param activity
* @return 1:MIUUI 2:Flyme 3:android6.0
*/

public static int StatusBarLightMode(Activity activity){
    int result=0;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        if(MIUISetStatusBarLightMode(activity, true)){
            result=1;
        }else if(FlymeSetStatusBarLightMode(activity.getWindow(), true)){
            result=2;
        }else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
            result=3;
        }
    }
    return result;
}

/**
* 已知系統(tǒng)類型時跌宛,設(shè)置狀態(tài)欄黑色文字酗宋、圖標(biāo)。
* 適配4.4以上版本MIUIV疆拘、Flyme和6.0以上版本其他Android
* @param activity
* @param type 1:MIUUI 2:Flyme 3:android6.0
*/
public static void StatusBarLightMode(Activity activity,int type){
    if(type==1){
      MIUISetStatusBarLightMode(activity, true);
    }else if(type==2){
        FlymeSetStatusBarLightMode(activity.getWindow(), true);
    }else if(type==3){
        activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
    }
}

/**
* 狀態(tài)欄暗色模式蜕猫,清除MIUI、flyme或6.0以上版本狀態(tài)欄黑色文字入问、圖標(biāo)
*/
public static void StatusBarDarkMode(Activity activity,int type){
    if(type==1){
        MIUISetStatusBarLightMode(activity, false);
    }else if(type==2){
        FlymeSetStatusBarLightMode(activity.getWindow(), false);
    }else if(type==3){        activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
    }
}

/**
* 設(shè)置狀態(tài)欄圖標(biāo)為深色和魅族特定的文字風(fēng)格
* 可以用來判斷是否為Flyme用戶
* @param window 需要設(shè)置的窗口
* @param dark 是否把狀態(tài)欄文字及圖標(biāo)顏色設(shè)置為深色
* @return  boolean 成功執(zhí)行返回true
*
*/
public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) {
    boolean result = false;
    if (window != null) {
        try {
            WindowManager.LayoutParams lp = window.getAttributes();
            Field darkFlag = WindowManager.LayoutParams.class                 .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
            Field meizuFlags = WindowManager.LayoutParams.class
                    .getDeclaredField("meizuFlags");
            darkFlag.setAccessible(true);
            meizuFlags.setAccessible(true);
            int bit = darkFlag.getInt(null);
            int value = meizuFlags.getInt(lp);
            if (dark) {
               value |= bit;
            } else {
                value &= ~bit;
            }
            meizuFlags.setInt(lp, value);
            window.setAttributes(lp);
            result = true;
        } catch (Exception e) {
        }
    }
    return result;
}

/**
* 需要MIUIV6以上
* @param activity
* @param dark 是否把狀態(tài)欄文字及圖標(biāo)顏色設(shè)置為深色
* @return  boolean 成功執(zhí)行返回true
*
*/
public static boolean MIUISetStatusBarLightMode(Activity activity, boolean dark) {
    boolean result = false;
    Window window=activity.getWindow();
    if (window != null) {
        Class clazz = window.getClass();
        try {
            int darkModeFlag = 0;
            Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
            Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
            darkModeFlag = field.getInt(layoutParams);
            Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
            if(dark){
                extraFlagField.invoke(window,darkModeFlag,darkModeFlag);//狀態(tài)欄透明且黑色字體
            }else{
                extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字體
            }
            result=true;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                //開發(fā)版 7.7.13 及以后版本采用了系統(tǒng)API丹锹,舊方法無效但不會報錯,所以兩個方式都要加上
                if(dark){
                    activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
                }else {                    activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
                }
            }
        }catch (Exception e){
        }
    }
    return result;
}
}

使用時代碼如下: 比如博主的開發(fā)的應(yīng)用是淺色背景色芬失,所以標(biāo)題欄也被設(shè)為淺色楣黍,此時應(yīng)該修改狀態(tài)欄顯色黑色文字圖標(biāo)

if(judgeNotchUtils.hasNotchScreen(BaseActivity.this)){
        // 有劉海屏的處理
        // 顯示狀態(tài)啦
        // 將狀態(tài)欄文字圖標(biāo)設(shè)為黑色
        showStatusBar();
        StatusBarUtils.StatusBarLightMode(BaseActivity.this)
    } else {
        // 無劉海屏的處理 隱藏狀態(tài)欄 
        hideStatusBar();
    }
    //顯示狀態(tài)欄
    public void showStatusBar() {
        if (Build.VERSION.SDK_INT < 30) {          getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        } else {
            View decorView = getWindow().getDecorView();
            int uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
            decorView.setSystemUiVisibility(uiOptions);
        }
    }

    //隱藏狀態(tài)欄
    public void hideStatusBar() {
        if (Build.VERSION.SDK_INT < 30) {            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                   WindowManager.LayoutParams.FLAG_FULLSCREEN);           getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        } else {
            View decorView = getWindow().getDecorView();
            int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
            decorView.setSystemUiVisibility(uiOptions);
        }
    }

同時將Activity根布局的fitsSystemWindows屬性設(shè)為true(默認(rèn)為false),此時根布局的paddding屬性由系統(tǒng)設(shè)置棱烂,用戶在布局文件中設(shè)置的 padding會被忽略租漂。系統(tǒng)會為該View設(shè)置一個paddingTop,值為statusbar(狀態(tài)欄)的高度颊糜。即此時應(yīng)用的Content不會和系統(tǒng)狀態(tài)欄發(fā)生重疊哩治。

android:fitsSystemWindows="true"

若不設(shè)置此屬性,則Activity內(nèi)容會與系統(tǒng)狀態(tài)欄發(fā)生重疊衬鱼。(╯﹏╰)(當(dāng)時調(diào)了好久)(╯﹏╰)

效果圖如下:

①應(yīng)用頁:狀態(tài)欄文字业筏、圖標(biāo)設(shè)為黑色:

image

image

②歡迎頁:狀態(tài)欄文字、圖標(biāo)不改變顏色鸟赫,仍為白色:

image

以上圖片蒜胖,讀者湊合看一下消别,不好截劉海屏的小劉海,所以后期自己加了形狀表示一下台谢。o(╯□╰)oo(╯□╰)oo(╯□╰)o

③不適配劉海屏?xí)r(直接隱藏狀態(tài)欄寻狂,劉海屏手機使用體驗感稍差)

在此附上各劉海屏手機廠商的劉海屏適配方案(謝謝奧特曼超人博主的分享):

android 兼容huawei手機劉海屏解決方案

android 兼容vivo手機劉海屏解決方案

android兼容oppo手機劉海屏解決方案

android兼容小米xiaomi劉海屏解決方案

android 關(guān)于google劉海屏的解決方案

最后,更多劉海屏適配文章推薦:

CSDN

Android 劉海屏適配全攻略- Android P 模擬器可模擬劉海屏

android 全面屏/劉海屏有效適配

詳解Android劉海屏適配

簡書

適配Android劉海屏小結(jié)

Android 劉海屏適配總結(jié)

博客園

一大波 Android 劉海屏來襲朋沮,全網(wǎng)最全適配技巧蛇券!

注:博主博客會同步發(fā)布到CSDN,歡迎讀者閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末樊拓,一起剝皮案震驚了整個濱河市纠亚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骑脱,老刑警劉巖菜枷,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異叁丧,居然都是意外死亡啤誊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門拥娄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚊锹,“玉大人,你說我怎么就攤上這事稚瘾∧道ィ” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵摊欠,是天一觀的道長丢烘。 經(jīng)常有香客問我,道長些椒,這世上最難降的妖魔是什么播瞳? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮免糕,結(jié)果婚禮上赢乓,老公的妹妹穿的比我還像新娘。我一直安慰自己石窑,他們只是感情好牌芋,可當(dāng)我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著松逊,像睡著了一般躺屁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上经宏,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天犀暑,我揣著相機與錄音熄捍,去河邊找鬼。 笑死母怜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缚柏。 我是一名探鬼主播苹熏,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼币喧!你這毒婦竟也來了轨域?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤杀餐,失蹤者是張志新(化名)和其女友劉穎干发,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體史翘,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡枉长,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了琼讽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片必峰。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖钻蹬,靈堂內(nèi)的尸體忽然破棺而出吼蚁,到底是詐尸還是另有隱情,我是刑警寧澤问欠,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布肝匆,位于F島的核電站,受9級特大地震影響顺献,放射性物質(zhì)發(fā)生泄漏旗国。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一滚澜、第九天 我趴在偏房一處隱蔽的房頂上張望粗仓。 院中可真熱鬧,春花似錦设捐、人聲如沸借浊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚂斤。三九已至,卻和暖如春槐沼,著一層夾襖步出監(jiān)牢的瞬間曙蒸,已是汗流浹背捌治。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纽窟,地道東北人肖油。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像臂港,于是被迫代替她去往敵國和親森枪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,969評論 2 355

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