Android布局
六大布局
LinearLayour线性布局
组件一个挨一个排列
常用属性:
orientation:组件排列方式,水平或垂直
gravity:组件包含的子元素的对齐方式
layout_gravity:组件在父容器里的对齐方式
layout_weight:布局的权重
RelativeLayout相对布局
子控件以其兄弟控件或父控件为参考按照相对位置进行布局,适用于复杂的嵌套布局
常用属性:
1.根据父容器定位:layout_alignParentLeft\Right\Top\Bottom 对齐
layout_centerHorizontal\Vertical\InParent 居中
2.根据兄弟组件定位:layout_toLeftOf\RightOf\above\below 参考组件方向
layout_alignTop\Bottom\Left\Right 对齐
3.margin:偏移,设置组件与父容器的边距
4.padding:填充,设置组件内部元素间的边距
TableLayout表格布局
继承自Linearout,本质上仍是线性布局管理器。表格布局采用行、列的形式管理UI组件
每个表格布局都是由一个或多个TableRow组成,一个TableRow代表TableLayout的一行
FrameLayout帧布局
也叫层布局,从屏幕左上角按照层次堆叠方式布局,后面的控件覆盖前面的控件。帧布局为每个加入其中的组件创建一个空白的区域(称为一帧),每个子组件占据一帧
GridLayout网格布局
把整个容器划分为rows * columns个网格,每个网格可以放置一个组件。提供了setRowCount(int)和setColumnCount(int)方法来控制网格的行和列的数量
常用属性:
orientation:子组件排列方式
layout_gravity:子组件对齐方式
rowCount:行数
columnCount:列数
AbsoluteLayout绝对布局
已过时
约束布局ConstraintLayout
类似于相对布局,但比相对布局更强大。非常适合使用可视化方式编写界面,不适合XML书写,可以有效解决布局嵌套过多的问题
相对布局和线性布局性能对比
RelativeLayout和LinearLayout绘制相同界面时,layout和draw的过程时间消耗差不多,关键在于measure过程相对布局比线性布局慢一些。所以从他们的onMeasure过程来研究耗时问题的根源
RelativeLayout的onMeasure分析
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//...
View[] views = mSortedHorizontalChildren;
int count = views.length;
for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) {
LayoutParams params = (LayoutParams) child.getLayoutParams();
applyHorizontalSizeRules(params, myWidth);
measureChildHorizontal(child, params, myWidth, myHeight);
if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
offsetHorizontalAxis = true;
}
}
}
views = mSortedVerticalChildren;
count = views.length;
for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) {
LayoutParams params = (LayoutParams) child.getLayoutParams();
applyVerticalSizeRules(params, myHeight);
measureChild(child, params, myWidth, myHeight);
if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
offsetVerticalAxis = true;
}
if (isWrapContentWidth) {
width = Math.max(width, params.mRight);
}
if (isWrapContentHeight) {
height = Math.max(height, params.mBottom);
}
if (child != ignore || verticalGravity) {
left = Math.min(left, params.mLeft - params.leftMargin);
top = Math.min(top, params.mTop - params.topMargin);
}
if (child != ignore || horizontalGravity) {
right = Math.max(right, params.mRight + params.rightMargin);
bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
}
}
}
//...
}
从源码可以看出,RelativeLayout会根据2次排列的结果对子View各做一次measure。首先RelativeLayout中子View的排列方式是基于彼此的依赖关系,而这个依赖关系可能和XML布局中View的顺序不同,在确定每个子View位置的时候,需要先给所有的子View排序一下。又因为RelativeLayout允许ViewB在横向上依赖ViewA,ViewA在纵向上依赖B,所以需要横向纵向分别进行一次排序测量。
LinearLayout的onMeasure分析
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
//LinearLayout会先做一个简单横纵方向判断,我们选择纵向这种情况继续分析
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
//...
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
//... child为空、Gone以及分界线的情况略去
//累计权重
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
totalWeight += lp.weight;
//计算
if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
//精确模式的情况下,子控件layout_height=0dp且weight大于0无法计算子控件的高度
//但是可以先把margin值合入到总值中,后面根据剩余空间及权值再重新计算对应的高度
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
} else {
if (lp.height == 0 && lp.weight > 0) {
//如果这个条件成立,就代表 heightMode不是精确测量以及wrap_conent模式
//也就是说布局是越小越好,你还想利用权值多分剩余空间是不可能的,只设为wrap_content模式
lp.height = LayoutParams.WRAP_CONTENT;
}
// 子控件测量
measureChildBeforeLayout(child, i, widthMeasureSpec,0, heightMeasureSpec,totalWeight== 0 ? mTotalLength :0);
//获取该子视图最终的高度,并将这个高度添加到mTotalLength中
final int childHeight = child.getMeasuredHeight();
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
}
//...
}
##
如果不使用weight属性,将只进行一次measure的过程
如果使用了weight属性,在第一次测量时获取所有子View的高度,之后再将剩余高度根据weight加到weight>0的子View上
结论
1.RelativeLayout慢于LinearLayout是因为它会让子View调用两次measure过程,而LinearLayout只需一次,但是weight属性存在时,LinearLayout也会调用两次measure
2.RelativeLayout的子View如果高度和RelativeLayout不同,会导致RelativeLayout在onMeasure()方法中做横向测量时,纵向的测量结果尚未完成,只好暂时使用自己的高度传入子View系统,导致父View给子View传入的值没有变化就不会做无谓的测量的优化失效
3.在不影响层级深度的情况下,使用LinearLayout而不是RelativeLayout
布局优化方案
Android系统是如何处理UI组件的更新操作的
1.Android需要把XML布局文件转换成GPU能够识别并绘制的对象。这个操作是在DisplayList的帮助下完成的,它持有所有将要交给GPU绘制到屏幕上的数据信息
2.CPU负责把UI组件计算成Polygons,Texture纹理,然后交给GPU进行栅格化渲染
3.GPU进行栅格化渲染(栅格化:把组件拆分到不同的像素上进行显示)
4.硬件展示在屏幕上
任何时候View中的绘制内容发生变化时,都会重新执行创建DisplayList,渲染DisplayList,更新到屏幕上等一系列操作。
Overdraw(过度绘制):描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次的UI结构里面,如果不可见的UI也在做绘制的操作,就会导致某些像素区域被绘制了多次,浪费大量的CPU以及GPU资源
布局优化思想
减少层级,越简单越好,减少overdraw
布局优化方法
1.在RelativeLayout和LinearLayout同时能够满足需求时,尽量使用RelativeLayout,因为可以通过扁平的RelativeLayout降低LinearLayout嵌套所产生布局树的层级。
2.使用抽象布局标签include\merge\ViewStub
3.使用约束布局可以在不使用任何嵌套的情况下创建大型复杂的布局
布局文件映射的两种方式
1.setContentView()方法,调用setContentView(R.layout.main),将XML布局文件直接显示UI
2.inflate()方法,View view = inflate.inflate(R.layout.main,null),将XML布局文件转换为一个View对象
原文链接:https://blog.csdn.net/qq_29966203/article/details/101380552