当前位置: 首页 > 后端技术 > Java

Android中View工作流的布局过程

时间:2023-04-02 02:12:29 Java

PS:本文为转载文章,原文可读性会更好,文末有原文链接ps:文章为基于AndroidApi31来分析源码。目录1.View的布局过程1.1View的布局过程(不是ViewGroup)1.1.1原始View的布局过程1.1.2具体View的布局过程1.2ViewGroup的布局过程1.View布局过程ViewGroup使用布局过程来确定子元素的位置。当ViewGroup的位置确定后,如果这个View是ViewGroup,并且有子元素,就会遍历onLayout中的所有子元素,调用它的layout方法。在布局方法中,会再次调用onLayout方法;先说非ViewGroup的布局过程。1.1View的布局过程(不是ViewGroup)1.1.2原始View的布局过程我们先来看看View的布局方式;先看图注2,onLayout方法是判断当前View的所有子元素的位置,我们看一下View的onLayout方法;看图,View的onLayout方法是一个空实现,所以如果在xml文件中直接使用View类作为标签,是不会有子元素的。好了,我们来看一下注释1中的setFrame方法,它位于View类中;看图中注3中四个变量的赋值,left、top、right、bottom分别是当前View的四个顶点的位置。所以setFrame方法就是判断当前View在父容器布局中的位置。1,1,2具体View的布局过程我这里介绍的具体View不是ViewGroup,是继承自View的子类或者View子类的子类,比如TextView,ImageView,Button等;好了,到这里我们用TextView的布局过程来分析,TextView并没有重写布局方法,而是重写了onLayout方法;图片看TextView中的onLayout方法,它调用了super.onLayout方法,而super是View,所以super.onLayout是一个空实现;看Note4的方法,只是自动计算并设置文字大小,所以TextView的position设置在View的setFrame方法中,证明TextView不能作为其他View的父容器。1.2ViewGroup的布局流程我们知道Android系统自带的ViewGroup实现类有RelativeLayout、LinearLayout、GridLayout、TableLayout、FrameLayout和Constraint-Layout。由于ViewGroup子类的布局特点不同,ViewGroup子类FrameLayout的位置设置也不同;这里我们使用FrameLayout的布局过程进行分析。同样,FrameLayout也没有重写布局方法,其父类ViewGroup也没有重写布局方法。我们只需要看它的onLayout方法即可;图片FrameLayout的onLayout方法调用了FrameLayout的layoutChildren方法,下面看layoutChildren方法;voidlayoutChildren(intleft,inttop,intright,intbottom,booleanforceLeftGravity){//5,finalintcount=getChildCount();//6。finalintparentLeft=getPaddingLeftWithForeground();finalintparentRight=right-left-getPaddingRightWithForeground();finalintparentTop=getPaddingTopWithForeground();finalintparentBottom=bottom-top-getPaddingBottomWith0前景(i);我<计数;i++){最终视图child=getChildAt(i);如果(child.getVisibility()!=GONE){...//7、finalintlayoutDirection=getLayoutDirection();finalintabsoluteGravity=Gravity.getAbsoluteGravity(gravity,layoutDirection);finalintverticalGravity=gravity&Gravity.VERTICAL_GRAVITY_MASK;//8、switch(absoluteGravity&Gravity.HORIZONTAL_GRAVITY_MASK){caseGravity.CENTER_HORIZONTAL:childLeft=parentLeft+(parentRight-parentLeft-width)/2+lp.leftMargin-lp.rightMargin;休息;caseGravity.RIGHT:if(!forceLeftGravity){childLeft=parentRight-width-lp.rightMargin;休息;}caseGravity.LEFT:default:childLeft=parentLeft+lp.leftMargin;}//9、switch(verticalGravity){caseGravity.TOP:childTop=parentTop+lp.topMargin;休息;caseGravity.CENTER_VERTICAL:childTop=parentTop+(parentBottom-parentTop-height)/2+lp.topMargin-lp.bottomMargin;休息;caseGravity.BOTTOM:childTop=parentBottom-height-lp.bottomMargin;休息;默认值:childTop=parentTop+lp.topMargin;}//10、child.layout(childLeft,childTop,childLeft+width,childTop+height);}}}见注5,是获取子元素个数;见注6中的parentLeft、parentRight、parentTop、parentBottom,是获取当前FrameLayout的四个方向的padding;见注释7,根据View中LayoutParams的Gravity计算FrameLayoutchildView的四个顶点的位置,看是从左到右还是从右到左;见注8,根据横坐标计算子View的左侧位置;见注释9,根据纵坐标计算子View的top位置;见注释10,知道子View的left,top,width,height后,递归调用子View的layout处理并顺便保存子View的位置。在Android中View工作流的measure过程中,我们在这篇文章中有提到。一般测量出来的宽高就等于最终的宽高吧?只是宽高的测量是在View的measure过程中形成的,最终的宽高是在View的layout过程中形成的。只是两者赋值的时机不一样,最后赋宽高的时机要晚一点。因此,在正常的开发中,我们可以认为View的测量宽高等于最终的宽高;但在某些特殊情况下,测量的宽高与最终的宽高不同。下面我举个例子;图片注释11中的代码会导致View最终的宽高无论如何都会比测量的宽高大40px,从而导致View显示异常。这意味着实测的宽高确实可以和最终的宽高不一样,但是在实际开发中,我们不推荐这样做;有时候,View需要多次measure过程才能确定自己的measurewidth和height。在之前的测量过程中,测量的宽度和高度可能与最终的宽度和高度不同。最后,测量的宽度和高度将与最终的宽度和高度相同。