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的显示的,并一步步解开所谓沉浸式与全屏的原理,首先看一下如何更改状态栏颜色。

一、状态栏、导航栏和Activity布局的演变

1、Android 5.0之前activity默认是在statusbar下边,navigationbar上边,而在Android5.0开始,activity真正的全屏,只不过内容布局还会空出statusbar,navigationbar空间(除非设置了SYSTEM_UI_FLAG_xx),statusbar和navigationbar处加入了有颜色的view。Android在API 21的时候为Window添加了setNavigationBarColor、setStatusBarColor,进一步提升SystemBar用户体验。PhoneWindow继承Window具体实现了setNavigationBarColor、setStatusBarColor,具体代码如下:

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

public class PhoneWindow extends Window implements MenuBuilder.Callback {

    private DecorView mDecor;
    int mStatusBarColor = 0;
    int mNavigationBarColor = 0;
    int mNavigationBarDividerColor = 0;

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

不难发现主要是DecorView的updateColorViews在work,通过查看代码,可以明白是DecorView在SystemBar的位置add了对应的ColorStateView,这个有点类似PhoneWindowManager里边的WindowState,之后对ColotStateView里边的view进行操作即可,比如说setBackground来改变其颜色。

二、DecorView更新状态栏和导航栏背景颜色的流程分析

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

getWindow().setStatusBarColor(RED);

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

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、PhoneWindow的setStatusBarColor方法最终调用的是DecorView的updateColorViews函数,DecorView是属于Activity的PhoneWindow的内部对象,也就说,更新的对象从所谓的Window进入到了Activity自身的布局视图中,接着看DecorView的updateColorViews方法:

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);
	//导航栏视图属性对象
    public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES =
            new ColorViewAttributes(
                    SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
                    Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
                    Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
                    com.android.internal.R.id.navigationBarBackground,
                    0 /* hideWindowFlag */);
                   
    private final ColorViewState mStatusColorViewState =
            new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
    private final ColorViewState mNavigationColorViewState =
            new ColorViewState(NAVIGATION_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 navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
            boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
            int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
            //更新导航栏颜色,mNavigationColorViewState为导航栏的相关筛选条件
            updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
                    mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize/*导航栏高度*/,
                    navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
                    0 /* sideInset */, animate && !disallowAnimate, false /* force */);
            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);
        }
        ...代码省略...
    }
  }

DecorView的updateColorViews方法会调用updateColorViewInt方法依次对导航栏背景颜色对象mNavigationColorViewState和状态栏背景颜色对象mStatusColorViewState进行视图更新。

4、updateColorViewInt方法如下所示:

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {

    private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
            int dividerColor, int size/*状态栏或导航栏的高度*/, boolean verticalBar/*是否是竖直屏*/, boolean seascape, int sideMargin,
            boolean animate, boolean force) {
        //SystemUI视图是否存在    
        state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
        //SystemUI视图内容是否可见
        boolean show = state.attributes.isVisible(state.present, color,
                mWindow.getAttributes().flags, force);
		...代码省略...
    }
}                

updateColorViewInt方法首先会调用ColorViewAttributes的isPresent方法判断对应的SystemUI视图是否存在,并将结果赋值给state.present。

    public static class ColorViewAttributes {
        public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
                    //状态栏(SYSTEM_UI_FLAG_FULLSCREEN)、导航栏(SYSTEM_UI_FLAG_HIDE_NAVIGATION)
            return (sysUiVis & systemUiHideFlag) == 0
                    //状态栏(FLAG_TRANSLUCENT_STATUS)、导航栏(FLAG_TRANSLUCENT_NAVIGATION)
                    && (windowFlags & hideWindowFlag) == 0
                    //状态栏(STATUS_BAR_BACKGROUND_TRANSITION_NAME)、导航栏(NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME)
                    && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
                    || force);
        }
    }

然后会继续调用ColorViewAttributes的isVisible方法判断视图内容是否显示,并将结果赋值给布尔类型的show变量。

     public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
                //SystemUI视图是否存在
         return present
                 //如果背景是透明色,则没必要添加背景颜色
                 && (color & Color.BLACK) != 0
                 //背景色与translucent半透明效果互斥,半透明就统一用半透明颜色,不会再添加额外颜色。
                 && ((windowFlags & translucentFlag) == 0  || force);
     }

