1. 程式人生 > >再回首沉浸式狀態列以及魅族手機等系統問題解決

再回首沉浸式狀態列以及魅族手機等系統問題解決

        上次寫過一篇解決沉浸式狀態列問題的文章,不過當時僅僅是為了解決兩個問題,沒有過多理解過沉浸式,以至於每次發開需要沉浸式都需要網上搜索半天,然後拷貝程式碼過來,修改修改就得了,今天正好靜下心來好好思考了一下沉浸式。

        頁面做成沉浸式的思路有兩個:

       第一.就是直接修改狀態列的顏色,需要什麼顏色就將狀態列的顏色改成什麼顏色,就這麼簡單,沒了。純白色沉浸式設定程式碼如下:

/**
     * 設定沉浸式狀態列
     */
    public static void immersiveNotificationBar(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//字型顏色改為黑色,非白色沉浸式狀態列不需要設定
            activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        }
        immersiveNotificationBar(activity, 255);
    }

    /**
     * 設定沉浸式狀態列
     */
    public static void immersiveNotificationBar(Activity activity, int alpha) {
        String brand = Build.BRAND;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//解決華為手機等狀態列上面有一個蒙層問題
                try {
                    Class decorViewClazz = Class.forName("com.android.internal.policy.DecorView");
                    Field field = decorViewClazz.getDeclaredField("mSemiTransparentStatusBarColor");
                    field.setAccessible(true);
                    field.setInt(window.getDecorView(), Color.TRANSPARENT);  //改為透明
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                }
            }
            if ("vivo".equalsIgnoreCase(brand) || "OPPO".equals(brand)) {//oppo和vivo手機狀態列最好不要顯示為純白色,官方未給出改變字型顏色為黑色方法
                window.setStatusBarColor(Color.argb(alpha, 208, 208, 208));
            } else {
                window.setStatusBarColor(Color.argb(alpha, 255, 255, 255));
            }
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
            int count = decorView.getChildCount();
            if (count > 0 && null != decorView.getChildAt(count - 1)) {
                decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(Color.WHITE, 30));
            } else {
                View statusView = createStatusBarView(activity, Color.WHITE, 30);
                decorView.addView(statusView);
            }
            setRootView(activity);
        }
    }

    private static View createStatusBarView(Activity activity, @ColorInt int color, int alpha) {
        View statusBarView = new View(activity);
        LinearLayout.LayoutParams params =
                new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStarusBarHeight(activity));
        statusBarView.setLayoutParams(params);
        statusBarView.setBackgroundColor(calculateStatusColor(color, alpha));
        return statusBarView;
    }

    private static void setRootView(Activity activity) {
        ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
        rootView.setFitsSystemWindows(true);
        rootView.setClipToPadding(true);
    }

    private static int calculateStatusColor(@ColorInt int color, int alpha) {
        float a = 1 - alpha / 255f;
        int red = color >> 16 & 0xff;
        int green = color >> 8 & 0xff;
        int blue = color & 0xff;
        red = (int) (red * a + 0.5);
        green = (int) (green * a + 0.5);
        blue = (int) (blue * a + 0.5);
        return 0xff << 24 | red << 16 | green << 8 | blue;
    }

    public static int getStarusBarHeight(Context context) {
        int statusBarHeight1 = -1;
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            statusBarHeight1 = context.getResources().getDimensionPixelSize(resourceId);
        }
        return statusBarHeight1;
    }

        程式碼複製過去就可以了,然後將setStatusBarColor裡的顏色值改成自己的就可了,很簡單。

       第二種方法,就是將狀態列的顏色改為透明,然後頁面設定為全屏顯示,自己頁面頂部預留一個view來當做狀態列。這個呢,靈活性高,可控性也好,相對於第一種來說適用範圍更廣,可以說就是我自己想讓狀態列顯示什麼就顯示什麼,當然了仔細想想其實這是一種假象。具體過程分為三個步驟:

     1)頁面設定為全屏且顯示狀態列,狀態列為透明

