Android 9.0系统源码

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

前言

状态栏与导航栏属于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。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: android