Android 布局优化--merge标签

性能优化之一就是layout的优化,

as 常识:

布局是否合理主要影响的是页面测量时间的多少,我们知道一个页面的显示测量和绘制过程都是通过递归来完成的,多叉树遍历的时间与树的高度h有关,其时间复杂度 O(h),如果层级太深,每增加一层则会增加更多的页面显示时间,所以布局的合理性就显得很重要。

那布局优化有哪些方法呢,主要通过减少层级、减少测量和绘制时间、提高复用性三个方面入手。总结如下:

  • 减少层级。合理使用 RelativeLayout 和 LinerLayout,合理使用Merge。
  • 提高显示速度。使用 ViewStub,它是一个看不见的、不占布局位置、占用资源非常小的视图对象。
  • 布局复用。可以通过includ标签来提高复用。
  • 尽可能少用wrap_content。wrap_content 会增加布局 measure 时计算成本,在已知宽高为固定值时,不用wrap_content 。
  • 删除控件中无用的属性。


这里主要说第一点的merge。

对比一下吧,使用merge之前,和使用merge之后的效果:第一张图片是没有使用merge的,第二章是使用merge的。

对应布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.spreadtrumshitaoli.layoutoptimize.MainActivity">


    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:text="Not merge"
        android:textSize="30dp"/>

    

</RelativeLayout>


对应布局:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.spreadtrumshitaoli.layoutoptimize.MainActivity">


    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:text="Not merge"
        android:textSize="30dp"/>



</merge>

可以看到少了RelativeLayout 这一层布局。


现在认识到merge可以起到优化作用,辣么,什么场景下用好呢?为什么merge可以优化布局,但是大多数layout没有使用merge呢?

  1. 如果Activity的布局文件根节点是FrameLayout,可以替换为merge标签,这样,执行setContentView之后,会减少一层FrameLayout节点。
  2. 自定义View如果继承LinearLayout,建议让自定义View的布局文件根节点设置成merge,这样能少一层结点。
  3. 知道当前父布局的布局是什么,可以使用merge并添加相应的layout属性(正规军(源码中)没有使用),推荐星为0星,不过也是一个方案。

针对3,举个例子:

刚才的例子,relativelayout中,有属性android:layout_alignParentRight 等,那么子添加到这个layout中的merge里,你也可以写这个属性。就在图1对应的layout中,我们

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.spreadtrumshitaoli.layoutoptimize.MainActivity">


    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:text="Not merge"
        android:textSize="30dp"/>

<include layout="@layout/merge_tag"></include>

</RelativeLayout>

merge_tag如下:其中的android:layout_alignParentBottom="true" 是有效的。

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">

    <TextView
        android:text="@string/app_name"
        android:layout_alignParentBottom="true"
        android:textColor="@android:color/holo_green_light"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"/>

</merge>

这种情况不推荐使用。

主要记住1、2的使用场景即可,其实merge标签使用并不太多,更多的是include和viewstub这两个。

下篇文章会介绍一下include及viewstub。


merge为什么会这样?父布局的属性,merge里可以使用,merge到底是view、viewgroup还是什么呢,它是不是个layout?

使用的时候,有没有要注意的点呢?

先来看看merge标签:

在layoutinflater中,有源码

if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } 

其中,rInflate中部分代码如下:

{
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflateChildren(parser, view, attrs, true);
                viewGroup.addView(view, params);
            }

可以看到,会把merge解析到成一个view(注意是解析,merge并不是view,只不过吧内容解析了,因为merge里面的是view),然后add到父布局中,带的param是父布局的。所以上面的3点也就得到解释了。

注意的点:

  1. merge必须放在布局文件的根节点上。
  2. merge并不是一个ViewGroup,也不是一个View,它相当于声明了一些视图,等待被添加。
  3. merge标签被添加到A容器下,那么merge下的所有视图将被添加到A容器下。
  4. 因为merge标签并不是View,所以在通过LayoutInflate.inflate方法渲染的时候, 第二个参数必须指定一个父容器,且第三个参数必须为true,也就是必须为merge下的视图指定一个父亲节点。(其中第三个参数可以省略:源码:
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    
        return inflate(resource, root, root != null);
    }

android 源码例子:

  • only in /sprdroid8.1_trunk/frameworks/base/tests/SharedLibrary/lib/res/layout/
1<?xml version="1.0" encoding="utf-8"?>
2
3<merge xmlns:android="http://schemas.android.com/apk/res/android">
4    <TextView android:id="@+id/name"
5        android:layout_width="wrap_content"
6        android:layout_height="wrap_content"/>
7    <TextView android:id="@+id/street"
8        android:layout_width="wrap_content"
9        android:layout_height="wrap_content"/>
10    <TextView android:id="@+id/cityStateZip"
11        android:layout_width="wrap_content"
12        android:layout_height="wrap_content"/>
13    <TextView android:id="@+id/country"
14        android:layout_width="wrap_content"
15        android:layout_height="wrap_content"/>
16</merge>

sprdroid8.1_trunk/frameworks/base/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/AddressView.java

public classAddressView extends LinearLayout

View view = LayoutInflater.from(context).inflate(R.layout.address, this);



猜你喜欢

转载自blog.csdn.net/shi_xin/article/details/79817458