/**
     * 通過設定全屏,設定狀態列透明
     *
     * @param activity
     */
    private void fullScreen(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                //5.x開始需要把顏色設定透明,否則導航欄會呈現系統預設的淺灰色
                Window window = activity.getWindow();
                View decorView = window.getDecorView();
                //兩個 flag 要結合使用,表示讓應用的主體內容佔用系統狀態列的空間
                int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                            | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                }
                window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                decorView.setSystemUiVisibility(option);
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                window.setStatusBarColor(Color.TRANSPARENT);
                //導航欄顏色也可以正常設定
//                window.setNavigationBarColor(Color.TRANSPARENT);
            } else {
                Window window = activity.getWindow();
                WindowManager.LayoutParams attributes = window.getAttributes();
                int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
                int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
                attributes.flags |= flagTranslucentStatus;
//                attributes.flags |= flagTranslucentNavigation;
                window.setAttributes(attributes);
            }
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//解決華為手機等狀態列上面有一個蒙層問題
            try {
                Class decorViewClazz = Class.forName("com.android.internal.policy.DecorView");
                Field field = decorViewClazz.getDeclaredField("mSemiTransparentStatusBarColor");
                field.setAccessible(true);
                field.setInt(getWindow().getDecorView(), Color.TRANSPARENT);  //改為透明
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    }

        2)設定佈局中的頂部view高度為狀態列高度(或者自己想著頂部直接弄成頂天的圖片也可以)

        3)設定頂部view的顏色,隨意修改達到沉浸式,甚至可以新增圖片都無所謂

好了,沉浸式狀態列問題就這樣基本解決了,再回頭想想,真的沒什麼。不過最讓人頭疼的應該是國產系統的適配問題解決,沉浸式中最難的應該就是屬於白色沉浸式問題,狀態列顏色變成深色問題。下面我們來解決魅族手機狀態列字型顏色問題。

問題如下:

        Activity+四個Fragment,Fragment切換時切換狀態列顏色沒問題,但是在其中一個Fragment中跳轉另一個Activity,然後再回來的時候設定黑色狀態列的那個Fragment就失效了,請問是怎麼回事?每次點選切換Fragment的時候都有設定狀態列顏色。

       很不幸我也遇到了這個問題,我們分析分析為什麼?魅族手機牛逼的地方在於他會自動讀取狀態列下面view的顏色值,然後根據讀取到的顏色值來自動設定字型的顏色,尼瑪,這就完全不可控了啊!!!!我覺得應該不止魅族系統這樣,奧,對了,使用魅族官網的設定狀態列字型顏色的程式碼也照樣沒卵用。那麼我們怎麼辦?欺騙系統,我們可以欺騙系統,怎麼欺騙?就是當系統讀取狀態列後面view的時候,我們提前手動設定view的顏色,等著讀取完了我們再改成我們想要的顏色,這不就解決了!還是以需要狀態列字型為黑色為例(沉浸式狀態列設定方式為上面第二種方法):

    第一步:直接操作我們要顯示的沉浸式view或者在view上面再覆蓋一層也可以,我們就以上面再覆蓋一層view為例吧,xml中view的顏色要設定為白色。

    重寫window獲取焦點的方法,當頁面獲取到焦點時我們將覆蓋的view設定為透明顏色

@Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            if (null == mView) {//mView就是狀態列或者上面覆蓋的一層
                mView = findViewById(R.id.status_view);
                mView.post(new Runnable() {//為了解決初次進來狀態列字型顏色為白色
                    @Override
                    public void run() {
                        mView.setBackgroundColor(Color.TRANSPARENT);
                    }
                });
            } else {
                mView.setBackgroundColor(Color.TRANSPARENT);
            }
        }
    }

    第二步:就是當頁面徹底不可見的時候我們再次將view的北京顏色設定為白色,當頁面再次可見時扔會執行上面程式碼設定為透明

@Override
    protected void onStop() {
        super.onStop();
        if (null != mView) {
            mView.setBackgroundColor(Color.WHITE);
        }
    }

    這裡需要說明為什麼是在onstop中而不是onpause方法中設定,在onpause中設定會讓使用者看見白色的狀態列,所以用在onstop中設定。

    最後貼一張圖(魅藍note6)手機上的效果: