Chapter VII Layout

Chapter 7 Android Layout

(1) Six major layouts

(1) LinearLayout

The components in the LinearLayout container are arranged one by one. By controlling the android: orientation attribute, you can control whether the components are arranged horizontally or vertically.

(2) TableLayout table layout

TableLayout inherits from Linearout and is still essentially a linear layout manager. The table layout uses the form of rows and columns to manage UI components. It is not necessary to explicitly declare how many rows and columns are included, but to control the number of rows and columns of the table by adding TableRow and other components.

(3) FrameLayout frame layout

Frame layout, or layer layout, is arranged in a hierarchical stacking manner from the upper left corner of the screen, and the controls behind cover the controls in front. The frame layout creates a blank area (called a frame) for each component added to it, and each sub-component occupies a frame. These frames are automatically aligned according to the gravity property.
This layout is often used in the design of maps in development. Because it is a hierarchical layout, we can
use this layout when we need to implement a layered display style. For example, if we want to implement a layout similar to Baidu maps, our mobile logo is On top of a layer.

(4) RelativeLayout relative layout

Relative layout allows child controls to be laid out relative to sibling controls or parent controls. You can set child controls to align up, down, left, and right relative to sibling controls or parent controls.
RelativeLayout can replace some nested views. When we use LinearLayout to achieve a simple layout but use too much nesting, we can consider using RelativeLayout to re-layout.

(5) GridLayout table layout

GridLayout divides the entire container into rows × columns grid, each grid can place a component. SetRowCount (int) and setColumnCount (int) methods are provided to control the number of rows and columns of the grid.

(6) AbsoluteLayout (outdated)

(2) Constraint layout ConstraintLayout

(1) Features

ConstraintLayout uses constraints to specify the position and relationship of each control. It is a bit similar to RelativeLayout, but far more powerful than RelativeLayout.

(2) Advantages

a. Very suitable for using visual methods to write interface (not suitable for writing in XML)
b. Effectively solve the problem of too many nesting of layout (complex layout will always be accompanied by multiple layers of nesting, and the more nesting, The worse the performance of the program)

(3) Basic operation

a. Introduce
b. Add constraints / delete constraints c.
Inspector set all the properties of the current control, such as text content, color, click event, etc. d.
Guidelines
e. Automatically add constraints (Autoconnect, Inference)
See: Android new features introduced, ConstraintLayout completely resolved

(3) Performance comparison between RelativeLayout and LinearLayout

(1) Performance comparison

The core of the problem is: when RelativeLayout and LinearLayout are used as ViewGroup to express the same layout, whose drawing process is faster.
Through many experimental results on the Internet, we can get that the time consumption of layout and draw is almost the same when drawing the same interface. The key is that the RelativeLayout process is slower than the LinearLayout. So from the onMeasure process of RelativeLayout and LinearLayout to explore the root cause of time-consuming problems.

(2) Source code analysis

1. OnMeasure analysis of RelativeLayout

@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);
           }
       }
  }
  //...
}

According to the source code, we found that RelativeLayout will take a measure for each sub-view based on the results of the two arrangements. First of all, the arrangement of sub-views in RelativeLayout is based on each other's dependencies, and this dependency may be different from the order of the views in the Xml layout. When determining the position of each sub-view, you need to sort all the sub-views first. And because RelativeLayout allows ViewB to depend on ViewA in the horizontal direction, and ViewA depends on B in the vertical direction. Therefore, it is necessary to perform a sorting measurement horizontally and vertically.
Also note that the View.measure () method has the following optimizations:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
                widthMeasureSpec != mOldWidthMeasureSpec ||
                heightMeasureSpec != mOldHeightMeasureSpec) {
        ...
        mOldWidthMeasureSpec = widthMeasureSpec;
        mOldHeightMeasureSpec = heightMeasureSpec;
}

That is, if we or our child View does not require a forced refresh, and the value passed by the parent View to the child View has not changed (that is, the position of the child View has not changed), we will not make unnecessary measurements. When RelativeLayout is doing horizontal measurement in onMeasure, the vertical measurement result has not been completed, so I have to temporarily use myHeight to pass into the sub View system. This will cause the above optimization to fail when the height of the sub-View and the height of the RelativeLayout are set (Margin is set). When the View system is sufficiently complex, the efficiency problem will be obvious.

2. OnMeasure analysis of LinearLayout


@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)); 
          } 
        //...
}

Some comments have been marked in the source code. It should be noted that after each child measurement is completed, child.getMeasuredHeight () will be called to obtain the final height of the subview and add this height to mTotalLength. But getMeasuredHeight temporarily avoids lp.weight> 0 and the height is 0 sub-view, because the remaining height will be assigned to the corresponding sub-view by weight later. Therefore, the following conclusions can be drawn:
(1) If we do not use the weight attribute in LinearLayout, we will only carry out the measurement process once.
(2) If the weight attribute is used, LinearLayout obtains the height of all sub-views during the first measurement, and then adds the remaining height to the sub-views with weight> 0 according to weight.
This shows that the weight attribute has an impact on performance.

(3) Conclusion

