国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Android透明化/沉浸式狀態欄實踐及源碼分析

DobbyKim / 1293人閱讀

摘要:何為透明狀態欄有人說透明狀態欄就是沉浸式狀態欄,這里不多解釋,詳見為什么在國內會有很多用戶把透明欄稱作沉浸式頂欄。下面我們就以上幾點來討論一下中透明狀態欄的實現。

何為透明狀態欄?有人說透明狀態欄就是沉浸式狀態欄,這里不多解釋,詳見為什么在國內會有很多用戶把「透明欄」(Translucent Bars)稱作 「沉浸式頂欄」?。本文將對于透明狀態欄設置方法做一個講解,以及遇到的問題該怎么解決,來面對各種需求。

本文所提到的透明狀態欄其實指的是將頂部的導航欄延伸到狀態欄,使之渾然一體(Google官方建議狀態欄顏色比導航欄的顏色略深一點),并不代表一定不設置背景色,比如導航欄是白色,則可設置狀態欄為白色,視情況而定。

相比于iOS系統,Android系統對于狀態欄的設置就顯得稍微復雜了一點。Android系統提供了API 19以上對狀態欄的設置接口,而直到API 23以上才提供對于icon顏色的設置,還有就是各家廠商(如魅族,小米等)對于狀態欄的有自己的定制,對于需要使用淺色背景狀態欄的應用,沒處理好的話往往導致淺色背景,白色icon ,狀態欄不分你我的悲劇。。

(內心os:嗯?右上角那一個綠色的電池,用戶一定知道他是狀態欄對吧。)

我隨即對比了一些主流app,發現在我的魅藍2(Android 5.1 Flyme 4.5)上竟然都不支持透明狀態欄,這對于我這種追求審美的人(其實是視覺提的需求)來說簡直不能忍。在我折騰了幾天之后,終于解決了這些問題,希望對大家思路有一些幫助。

言歸正傳,本文主要針對以下幾點進行分析:1.是否隱藏狀態欄(全屏模式) 2.狀態欄的背景色的設置 3.狀態欄icon的顏色的設置,而對于透明狀態欄設置過程中,可能造成的icon顏色設置成功,而背景顏色設置失敗,等等原因造成的淺色底,淺色字或深色底,深色字等錯誤情況的處理及兜底方案 4.源碼實現。

下面我們就以上幾點來討論一下Android中透明狀態欄的實現。

1. 全屏模式(沉浸式狀態欄)

這種情況其實用得并不多,基本上使用場景在閃屏頁展示廣告或logo,以及一些閱讀類app需要盡可能的利用到屏幕大小,展示更多的內容。

設置方法很簡單,分兩種,在API > 16時:

theme中定義:將自定義該Activtiy的theme,并在其中添加

true

代碼中定義:

window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
2. 狀態欄的背景色

我們都知道,在Android5.0(即API > 21)時,Google官方提供接口設置對應的狀態欄背景色

window.setStatusBarColor(@ColorInt int color);

那我們要想在Android5.0以下設置背景色就真的沒有辦法了嗎?并不是,我們發現在Android4.4以后,出現了windowTranslucentStatus這一特性,所以思路如下:

先設置狀態欄的透明屬性

給根布局頂部加上一個和狀態欄一樣大小的矩形View,充當假的狀態欄

設置FitsSystemWindows屬性為true,此時跟布局會延伸到狀態欄,處在頂部位置就是之前設置的view,這樣就可以以假亂真了。

       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            // 設置狀態欄透明
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            // 生成一個狀態欄大小的矩形View
            View statusView = createStatusView(activity, color);
            ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
            decorView.addView(statusView);
            // 設置根布局的參數
            ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
            rootView.setFitsSystemWindows(true);
            rootView.setClipToPadding(true);
        }

設置矩形色塊的代碼如下:

  private static View createStatusView(Activity activity, int color) {
        // 獲得狀態欄高度
        int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
        int statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId);

        // 繪制一個和狀態欄一樣高的矩形
        View statusView = new View(activity);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                statusBarHeight);
        statusView.setLayoutParams(params);
        statusView.setBackgroundColor(color);
        return statusView;
    }

效果如下:

3. 狀態欄icon的顏色

我們通過調研發現,魅族和小米的兩個廠商分別對于Flyme及MIUI做了狀態欄的定制,所以我們可以在Flyme4.0以上及MIUIV6以上都可以實現icon顏色的替換,結合上述的狀態欄背景色的替換,我們就可以對于魅族及小米兩款機型適配淺色的導航欄了。

3.1 魅族Flyme4.0+
    /**
     * 魅族Flyme4+以上透明狀態欄
     * @param window 
     * @param dark 是否把狀態欄字體及圖標顏色設置為深色
     * @return 是否設置成功
     */
    public static boolean setMeizuStatusBarDarkIcon(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;
    }
