Android 视图 状态栏

【Android 视图 状态栏】

[Digging] Android Translucent Status Bar
Android状态栏微技巧,带你真正理解沉浸式模式
Android 状态栏着色实践

演示代码传送门

全屏,不保留状态栏文字(Splash页面,欢迎页面)

这个效果大家脑补下,就不贴图了
首先在style.xml中设置为noActionBar的主题,这是必须的

<style name="fullScreen" parent="Theme.AppCompat.DayNight.NoActionBar">
</style>

方式有三种

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_fullscreen_no_text);
    //方式一
    //getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    //方式二
    //getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
    //方式三 style.xml中配置
    //<style name="fullScreen" parent="Theme.AppCompat.DayNight.NoActionBar">
    //        <item name="android:windowFullscreen">true</item>
    //</style>
}

全屏保留状态栏文字(页面上部有Banner图)

现在项目,大部分向下支持到19,所以先不考虑太低版本的情况

Window window = getWindow();
//默认API 最低19 
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
    window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    ViewGroup contentView = window.getDecorView().findViewById(Window.ID_ANDROID_CONTENT);
    contentView.getChildAt(0).setFitsSystemWindows(false);
}

标题栏与状态栏颜色一致 xml中配置

<style name="status_toolbar_same_color" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/status_toolBar_same_color</item>
    <item name="colorPrimaryDark">@color/status_toolBar_same_color</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

我们能看到这种处理方式,是可以解决一些业务场景,但是如果在低于21版本手机上就不管用了,那怎么办呢?请接着往下看

Window window = getWindow();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(getResources().getColor(R.color.status_toolBar_same_color));
    } else {
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        ViewGroup systemContent = findViewById(android.R.id.content);

        View statusBarView = new View(this);
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight());
        statusBarView.setBackgroundColor(getResources().getColor(R.color.status_toolBar_same_color));

        systemContent.getChildAt(0).setFitsSystemWindows(true);

        systemContent.addView(statusBarView, 0, lp);

    }

适配后的结果:

不同Fragment中对StatusBar的处理不一样

<android.support.v7.widget.Toolbar
    android:id="@+id/base_toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="@android:color/holo_blue_dark">

    <TextView
        android:id="@+id/base_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textColor="@android:color/black" />
</android.support.v7.widget.Toolbar>

<FrameLayout
    android:id="@+id/base_container"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1">

</FrameLayout>

上述代码是两个Fragment所依附的Activity对应的部分layout

private void addStatusBar() {
    //条件状态栏透明,要不然不会起作用
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    if (mStatusBarView == null) {
        mStatusBarView = new View(FragmentStatusAndActionBarActivity.this);
        int screenWidth = getResources().getDisplayMetrics().widthPixels;
        int statusBarHeight = getStatusBarHeight();
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(screenWidth, statusBarHeight);
        mStatusBarView.setLayoutParams(params);
        mStatusBarView.requestLayout();

        //获取根布局
        ViewGroup systemContent = findViewById(android.R.id.content);
        ViewGroup userContent = (ViewGroup) systemContent.getChildAt(0);
        userContent.setFitsSystemWindows(false);
        userContent.addView(mStatusBarView, 0);
    }
}

上面是对应Activity中的布局,意思就是不使用系统提供的ActionBar,使用ToolBar来代替(网上一大推代替的方法),下面的代码中设置,状态栏透明,并且设置了sitFitSystemWindow(false),通过这些操作,我们相当于把系统的StatusBar,ActionBar,都干掉了,那么接下来,我们就可以模拟创建出StatusBaruserContent.addView(mStatusBarView, 0);那么现在我们就可以自己控制statusBar和ActionBar,显示什么颜色?消失还是隐藏?

ToolBar显示的Fragment:

 @Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    mActivity.mToolbar.setVisibility(View.VISIBLE);//设置ToolBar显示
    //设置statusBar的颜色
    mActivity.mStatusBarView.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_bright));
}

ToolBar隐藏的Fragment

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    mActivity.mToolbar.setVisibility(View.GONE);//设置ToolBar消失
    //设置statusBar的颜色
    mActivity.mStatusBarView.setBackgroundColor(getResources().getColor(android.R.color.holo_orange_light));
}
复制代码

设置状态栏文字的颜色

 //设置白底黑字
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}

但是需要注意的是:目前只有android原生6.0以上支持修改状态栏字体
除此国内厂商小米、魅族也开放了修改状态栏字体的方式

