上次我们说到视角的测量流程,今天接着说说布局。
关于布局,很多朋友知道它是负责布局的,那么具体是怎么布局的?视图组和视角的布局方法又有什么不同?一起来看看吧,需要的朋友可以参考下
目录
一、视图布局方法二、视图组布局方法三、自定义垂直布局垂直布局四、效果展示
一、View layout方法
首先,还是从ViewRootImpl说起,界面的绘制会触发执行测量、执行布局方法,而在执行布局方法中就会调用视图的布局方法开始一层层视角的布局工作。
私有void执行布局(窗口管理器.LayoutParams lp,int desiredWindowWidth
int desiredWindowHeight) {
最终视图主机=视图
host.layout(0,0,host.getMeasuredWidth(),host。getmeasuredheight());
}
视图我们都知道了,就是顶层视图——装饰视图,那么就进去看看装饰视图的布局方法:
不好意思,装饰视图中并没有布局方法.
所以,我们直接看看视角的布局方法:
公共空布局(int l,int t,int r,int b) {
boolean changed=isLayoutModeOptical(m parent)?
setOpticalFrame(l,t,r,b) : setFrame(l,t,r,b);
if(changed | |(mPrivateFlags PFLAG布局要求)==PFLAG布局要求){
onLayout(已更改,l,t,r,b);
}
}
protected void onLayout(boolean changed,int left,int top,int right,int bottom) {
}
首先,方法传入了四个参数,分别代表视角的左、上、下、右四个值。
然后通过设置光学框架方法或者setFrame方法判断布局参数是否改变。
具体判断过程就是通过老的上下左右值和新的上下左右值进行比较,逻辑就在setFrame方法中:
受保护的布尔setFrame(int left,int top,int right,int bottom) {
布尔变化=假;
如果(mLeft!=left || mRight!=对|| mTop!=top || mBottom!=底部){
已更改=真
//记住我们抽取的位
int DRAWN=mPrivateFlags PFLAG _ DRAWN;
int old width=m右-m左;
int old height=mBottom-mTop;
int new width=右-左;
int new height=bottom-top;
布尔型sizeChanged=(newWidth!=oldWidth) || (newHeight!=老身高);
//使我们的旧立场无效
无效(大小改变);
mLeft=左;
mTop=top
mRight=右
mBottom=底部
mrendernode。setlefttoprightbottom(m left,mTop,mRight,mBottom);
}
退货已更改;
}
如果上下左右有一个参数值发生了改变,就说明这个视角的布局发生了改变,然后重新计算视角的宽度高度(新宽度、新高度),并赋值了视角新的上下左右参数值。
在这个布局方法中主要涉及到了四个参数:mLeft、mTop、mBottom、mRight,分别代表了视角的左坐标、上坐标、下坐标和右坐标,你可以把视角理解为一个矩形,确定了这四个值,就能确定视角矩形的四个顶点值,也就能确定视角在画布中的具体位置。
所以,布局方法到底干了啥?
就是传入上下左右值、然后赋值上下左右值、完毕。
然后我们就可以根据这些值获取视角的一系列参数,比如视角宽度:
public final int getWidth() {
返回m右-m左;
}
至此,查看的布局方法就结束了,主要就是通过对上下左右参数的赋值完成对视角的布局,非常简单。
下面看看视图组。
二、ViewGroup layout方法
@覆盖
公共最终空布局(int l,int t,int r,int b) {
如果(!mSuppressLayout(m transition==null | |!m过渡。ischanginglayout())){
if (mTransition!=null) {
m过渡。版面变化(这个);
}
super.layout(l,t,r,b);
}否则{
mlayoutcalledwhilespected=true;
}
}
呃,或者称之为视图的布局方法。是不是ViewGroup和View的布局过程是一样的,就是决定了自己的位置?
ViewGroup的子视图呢?不是,我们刚才讲Layout方法的时候漏掉了一个onLayout方法,但是这个方法在View中是一个空实现,在ViewGroup中变成了一个抽象方法:
@覆盖
受保护的抽象void onLayout(布尔值已更改,
int l,int t,int r,int b);
也就是说,任何视图组都必须实现这个方法来完成子视图的布局。
具体的布局逻辑是在onLayout方法中逐个调用子视图的布局方法,然后完成每个子视图的布局,最后完成绘图工作。
接下来,我们自己实现一个垂直的LinearLayout(类似于线性布局),正好可以在下一节回顾onMearsure,在本节回顾onLayout。
三、自定义垂直布局VerticalLayout
首先,我们需要确定我们自定义的ViewGroup在垂直方向上的功能类似于LinearLayout函数,并且这个ViewGroup下的子视图可以按垂直线性顺序排出。就叫VerticalLayout吧~ ~
继承ViewGroup
首先,我们必须从ViewGroup继承这个布局,并实现相应的构造方法:
公共类VerticalLayout : ViewGroup {
构造函数(context: Context,attrs: AttributeSet?defStyleAttr: Int=0) : super(
语境,
attrs,
defStyleAttr
)
构造函数(context: Context,attrs: AttributeSet?):super(context,attrs) {
}
}
重写generateLayoutParams方法
需要重写一个方法来定制视图组,这就是generateLayoutParams。这一步就是让我们的ViewGroup支持Margin,然后通过MarginLayoutParams就可以得到子视图的Margin值。
覆盖fun generateLayoutParams(attrs:AttributeSet?):LayoutParams?{
返回MarginLayoutParams(上下文,属性)
}
重写测量方法onMeasure
然后,我们需要测量我们的布局,也就是重写onMeasure方法。
在这个方法中,我们需要测量我们的布局,并将测量的宽度和高度传递给setMeasuredDimension方法来完成测量。
受保护的最终void setMeasuredDimension(int measured width,int measuredHeight)
如前所述,onmeasuremethod将传入两个参数,widthMeasureSpec和heightMeasureSpec。
根据当前视图的LayoutParams和父视图的测量规范,它包含父视图预期的测量模式和测量大小:
当测量模式为MeasureSpec时。精确地
也就是说,当宽度或高度是某个值时,当前布局视图的宽度和高度被设置为由父视图设置的测量尺寸。比如宽度是400dp,那么我们可以直接调用setMeasuredDimension来传入这个固定值,不需要重新测量。
当测量模式为MeasureSpec时。最多_或未指定:
此时意味着父视图对当前视图的要求是不固定的,可以是任意大小,也可以不超过最大值。例如,将这个VerticalLayout的高度设置为wrap_content。然后我们又要测量高度,因为只有我们设计师才知道如何计算适应高度。也就是VerticalLayout是垂直的线性布局,所以高度自然是所有子视图的高度之和。
至此,onMeasure方法的逻辑基本清晰:
override fun on measure(widthMeasureSpec:Int,heightsmeasurespec:Int){
super.onMeasure(宽度测量规格,高度测量规格)
//获取宽度和高度的测量模式和测量尺寸
val width mode=measure spec . get mode(widthMeasureSpec)
val height mode=measure spec . get mode(heightmeautespec)
val size width=measure spec . getsize(widthMeasureSpec)
val size height=measure spec . getsize(heightsmeasurespec)
var mHeight=0
var mWidth=0
//遍历子视图以获取总高度。
for (i in 0,直到childCount) {
val childView=getChildAt(i)
//测量子视图的宽度和高度
测量孩子(子视图,宽度测量规格,高度测量规格)
val LP=子视图。布局参数作为MarginLayoutParams
val子宽度=子视图。测量宽度LP。左边距LP。右边距
val子高度=子视图。测量高度LP。上边距LP。底部边距
//计算得出最大宽度
mWidth=Math.max(mWidth,childWidth)
//累计计算高度
mHeight=childHeight
}
//设置宽高
setMeasuredDimension(
if (widthMode==MeasureSpec .确切地说)sizeWidth else mWidth,
if (heightMode==MeasureSpec .准确地说)尺寸高度否则高度
)
}
主要的逻辑就是遍历子查看,得出垂直布局的实际宽高:
最终视图组的高=所有子视角的(高边缘值)
最终视图组的宽=最大子视角的(宽边缘值)
最后调用setMeasuredDimension根据测量模式传入宽高。
重写布局方法onLayout
上文说过,作为一个视图组,必须重写onLayout方法,来保证子视角的正常布局摆放。
垂直线性布局垂直布局亦是如此,那么在这个布局中onLayout方法的关键逻辑又是什么呢?
还是那句话,确定位置,也就是确定左、上、右、下四个参数值,而在垂直布局中,最关键的参数就是这个上,也就是顶端值。
每个视角的顶端值必须是上一个视角的底部值,也就是接着上一个视角进行摆放,这样才会是垂直线性的效果,所以我们需要做的就是动态计算每个视角的顶端值,其实也就是不断累加视角的高度,作为下一个视角的顶端值。
覆盖fun onLayout(已更改:Boolean,l: Int,t: Int,r: Int,b: Int) {
var childWidth=0
var childHeight=0
var childTop=0
var lp: MarginLayoutParams
//遍历子查看,布局每个子视角
for(我在0、直到childCount) {
val childView=getChildAt(i)
子高度=子视图。测量高度
子宽度=子视图。测量宽度
LP=子视图。布局参数作为MarginLayoutParams
//累计计算顶端值
childTop=lp.topMargin
//布局子视角
childView.layout(
LP。左边的边距,
童顶,
lp .左边距子宽度,
儿童身高
);
儿童身高=儿童身高LP。底部边距
}
}
逻辑还是挺简单的,
左边的是固定的子视角的左边距。
顶端是累加计算的子视角的高度边缘值。
正确是左边的子视角的宽度。
底部是顶端子视角的高度。
最后调用子视角的布局方法,对每个子视角进行布局。
大功告成,最后看看我们这个自定义垂直线性布局的效果吧~
四、效果展示
com。熊猫。学习笔记3。垂直布局
Android:layout _ width=' wrap _ content '
Android:layout _ height=' wrap _ content '
文本视图
android:layout_width='100dp '
android:layout_height='100dp '
android:text='啦啦啦'
android:textSize='20sp '
Android:text color=' @ color/white '
Android:background=' @ color/design _ default _ color _ primary '
/
文本视图
android:layout_width='300dp '
android:layout_height='200dp '
android:layout_marginTop='20dp '
Android:background=' @ color/card view _ dark _ background '
android:textSize='20sp '
Android:text color=' @ color/white '
android:text='你好啊'
/
文本视图
android:layout_width='140dp '
android:layout_height='100dp '
android:text='嘻嘻'
Android:layout _ margin left=' 10dp '
android:layout_marginTop='10dp '
android:textSize='20sp '
安卓:重力='中心'
Android:text color=' @ color/black '
Android:background=' @ color/teal _ 200 '
/
/com . panda . study note 3 . vertical layout
关于Java基础布局的详细知识这篇文章就到这里了。有关Java布局的更多信息,请搜索我们以前的文章或继续浏览下面的相关文章。希望你以后能支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。