1. RelativeLayout is slower than LinearLayout because it will make the child View call the measurement process twice, while LinearLayout only needs one time, but when there is a weight attribute, LinearLayout also requires two measures.
2. If the height of the child View of RelativeLayout is different from that of RelativeLayout, it will cause the RelativeLayout to make horizontal measurement in the onMeasure () method, but the vertical measurement result has not been completed, so it has to be temporarily transferred to the sub View system with its own height. And the value passed by the parent View to the child View does not change, so the unnecessary measurement optimization will fail, and the solution is to use padding instead of margin to optimize this problem.
3. Use Linearlayout instead of RelativeLayout without responding to the depth of the hierarchy.
4. A new Android project SDK will automatically generate the avtivity_main.xml layout file for us, and then its root node is RelativeLayout by default?
DecorView's hierarchy depth is known and fixed. The use of RelativeLayout does not reduce the hierarchy depth in the upper title bar and the lower content bar. Therefore, using LinearLayout in this case is more efficient.
5. As the top-level View, DecorView is a vertical LinearLayout, with the title bar above and the content bar below. The commonly used setContentView () method is to set the layout for the content bar?
The default new RelativeLayout for developers is to hope that developers can use as few View levels as possible. Many effects require the nesting of multiple LinearLayouts, which is certainly not as good as the performance of one-layer RelativeLayout. Therefore, we should try to reduce the nesting of the layout and reduce the hierarchical structure, using techniques such as viewStub and include. Larger layout optimization can be performed.

(4) Layout optimization plan

(1) How does the Android system handle the update operation of UI components

1. Android needs to convert XML layout files into objects that the GPU can recognize and draw. This operation is done with the help of DisplayList. DisplayList holds all the data information that will be handed over to the GPU to be drawn on the screen.
2. The CPU is responsible for calculating the UI components into Polygons and Texture textures, and then handing them to the GPU for rasterization rendering.
3. GPU rasterization rendering. (Rasterization: Split the components into different pixels for display)
4. The hardware is displayed on the screen.
It should be noted that whenever the drawing content in the View changes, a series of operations such as creating a DisplayList, rendering the DisplayList, and updating to the screen will be performed again. The performance of this process depends on the complexity of the View, the state change of the View and the execution performance of the rendering pipeline
Overdraw (overdraw): describes that a pixel on the screen is drawn multiple times within the same frame time. In the multi-level UI structure, if the invisible UI is also doing drawing operations, it will cause some pixel areas to be drawn multiple times, wasting a lot of CPU and GPU resources. (You can open the Show GPU Overdraw option through the developer options and observe the Overdraw situation on the UI)
So we need to minimize Overdraw as much as possible.

(2) Android layout optimization ideas

Reduce the level, the simpler the better, and reduce the overdraw, the better the performance

(3) Common methods for Android layout optimization

1. Make good use of RelativeLayout

When RelativeLayout and LinearLayout can meet the requirements at the same time, try to use RelativeLayout. This can be seen from the default layout of our MainActivity. The default is RelativeLayout, because the level of the layout tree generated by LinearLayout nesting can be reduced by flat RelativeLayout.
Android provides several convenient layout managers. Most of the time, you only need some of the basic features of these layouts to implement the UI. In general, when using LinearLayout, there will always be one more View level than RelativeLayout. Each time a View is added to the application, or a layout manager is added, it will increase the consumption of the system at runtime, so this will cause the process of interface initialization, layout, and drawing to slow down.
The same layout LinearLayout and RelativeLayout use Hierarchy View to view the hierarchical tree:
LinearLayout
Insert picture description here
RelativeLayout
Insert picture description here
is very obvious. It can be seen that RelativeLayout is one level less than LinearLayout, of course, the rendering time is also greatly reduced

2. Use abstract layout tags include, merge, ViewStub

3.1) include tag

The include tag is often used to extract the public part of the layout. For example, if we need the data of the LinearLayout in activity_main.xml, then we can directly include it.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.jared.layoutoptimise.MainActivity">
 
    <include layout="@layout/item_test_linear_layout" />
     
</RelativeLayout>

Insert picture description here

3.2) merge tag

The merge tag is used as an auxiliary extension of the include tag. Its main function is to prevent redundant layout nesting when referencing the layout file. Android rendering requires time. The more complex the layout, the worse the performance. As the above include tag introduces the previous LinearLayout, it leads to an additional level of interface. If you use merge at this time, you can reduce one level.

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:src="@mipmap/ic_launcher" />
 
    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="16dp"
        android:layout_toRightOf="@+id/iv_image"
        android:text="这个是MergeLayout"
        android:textSize="16sp" />
 
    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv_title"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"
        android:layout_toRightOf="@+id/iv_image"
        android:text="这个是MergeLayout,这个是MergeLayout"
        android:textSize="12sp" />
 
</merge>

activity_main can be included directly

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.jared.layoutoptimise.MainActivity">
 
    <include layout="@layout/item_merge_layout" />
 
</RelativeLayout>

Insert picture description here

3.3) ViewStub tag

Viewstub is a subclass of view. He is a lightweight View, a hidden, no-size View. It can be used to simply fill in the layout file while the program is running.

(4) Android's latest layout method, ConstantLayout

ConstraintLayout allows you to create large and complex layouts without any nesting. It is very similar to RelativeLayout. All views depend on the relative relationship between the sibling control and the parent control. However, ConstraintLayout is more flexible than RelativeLayout. It is also very convenient to use in AndroidStudio at present.

(5) Method to detect the layout depth

(1) Dump UI Hierarchy for UI Atomator, analysis UI

Start Android Device Monitor from Android Studio: Tools-> Android-> Android Device Monitor. The method of use is very simple, as shown below
Insert picture description here

(2) HierachyViewer

Click the menu Tools> Android> Android Device Monitor in turn, as shown below:
Insert picture description here
or directly click the icon next to the question mark next to the menu, as shown below: After
Insert picture description here
starting Android Device Monitor successfully, click the switch view icon in the new window, select Hierarchy Viewe, As shown below:
Insert picture description here

Published 74 original articles · won 15 · views 6255

Guess you like

Origin blog.csdn.net/qq_29966203/article/details/90473634