5、继续往下看DecorView的updateColorViewInt方法。

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {

    private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
            int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
            boolean animate, boolean force) {
	        //SystemUI视图是否存在    
	        state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
	        //SystemUI视图内容是否可见
	        boolean show = state.attributes.isVisible(state.present, color,
	                mWindow.getAttributes().flags, force);
	        //是否显示View
			boolean showView = show && !isResizing() && size > 0;
	        if (view == null) {
            if (showView) {
            	//如果状态栏、导航栏占位视图不存在则创建占位视图
                state.view = view = new View(mContext);
                //设置颜色
                setColor(view, color, dividerColor, verticalBar, seascape);
                //设置转换名称
                view.setTransitionName(state.attributes.transitionName);
                //设置id
                view.setId(state.attributes.id);
                //窗口可见性发生了改变
                visibilityChanged = true;
                view.setVisibility(INVISIBLE);
                state.targetVisibility = VISIBLE;
                LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
                        resolvedGravity);
                if (seascape) {
                    lp.leftMargin = sideMargin;
                } else {
                    lp.rightMargin = sideMargin;
                }
                //将新创建的占位视图添加到当前窗口视图内容中
                addView(view, lp);
                updateColorViewTranslations();
            }
        } else {
            int vis = showView ? VISIBLE : INVISIBLE;
            visibilityChanged = state.targetVisibility != vis;
            state.targetVisibility = vis;
            LayoutParams lp = (LayoutParams) view.getLayoutParams();
            int rightMargin = seascape ? 0 : sideMargin;
            int leftMargin = seascape ? sideMargin : 0;
            if (lp.height != resolvedHeight || lp.width != resolvedWidth
                    || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
                    || lp.leftMargin != leftMargin) {
                lp.height = resolvedHeight;
                lp.width = resolvedWidth;
                lp.gravity = resolvedGravity;
                lp.rightMargin = rightMargin;
                lp.leftMargin = leftMargin;
                view.setLayoutParams(lp);
            }
            if (showView) {
                setColor(view, color, dividerColor, verticalBar, seascape);
            }
        }
        if (visibilityChanged) {
        	//如果状态栏、导航栏视图可见性发生了变化,添加动画
            view.animate().cancel();
            if (animate && !isResizing()) {
                if (showView) {
                    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(showView ? VISIBLE : INVISIBLE);
            }
        }
        state.visible = show;
        state.color = color;	
	}
}

三、DecorView更新导航栏背景颜色

1、导航栏背景颜色对象

    public static final String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME =
            "android:navigation:background";
            

    public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES =
            new ColorViewAttributes(
                    SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
                    Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
                    Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
                    com.android.internal.R.id.navigationBarBackground,
                    0 /* hideWindowFlag */);
                           
    private final ColorViewState mNavigationColorViewState =
            new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES);
 

2、对于ColorViewAttributes的isPresent方法来说:

    public static class ColorViewAttributes {
        public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
                    //如果当前窗口的sysUiVis中包含SYSTEM_UI_FLAG_HIDE_NAVIGATION属性,则隐藏导航栏。
            return (sysUiVis & systemUiHideFlag) == 0
                    //如果当前窗口的样式windowFlags中包含FLAG_TRANSLUCENT_NAVIGATION属性,则隐藏导航栏。
                    && (windowFlags & hideWindowFlag) == 0
                    //如果当前窗口的样式windowFlags中不包含FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS属性,也就是绘制导航栏背景,则隐藏导航栏。
                    && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
                    //是否强制导航栏存在,默认不强制
                    || force);
        }
    }

3、对于ColorViewAttributes的isVisible方法来说:

     public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
                //当前窗口的SystemUI视图存在,则显示导航栏视图内容
         return present
                 //如果导航栏背景不是透明色,则显示导航栏视图内容
                 && (color & Color.BLACK) != 0
                 //如果导航栏窗口半透明,则显示导航栏视图内容
                 && ((windowFlags & translucentFlag) == 0 
                 //是否强制显示导航栏视图,默认不强制
                  || force);
     }

四、DecorView更新状态栏背景颜色

1、状态栏背景颜色对象

   public static final String STATUS_BAR_BACKGROUND_TRANSITION_NAME = "android:status:background";  

    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);

2、对于ColorViewAttributes的isPresent方法来说:

    public static class ColorViewAttributes {
        public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
                    //如果当前窗口的sysUiVis中包含SYSTEM_UI_FLAG_FULLSCREEN属性,则隐藏状态栏。
            return (sysUiVis & systemUiHideFlag) == 0
                    //如果当前窗口的样式windowFlags中包含FLAG_TRANSLUCENT_STATUS属性,则隐藏状态栏。
                    && (windowFlags & hideWindowFlag) == 0
                    //如果当前窗口的样式windowFlags中不包含STATUS_BAR_BACKGROUND_TRANSITION_NAME属性,也就是绘制状态栏背景,则隐藏状态栏。
                    && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
                    //是否强制状态栏存在,默认不强制
                    || force);
        }
    }

3、对于ColorViewAttributes的isVisible方法来说:

     public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
                //当前窗口的SystemUI视图存在,则显示状态栏视图内容
         return present
                 //如果状态栏背景不是透明色,则显示状态栏视图内容
                 && (color & Color.BLACK) != 0
                 //如果状态栏窗口半透明,则显示状态栏视图内容
                 && ((windowFlags & translucentFlag) == 0 
                 //是否强制显示状态栏视图,默认不强制
                  || force);
     }

参考文章:https://www.cnblogs.com/muouren/p/11706169.html

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值