最近做项目优化,有一些小新得体会和大家分享,本文偏向那些不经常使用或者对merge不太熟悉的开发者,如果你经常使用,或者已经十分的了解merge,那就可以离开了,因为看这个也许是在浪费时间,不过如果你也想复习一下,那不妨来看一看顺便帮我指出错误。
本文纯属个人观点,如果有错误,你来打我啊。
OK,不多废话简单的回忆一下View的绘制过程。
Activity对象创建完毕后,会创建decorview对象,并将decorview对象放到window中。然后会将decorview 和 viewrootimpl关联起来, 整个绘制流程就从
viewrootimpl的preformal... 方法开始。
从最顶级的decorview到我们自己创建的contentview再到我们contentview中的view或者自定义view,这些View 无一例外的要经过一下过程
<b>onMeasure</b>
<b>onLayout</b>
<b>onDraw</b>
<b>以上几个方法,在我之前的文章里面有分析,如果很陌生,可以看一下,入个门。</b>
也就是说,每个View都会经历这三个步骤,如果这个View有子View的话,在onMeasure onlayout ondraw 这三个方法处理的方式 基本都是相同的,先是遍历子View树,然后测量、layout、draw。每一步测量都是耗时的,view树越深,耗时越多,所以,在我们在书写xml布局的时候,尽量要减少多余层级的嵌套。这时候,merge标签就很有用了。
我们先看一个简单的例子,因为可能很多人也都有写过。<b>自定义组合View</b>
public class MyView extends RelativeLayout {
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initViews();
}
private void initViews() {
LayoutInflater.from(getContext()).inflate(R.layout.view_myview, this);
}
}
然后布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="我爱稀土掘金"/>
<TextView
android:layout_below="@+id/first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我爱稀土掘金"/>
</RelativeLayout>
运行结果

也是so easy
OK,你可能会问了,这有什么东西吗?
我们来看一下View的层级

没错,多了一层,也就是MyView明明可以很好的承载两个TextView,为什么还要多一个ViewGroup承载呢?我们作为一名开发者,不能容忍任何性能的消耗,所以这种情况,我们可能需要使用merge标签
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="我爱稀土掘金"/>
<TextView
android:layout_below="@+id/first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我爱稀土掘金"/>
</merge>
OK在运行一下


成功消去那一层,胜利了。
那是不是什么情况都可以这么搞呢?其实肯定不是的,比如说
public class MyView extends LinearLayout {
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initViews();
}
private void initViews() {
LayoutInflater.from(getContext()).inflate(R.layout.view_myview, this);
}
}
我将相对布局换成线性布局了。然后看结果

不一样了哦,这是为什么?我们只能从源码入手,来解释这个问题。由于已经有大神写的非常好了,所以我就不班门弄斧了。
我推荐一篇博客给大家<a href='http://blog.csdn.net/bboyfeiyu/article/details/45869393'>源码解析</a>
关键源码
final View view = createViewFromTag(parent, name, attrs);
// 获取merge标签的parent
final ViewGroup viewGroup = (ViewGroup) parent;
// 获取布局参数
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
// 递归解析每个子元素
rInflate(parser, view, attrs, true);
// 将子元素直接添加到merge标签的parent view中
viewGroup.addView(view, params);
原因就是,其实我们使用merge标签后,Layoutinflater解析遇到merge标签,直接解析成View,然后把解析出来的属性通过generateLayoutParams方法,变成我们熟悉的params,然后再添加到merge父容器里面去。父容器,在我们这里对应着MyView,MyView也就是LinearLayout,所以才会出现上面那样的效果,虽然,我们在merge标签里面写了below,但是LinearLayout是无法识别这个属性,还是按照默认的横向走向布局2个子View,导致出现了以上的情况,所以,使用merge还是有很大的局限性的.
我们要注意。要<b>自定义组合View会增加一层层级,所以,如果可以的话,我们要加上merge标签。而且保证我们在merge标签里面写的一些属性,要可以被merge标签的父容器识别才可以。</b>
网友评论