Android六大布局

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值