当前位置: 首页 > news >正文

Android 9.0系统源码_SystemUI(八)PhoneWindow更新状态栏和导航栏背景颜色的流程解析

前言

状态栏与导航栏属于SystemUi的管理范畴,虽然界面的UI会受到SystemUi的影响,但是,APP并没有直接绘制SystemUI的权限与必要。APP端之所以能够更改状态栏的颜色、导航栏的颜色,其实还是操作自己的View更改UI。可以这么理解:状态栏与导航栏拥有自己独立的窗口,而且这两个窗口的优先级较高,会悬浮在所有窗口之上,可以把系统自身的状态栏与导航栏看做全透明的,之所有会有背景颜色,是因为下层显示界面在被覆盖的区域添加了颜色,之后,通过SurfaceFlinger的图层混合,好像是状态栏、导航栏自身有了背景色。看一下一个普通的Activity展示的时候,所对应的Surface(或者说Window也可以)。

在这里插入图片描述

  • 第一个XXXXActivity,大小是屏幕大小
  • 第二个状态栏StatusBar,大小对应顶部那一条
  • 第三个是底部虚拟导航栏NavigationBar,大小对应底部那一条
  • HWC_FRAMEBUFFER_TARGET:是合成的目标Layer,不参与合成

从上表可以看出,虽然只展示了一个Activity,但是同时会有StatusBar、NavigationBar、XXXXActivity可以看出Activity是在状态栏与导航栏下面的,被覆盖了,它们共同参与显示界面的合成,但是,StatusBar、NavigationBar明显不是属于APP自身UI管理的范畴。下面就来分析一下,APP层的API如何影响SystemUI的显示的,并一步步解开所谓沉浸式与全屏的原理,首先看一下如何更改状态栏颜色。

一、状态栏颜色更新原理

1、假设当前的场景是默认样式的Activity,如果想要更新状态栏颜色只需要如下代码:

getWindow().setStatusBarColor(RED);

2、其实这里调用的是PhoneWindow的setStatusBarColor函数,无论是Activity还是Dialog都是被抽象成PhoneWindow:

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

public class PhoneWindow extends Window implements MenuBuilder.Callback {

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

3、最终调用的是DecorView的updateColorViews函数,DecorView是属于Activity的PhoneWindow的内部对象,也就说,更新的对象从所谓的Window进入到了Activity自身的布局视图中,接着看DecorView,这里只关注更改颜色:

frameworks/base/core/java/com/android/internal/policy/DecorView.java

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {

   public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
           new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
                   Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
                   Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
                   com.android.internal.R.id.statusBarBackground,
                   FLAG_FULLSCREEN);
                   
   private final ColorViewState mStatusColorViewState =
           new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);

 private WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
        WindowManager.LayoutParams attrs = mWindow.getAttributes();
        int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
        final boolean isImeWindow =
                mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;
        if (!mWindow.mIsFloating || isImeWindow) {
        	...代码省略...
            boolean statusBarNeedsRightInset = navBarToRightEdge
                    && mNavigationColorViewState.present;
            boolean statusBarNeedsLeftInset = navBarToLeftEdge
                    && mNavigationColorViewState.present;
            int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
                    : statusBarNeedsLeftInset ? mLastLeftInset : 0;
            //更新状态栏颜色,mStatusColorViewState为状态栏的相关筛选条件
            updateColorViewInt(mStatusColorViewState, sysUiVisibility,
                    calculateStatusBarColor(), 0, mLastTopInset,
                    false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
                    animate && !disallowAnimate,
                    mForceWindowDrawsStatusBarBackground);
        }
        ...代码省略...
    }
  }

4、mStatusColorViewState其实就代表StatusBar的背景颜色对象,主要属性包括显示的条件以及颜色值:

    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);
public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
           new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
                   Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
                   Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
                   com.android.internal.R.id.statusBarBackground,
                   FLAG_FULLSCREEN);

如果当前对应Window的SystemUi设置了SYSTEM_UI_FLAG_FULLSCREEN后,就会隐藏状态栏,那就不在需要为状态栏设置背景,否则就设置:

  private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
                int size, boolean verticalBar, int rightMargin, boolean animate) {
                <!--关键点1 条件1-->
            state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0
                    && (getAttributes().flags & state.hideWindowFlag) == 0
                    && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
               <!--关键点2 条件2-->
            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;

            if (view == null) {
                if (show) {
                    state.view = view = new View(mContext);
                    view.setBackgroundColor(color);
                    view.setTransitionName(state.transitionName);
                    view.setId(state.id);
                    visibilityChanged = true;
                    view.setVisibility(INVISIBLE);
                    state.targetVisibility = VISIBLE;
            <!--关键点3-->
                    LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
                            resolvedGravity);
                    lp.rightMargin = rightMargin;
                    addView(view, lp);
                    updateColorViewTranslations();
                }}  
              ...}

先看下关键点1跟2 ,这里是根据SystemUI的配置决定是否显示状态栏背景颜色,如果状态栏都不显示,那就没必要显示背景色了,其次,如果状态栏显示,但背景是透明色,也没必要添加背景颜色,即不满足(color & Color.BLACK) != 0。最后看一下translucentFlag,默认情况下,状态栏背景色与translucent半透明效果互斥,半透明就统一用半透明颜色,不会再添加额外颜色。最后,再来看关键点3,其实很简单,就是往DecorView上添加一个View,原则上说DecorView也是一个FrameLayout,所以最终的实现就是在FrameLayout添加一个有背景色的View。

相关文章:

  • 网站图片翻页效果如何做/中国国际新闻
  • 做图片带字的网站/最佳的资源搜索引擎
  • 做网站怎么赚零花钱/seowhy论坛
  • 照片模板图片/网站用户体验优化
  • 做天猫网站多少钱/网站推广的100种方法
  • 0基础如何做网站/淄博百度推广
  • ElementUI中树形表格下拉卡死的问题解决
  • AppScan扫描报告
  • YOLOv8代码上线,官方宣布将发布论文,附精度速度初探和对比总结
  • 【docker概念和实践 1】 基本概念和组成原理
  • YOLOV8——快速训练指南(上手教程、自定义数据训练)
  • 虹科新闻 | 虹科与丹麦Eupry正式建立合作伙伴关系
  • Linux和Windows修改pip源
  • 安装zsh
  • 拉伯证券|年内第二大解禁周来袭
  • Unity 之 Addressable可寻址系统 -- 代码加载介绍 -- 进阶(一)
  • Openresty宏观概述笔记
  • 类模板的友元