小米 MIUI6
魅族 Flyme

切换fragment时,toolBar和statusbar显示与否、statusBar颜色、status文字颜色

评论区,有同学提出能否"不同Fragment中切换状态栏颜色和状态栏文字的颜色,甚至同时切换风格(纯色状态栏变成banner往上顶的状态栏)的情况",这种情况肯定是没有问题的,也不难,现在状态栏和标题栏都是我们自己,我们想让它怎么样,它不得乖乖听话,对不~

先上图:

gif

其实调整的不多,这里我只贴下关键代码,gitub代码仓库已更新,大家可以clone看完成代码

这是只有Banner的fragment:

 @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        //设置ToolBar隐藏
        mActivity.mToolbar.setVisibility(View.GONE);
        //设置statusBar的隐藏
        mActivity.mStatusBarView.setVisibility(View.GONE);
        //恢复默认statusBar文字颜色
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
            mActivity.getWindow().getDecorView().setSystemUiVisibility(View.VISIBLE);
        mActivity.mStatusBarView.setVisibility(View.GONE);
    }

改变statusBar字体颜色

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    //设置ToolBar显示
    mActivity.mToolbar.setVisibility(View.VISIBLE);
    //设置ToolBar的颜色
    mActivity.mToolbar.setBackgroundColor(getResources().getColor(R.color.colorAccent));
    //设置statusBar的颜色
    mActivity.mStatusBarView.setBackgroundColor(getResources().getColor(R.color.colorAccent));
    //设置statusBar显示
    mActivity.mStatusBarView.setVisibility(View.VISIBLE);
    //设置statusBar字体颜色
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        mActivity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}

扩展

Activity中window是怎么回事?里面有什么View/ViewGroup

写了个方法,将整个Window内的View都打印出来了

private void printChildView(ViewGroup viewGroup) {
    Log.i("printView-ViewGroup", viewGroup.getClass().getSimpleName() + "的子View和数量:" + viewGroup.getChildCount());
    for (int i = 0; i < viewGroup.getChildCount(); i++) {
        String simpleName = viewGroup.getChildAt(i).getClass().getSimpleName();
        Log.i("printView-ChildView", simpleName);
    }
    for (int i = 0; i < viewGroup.getChildCount(); i++) {
        if (viewGroup.getChildAt(i) instanceof ViewGroup) {
            printChildView((ViewGroup) viewGroup.getChildAt(i));
        }
    }
}

这是结果

printView-ViewGroup: DecorView的子View和数量:1
printView-ChildView: LinearLayout
printView-ViewGroup: LinearLayout的子View和数量:2
printView-ChildView: ViewStub
printView-ChildView: FrameLayout
printView-ViewGroup: FrameLayout的子View和数量:1
printView-ChildView: ActionBarOverlayLayout
printView-ViewGroup: ActionBarOverlayLayout的子View和数量:2
printView-ChildView: ContentFrameLayout
printView-ChildView: ActionBarContainer
printView-ViewGroup: ContentFrameLayout的子View和数量:2
printView-ChildView: View
printView-ChildView: ConstraintLayout
printView-ViewGroup: ConstraintLayout的子View和数量:1
printView-ChildView: AppCompatTextView
printView-ViewGroup: ActionBarContainer的子View和数量:2
printView-ChildView: Toolbar
printView-ChildView: ActionBarContextView
printView-ViewGroup: Toolbar的子View和数量:1
printView-ChildView: AppCompatTextView
printView-ViewGroup: ActionBarContextView的子View和数量:0
复制代码

我们根据结果画一个分布图


上述这个ContentFrameLayout就是我们Activity中通过setContentView(View)添加的,至于其中的View是我们自己设备的statusbar,把这个图画出来,希望能起一个抛砖引玉的作用,有想法的可以继续往下研究,我这里就不研究了,有想法的可以评论。

setFitsSystemWindows()是什么

fitsSystemWindows代表的是:当设置SystemBar(包含StatusBar&NavigationBar)透明之后,通过添加flag的方式 将fitsSystemWindows至为true,则还是为SystemBar预留空间,当设置为false的时候,就是不为SystemBar预留空间,比如我们设置状态栏和标题栏的时候,如果不设置fitSystemWindows为true的话,就变成了

这肯定不是我们想要的效果,但是为什么会这样呢?
我们思考一中,发现ToolBar和我们的开发者通过setContentView是在一个ActionBarOverlayLayout中,那我就去看看这个View


