上篇分析了DecorView创建过程,大致说了其子View构成,还剩下一些疑点,本篇继续分析。
1、DecorView各个子View具体布局内容2、状态栏(背景)和导航栏(背景)如何添加到DecorView里3、DecorView子View位置与大小的确定4、常见的获取DecorView各个区块大小的方法
照旧,打开Tools->LayoutInspector
先来看看LinearLayout,之前分析过加载DecorView时,根据不同的feature确定不同的布局,我们的demo加载的是默认布局:R.layout.screen_simple。这是系统自带的布局文件,在哪找呢?
切换到Project模式——>找到ExternalLibraries——>对应的编译API——>reslibraryroot——>layout文件夹下——>寻找对应的布局名
R.layout.screen_simple布局内容
再来看看实际的layout展示:
正好和LinearLayout对应,ViewStub也对得上,但是明明布局文件里的FrameLayout是没有子View的,实际怎么会有呢?当然是中途动态添加进去的。
之前分析过,DecorView创建成功后,又继续加载了一个布局:R.layout.abc_screen_toolbar,并赋予subDecor变量,最后将subDecor里的某个子View添加到DecorView里。那么该布局文件在哪找呢?按照上面的方法,你会发现layout里并没有对应的布局文件。
实际上加载R.layout.abc_screen_toolbar是由AppCompatDelegateImpl.java完成的,而该类属于androidx.appcompat.app包,因此该寻找androidx里资源文件
ActionBarOverlayLayout还有个子View
此时DecorView和subDecor已经结合了,并且android.R.id.content也存在,我们在setContentView(xx)里设置的layout会被添加到android.R.id.content里。
前面只是分析了LinearLayout及其子View的构造,而DecorView还有另外两个子View:状态栏(背景)/导航栏(背景)没有提及,接下来看看它们是如何关联上的。既然是DecorView的子View,那么必然有个addView()的过程,搜索后确定如下方法:
DecorView.javaprivatevoidupdateColorViewInt(finalColorViewStatestate,intsysUiVis,intcolor,intdividerColor,intsize,booleanverticalBar,booleanseascape,intsideMargin,booleananimate,booleanforce){Viewview=state.view;//确定View的宽高intresolvedHeight=verticalBarLayoutParams.MATCH_PARENT:size;intresolvedWidth=verticalBarsize:LayoutParams.MATCH_PARENT;//确定View的GravityintresolvedGravity=verticalBar(seascapestate.attributes.seascapeGravity:state.attributes.horizontalGravity):state.attributes.verticalGravity;if(view==null){if(showView){//构造Viewstate.view=view=newView(mContext);//设置View背景色setColor(view,color,dividerColor,verticalBar,seascape);//设置idview.setId(state.attributes.id);LayoutParamslp=newLayoutParams(resolvedWidth,resolvedHeight,resolvedGravity);//添加到DecorViewaddView(view,lp);}}else{//省略...}//省略}该方法根据条件添加子View到DecorView,调用该方法的地方有两处:
再用图表示状态栏、导航栏添加流程:
状态栏/导航栏如何确定位置呢?
publicstaticfinalColorViewAttributesSTATUS_BAR_COLOR_VIEW_ATTRIBUTES=newColorViewAttributes(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);publicstaticfinalColorViewAttributesNAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES=newColorViewAttributes(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*/);预先设置属性,在updateColorViewInt(xx)设置View的Gravity。导航栏:Gravity.BOTTOM状态栏:Gravity.TOP这样,导航栏和状态栏在DecorView里的位置确定了。
DecorView三个直接子View添加流程已经确定,通过LayoutInspector看看其大小与位置:
从上图两个标红的矩形框分析:LinearLayout上边界是顶到屏幕,而下边界的与导航栏的顶部平齐,而状态栏是盖在LinearLayout上的,这也就是为什么我们可以设置沉浸式状态栏的原因。ContentFrameLayout包含了内容区域,ContentFrameLayout上边界与标题栏底部对齐,下边界充满父控件。来看看代码里如何确定LinearLayout和FrameLayout位置:
ContentFrameLayout父控件是ActionBarOverlayLayout,因此它的位置受父控件控制,ActionBarOverlayLayout计算标题栏占的位置,而后设置ContentFrameLayoutmarginTop属性。
针对上面的布局,对应的用图说话:
既然知道了DecorView各个子View的布局,当然就有相应的方法获取其大小。
只要能获取到DecorView对象,一切都不在话下。常见的通过Activity或者View获取:Activity: