登录 立即注册
安币:

安卓巴士 - 安卓开发 - Android开发 - 安卓 - 移动互联网门户

查看: 465|回复: 4

Android 状态栏关于开发的几件事 ,android 沉浸式状态栏

[复制链接]

359

主题

359

帖子

828

安币

手工艺人

发表于 2018-1-9 17:09:57 | 显示全部楼层 |阅读模式

        最近手头做了一个新的项目,开发中对状态栏的要求比较多,也作了一些总结,分享给大家。


         


        简答题



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


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


        标题栏与状态栏颜色一致(部分App风格)


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


        设置状态栏文字的颜色


        切换fragment时,toolBar显示与否、statusbar显示与否、statusBar颜色、statusBar文字颜色(新增)



        思考题



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


        setFitsSystemWindows()是什么鬼?



        简答题,是本篇文章阐述的内容;思考题,是针对所阐述的内容做一些拓展,反应两个层面:怎么开发?为什么能实现这样的功能?

        



        演示代码传送门  

        


        



        简答题

        





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



         


        这个效果大家脑补下,就不贴图了

        


首先在style.xml中设置为noActionBar的主题,这是必须的


[Java] 查看源文件 复制代码

        方式有三种


[Java] 查看源文件 复制代码
@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中配置
    //
    //        true
    //
}

         



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



        

        


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


[Java] 查看源文件 复制代码
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中配置



[Java] 查看源文件 复制代码
    
    @color/status_toolBar_same_color
    @color/status_toolBar_same_color
    @color/colorAccent

        


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

        


[Java] 查看源文件 复制代码
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的处理不一样



         
先上图

        


[Java] 查看源文件 复制代码

    





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


[Java] 查看源文件 复制代码
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:


[Java] 查看源文件 复制代码
 @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


[Java] 查看源文件 复制代码
@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));
}

        需求五、设置状态栏文字的颜色



        

        


[Java] 查看源文件 复制代码
 //设置白底黑字
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往上顶的状态栏)的情况",这种情况肯定是没有问题的,也不难,现在状态栏和标题栏都是我们自己,我们想让它怎么样,它不得乖乖听话,对不~


        先上图:


        

        


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


        这是只有Banner的fragment:


[Java] 查看源文件 复制代码
 @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字体颜色


[Java] 查看源文件 复制代码
@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都打印出来了


[Java] 查看源文件 复制代码
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));
        }
    }
}

        这是结果


[Java] 查看源文件 复制代码
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&amp;NavigationBar)透明之后,通过添加flag的方式
将fitsSystemWindows至为true,则还是为SystemBar预留空间,当设置为false的时候,就是不为SystemBar预留空间,比如我们设置状态栏和标题栏的时候,如果不设置fitSystemWindows为true的话,就变成了


        


这肯定不是我们想要的效果,但是为什么会这样呢?

        


我们思考一中,发现ToolBar和我们的开发者通过setContentView是在一个ActionBarOverlayLayout中,那我就去看看这个View

        


        


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

        


[Java] 查看源文件 复制代码

    

    

        

        

    


        通过includy引入的ContentView  abc_screen_content_include.xml


[Java] 查看源文件 复制代码

    


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


[Java] 查看源文件 复制代码
@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,应该是调用这个方法进行设置的,但是怎么调用的,目前我还没有找到,希望懂得同学指点迷津,万分感谢!


[Java] 查看源文件 复制代码
 /**
     * 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 &amp; 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 &amp;= ~PFLAG3_FITTING_SYSTEM_WINDOWS;
            }
        } else {
            // We're being called from the newer apply insets path.
            // Perform the standard fallback behavior.
            return fitSystemWindowsInt(insets);
        }
    }

        文章中有任何有异议的地方欢迎提出!


         

        


学不尽的技术,做不完的分享!


发表于 2018-1-10 11:47:37 | 显示全部楼层
楼主威武,以后多发干货,多办活动~!

28

主题

1万

帖子

1386

安币

Android大神

小白

Rank: 6Rank: 6

发表于 2018-1-11 04:16:58 | 显示全部楼层
支持楼主,支持安卓巴士!

7

主题

9321

帖子

1982

安币

Android大神

Rank: 6Rank: 6

发表于 2018-1-12 03:46:50 | 显示全部楼层
楼主威武,以后多发干货,多办活动~!

9

主题

9298

帖子

1796

安币

Android大神

Rank: 6Rank: 6

发表于 2018-1-13 12:01:56 | 显示全部楼层
每次我都积极回帖的,想要安币~
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

站长推荐

通过邮件订阅最新安卓weekly信息
上一条 /4 下一条

下载安卓巴士客户端

全国最大的安卓开发者社区
联系我们
关闭
合作电话:
13802416937
Email:
435399051@qq.com
商务市场合作/投稿
问题反馈及帮助
联系我们

广告投放| 下载客户端|申请友链|手机版|站点统计|安卓巴士 ( 粤ICP备15117877号 )

快速回复 返回顶部 返回列表