3.2 MIUIV6.0+
 /**
     * 設置狀態欄字體圖標為深色,需要MIUIV6以上
     * @param window
     * @param dark 是否把狀態欄字體及圖標顏色設置為深色
     * @return  是否設置成功
     *
     */
    public static boolean setMIUIStatusBarLightMode(Window window, boolean dark) {
        boolean result = false;
        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);//狀態欄透明且黑色字體
                }else{
                    extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字體
                }
                result=true;
            }catch (Exception e){
                //錯誤處理
            }
        }
        return result;
    }

效果如下:

4. 設置背景色失敗處理方案 4.1 解決思路

說了那么多,有童鞋可能就問了,上述說的代碼可能對大多數機型有效,但是萬一某些機型設置失敗了呢?體驗不但沒提升,反倒讓用戶不能忍受了。

好吧,下面我們來看一下應該怎么解決。思路如下:

獲取狀態欄的背景色,與需要設置的顏色進行比對,如果相等則設置成功。

如果背景色設置失敗,針對非Flyme機型,我們回退狀態欄icon顏色,還原到最初狀態。

上述為什么要強調非Flyme機型,是因為用過Flyme機型的童鞋肯定知道有一個透明狀態欄的開關,當開啟后,Flyme會對狀態欄進行處理,但處理效果并不和我們想要的一樣(比如視圖頂部為黑色背景,但是卻設置了白色狀態欄),并且兩者都對狀態欄進行操作,操作的沖突也有可能引起上述的icon顏色不明顯的情況,所以我們自己定義一個ColorDrawable進行替換。

4.2 獲取狀態欄顏色
   /**
     * 獲取狀態欄顏色
     *
     * @param window 需要獲取的activity
     * @return color   狀態欄的顏色值
     */
    public static int getColor(Window window){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return window.getStatusBarColor();
        }else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            ViewGroup decorView = (ViewGroup) window.getDecorView();
            int count = decorView.getChildCount();
            if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) {
                int color = Color.TRANSPARENT;
                Drawable background = decorView.getChildAt(count - 1).getBackground();
                if (background instanceof ColorDrawable)
                    color = ((ColorDrawable) background).getColor();
                return color;
            }
        }
        return -1;
    }

下面講一下針對如何屏蔽Flyme機型沉浸式狀態欄打開時的影響:

 private static boolean setMeizuStatusColor(Window window, @ColorInt int color, int alpha) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            final View view = window.getDecorView().findViewById(android.R.id.statusBarBackground);
            if (view != null) {
                if (!(view.getBackground() instanceof MeizuColorDrawable)) {
                        //替換成自定義ColorDrawable
                    view.setBackground(new MeizuColorDrawable());
                }

                if (view.getBackground() instanceof MeizuColorDrawable) {
                    MeizuColorDrawable colorDrawable = (MeizuColorDrawable) view.getBackground();
                    int finalColor = StatusBarUtil.calculateStatusColor(color, alpha);
                    view.setBackgroundColor(finalColor);
                    colorDrawable.setColor(finalColor);
                    return true;
                }
            }
        }
        return false;
    }
4.3 兜底方案

上面提到了一些失敗判斷途徑,那么如果還是對一些機型無效呢?我的建議是可以在本地預埋一個開關,萬一出問題,服務器可以對相應機型的沉浸式開關關掉即可,或者采用hotfix等方式來進行修復。

5. 源碼分析

看過上面針對魅族和小米設置icon的源代碼我們知道,共同點都是通過反射來進行調用相應的方法,或者對于相應的狀態欄視圖進行設置顏色。
我們來看看源碼,在Window中有一個抽象方法是setStatusBarColor而在PhoneWindow中我們找到了該方法的實現

 @Override
    public void setStatusBarColor(int color) {
        mStatusBarColor = color;
        mForcedStatusBarColor = true;
        if (mDecor != null) {
            mDecor.updateColorViews(null, false /* animate */);
        }
    }

mDecor.updateColorViews()

        private WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
        ...
                 updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mNavigationBarColor,
                        navBarSize, navBarToRightEdge, 0 /* rightInset */,
                        animate && !disallowAnimate);

                boolean statusBarNeedsRightInset = navBarToRightEdge
                        && mNavigationColorViewState.present;
                int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
                updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor,
                        mLastTopInset, false /* matchVertical */, statusBarRightInset,
                        animate && !disallowAnimate);
        ...
}