在系统的 frameworks\support\v7\appcompat\res\layout\abc_screen_toolbar.xml下我们看到了ActionBarOverlayLayout的布局

<android.support.v7.internal.widget.ActionBarOverlayLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/decor_content_parent"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

    <include layout="@layout/abc_screen_content_include"/>

    <android.support.v7.internal.widget.ActionBarContainer
            android:id="@+id/action_bar_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            style="?attr/actionBarStyle"
            android:touchscreenBlocksFocus="true"
            android:gravity="top">

        <android.support.v7.widget.Toolbar
                android:id="@+id/action_bar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:navigationContentDescription="@string/abc_action_bar_up_description"
                style="?attr/toolbarStyle"/>

        <android.support.v7.internal.widget.ActionBarContextView
                android:id="@+id/action_context_bar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:visibility="gone"
                android:theme="?attr/actionBarTheme"
                style="?attr/actionModeStyle"/>

    </android.support.v7.internal.widget.ActionBarContainer>

</android.support.v7.internal.widget.ActionBarOverlayLayout>
复制代码

通过includy引入的ContentView abc_screen_content_include.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.v7.internal.widget.ContentFrameLayout
            android:id="@id/action_bar_activity_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:foregroundGravity="fill_horizontal|top"
            android:foreground="?android:attr/windowContentOverlay" />

</merge>
复制代码

layout布局很普通,没有什么特别之处,我看到这时候,猜想:当我们设置了fitSystemwindow(false),是不是在这个ActionBarOverlayLayoutonLyout()过程会对相应的布局做调整。然后穷就去他的onLayout()里看:

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    final int count = getChildCount();

    final int parentLeft = getPaddingLeft();
    final int parentRight = right - left - getPaddingRight();

    final int parentTop = getPaddingTop();
    final int parentBottom = bottom - top - getPaddingBottom();

    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            final int width = child.getMeasuredWidth();
            final int height = child.getMeasuredHeight();

            int childLeft = parentLeft + lp.leftMargin;
            int childTop = parentTop + lp.topMargin;

            child.layout(childLeft, childTop, childLeft + width, childTop + height);
        }
    }
}
复制代码

然而毛都没有。。。懵逼了,layout的参数都是来自布局文件里的,后来我跟着setFitSystemWindow()看到一个方法,就是这个fitSystemWindows(Rect insets)它的注释说明里有The content insets tell you the space that the status bar,应该是调用这个方法进行设置的,但是怎么调用的,目前我还没有找到,希望懂得同学指点迷津,万分感谢!

 /**
     * Called by the view hierarchy when the content insets for a window have
     * changed, to allow it to adjust its content to fit within those windows.
     * The content insets tell you the space that the status bar, input method,
     * and other system windows infringe on the application's window.
     ...
    protected boolean fitSystemWindows(Rect insets) {
        if ((mPrivateFlags3 & PFLAG3_APPLYING_INSETS) == 0) {
            if (insets == null) {
                // Null insets by definition have already been consumed.
                // This call cannot apply insets since there are none to apply,
                // so return false.
                return false;
            }
            // If we're not in the process of dispatching the newer apply insets call,
            // that means we're not in the compatibility path. Dispatch into the newer
            // apply insets path and take things from there.
            try {
                mPrivateFlags3 |= PFLAG3_FITTING_SYSTEM_WINDOWS;
                return dispatchApplyWindowInsets(new WindowInsets(insets)).isConsumed();
            } finally {
                mPrivateFlags3 &= ~PFLAG3_FITTING_SYSTEM_WINDOWS;
            }
        } else {
            // We're being called from the newer apply insets path.
            // Perform the standard fallback behavior.
            return fitSystemWindowsInt(insets);
        }
    }
复制代码

引用:

Android 状态栏关于开发的几件事

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,566评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,200评论 0 17
  • 第二次采访。 去了一位先锋艺术家家中作客。对方穿着睡裤接受采访。上一次见面,因为谈论严肃的事,一直不拘言笑,又因为...
    Chain在这里阅读 414评论 0 1
  • 感恩清晨明媚的阳光照满我的卧室,早晨醒来的第一眼是看到灿烂的阳光,心情很好! 感恩我们小区的门卫每次进出...
    做优雅的女人阅读 92评论 0 0
  • 早起刚好赶上堵车,原本半小时的车程,40分过去了却还没有开完半程。在车上的人很多还是中老年人,但是站着的年青人都难...
    秀姨阅读 343评论 0 0