那么mNavigationColorViewState和mStatusColorViewState又是什么呢?
別著急,我們繼續往下看

       private final ColorViewState mStatusColorViewState = new ColorViewState(
                SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
                Gravity.TOP,
                Gravity.LEFT,
                STATUS_BAR_BACKGROUND_TRANSITION_NAME,
                com.android.internal.R.id.statusBarBackground,
                FLAG_FULLSCREEN);
        private final ColorViewState mNavigationColorViewState = new ColorViewState(
                SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
                Gravity.BOTTOM,
                Gravity.RIGHT,
                NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
                com.android.internal.R.id.navigationBarBackground,
                0 /* hideWindowFlag */);

mNavigationColorViewState和mStatusColorViewState這兩個都是ColorViewState定義好的ColorViewState,主要存儲了關于statusbar的一些視圖信息。包括id,translucentFlag,verticalGravity等等。

接著看updateColorViewInt()方法,這個方法是對一個View設置顏色的,自然這個View也就是指的狀態欄的View,下面來看一下實現:

private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
              int size, boolean verticalBar, int rightMargin, boolean animate) {
              state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0
                    && (getAttributes().flags & state.hideWindowFlag) == 0
                    && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
            boolean show = state.present
                    && (color & Color.BLACK) != 0
                    && (getAttributes().flags & state.translucentFlag) == 0;

            boolean visibilityChanged = false;
            View view = state.view;

            int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
            int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
            int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity;

              //對傳入的ColorViewState的view做非空判斷
            if (view == null) {
                if (show) {
                    //新建一個view
                    state.view = view = new View(mContext);
                    //設置背景色
                    view.setBackgroundColor(color);
                    view.setTransitionName(state.transitionName);
                    view.setId(state.id);
                    visibilityChanged = true;
                    //有人可能會感覺奇怪,為什么show為true的時候,這里反倒要設置INVISIBLE。從后面代碼可以知道,這里無非是想通過動畫來做一個漸顯操作,而不是立馬顯示出來。
                    view.setVisibility(INVISIBLE);
                    state.targetVisibility = VISIBLE;

                    LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
                            resolvedGravity);
                    lp.rightMargin = rightMargin;
                    addView(view, lp);
                    updateColorViewTranslations();
                }
            } else {
                int vis = show ? VISIBLE : INVISIBLE;
                visibilityChanged = state.targetVisibility != vis;
                state.targetVisibility = vis;
                if (show) {
                    LayoutParams lp = (LayoutParams) view.getLayoutParams();
                    if (lp.height != resolvedHeight || lp.width != resolvedWidth
                            || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
                        lp.height = resolvedHeight;
                        lp.width = resolvedWidth;
                        lp.gravity = resolvedGravity;
                        lp.rightMargin = rightMargin;
                        view.setLayoutParams(lp);
                    }
                    //設置背景色
                    view.setBackgroundColor(color);
                }
            }
            //改變狀態欄visibility屬性時加入動畫
            if (visibilityChanged) {
                view.animate().cancel();
                if (animate) {
                    if (show) {
                        //顯示時先設置為全透明
                        if (view.getVisibility() != VISIBLE) {
                            view.setVisibility(VISIBLE);
                            view.setAlpha(0.0f);
                        }
                        //關于透明度的動畫插值器
                        view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
                                setDuration(mBarEnterExitDuration);
                    } else {
                        view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
                                .setDuration(mBarEnterExitDuration)
                                .withEndAction(new Runnable() {
                                    @Override
                                    public void run() {
                                        state.view.setAlpha(1.0f);
                                        state.view.setVisibility(INVISIBLE);
                                    }
                                });
                    }
                } else {
                    view.setAlpha(1.0f);
                    view.setVisibility(show ? VISIBLE : INVISIBLE);
                }
            }
        }
6. 總結

話說了這么多,其實就是想讓大家知道為什么很多app放棄在較低版本的Android系統對狀態欄進行視覺修改,尤其是淺色導航欄的應用。[捂臉]

本人目前在網易搬磚,如果想要挪挪地方,請猛戳此處,選好部門及崗位以后,歡迎投簡歷給我(changan_luo@163.com),我來幫您內推。

最后歡迎拍磚,說得不好的地方歡迎交流~
喜歡的話可以加個收藏,謝謝~

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66757.html

相關文章

  • Android 沉浸解析和輪子使用

    摘要:前言我們先一起來回顧一下實現沉浸式狀態欄的一般套路。從以上版本才是真正的可以設置沉浸式體驗,但也僅僅是操作狀態欄和導航欄的顯示與隱藏。屬性解釋設置了表明會負責系統的繪制,繪制透明背景的系統狀態欄和導航欄,然后用和的顏色填充相應的區域。 前言 我們先一起來回顧一下實現沉浸式狀態欄的一般套路。在Android上,關于對StatusBar(狀態欄)的操作,一直都在不斷改善,并且表現越來越好,...

    bingo 評論0 收藏0

發表評論

0條評論

DobbyKim

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<