https://www.jianshu.com/p/2430c1dd2722

一、LayoutInflater inflate-创建View

不同版本的LayoutInflater inflate View源码可能不大一样,先列举核心的代码:

  View view = (View) inflater.invoke(null, mContext, resource);

            if (view != null && root != null) {
                // We were able to use the precompiled inflater, but now we need to do some work to
                // attach the view to the root correctly.
                XmlResourceParser parser = res.getLayout(resource);
                try {
                    AttributeSet attrs = Xml.asAttributeSet(parser);
                    advanceToRootNode(parser);
                    ViewGroup.LayoutParams params = root.generateLayoutParams(attrs);

                    if (attachToRoot) {
                        root.addView(view, params);
                    } else {
                        view.setLayoutParams(params);
                    }
                } finally {
                    parser.close();
                }
            }

return view;
  • root为null,view不会设置LayoutParams,也不会add到root里。
  • root不为null
    会获取root创建的的layoutParams

    attachToRoot为true,将view添加到root并设置layoutParams。
    attachToRoot为false,不将view添加到root,仅设置layoutParams。

二、ViewGroup-添加View

现象1:

对于

View child_view1 = LayoutInflater.from(this).inflate(R.layout.child_view1,null);
viewPlacer.addView(child_view1);

结果为:

child_view1虽然在XML里定义的根布局是match_parent,但是实际给用户的感觉是wrap_conent。

Demo源码

由于系统自带的类LinearLayoutCompat无法debug它的addView方法,这里直接自定义一个类复写addView方法:

public class MyLinearLayoutCompat extends LinearLayoutCompat {
    public MyLinearLayoutCompat(@NonNull Context context) {
        super(context);
    }

    public MyLinearLayoutCompat(@NonNull Context context, @Nullable  AttributeSet attrs) {
        super(context, attrs);
    }

    public MyLinearLayoutCompat(@NonNull   Context context, @Nullable  AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void addView(View child, int index) {
        if (child == null) {
            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
        }
        ViewGroup.LayoutParams params = child.getLayoutParams();
        if (params == null) {
            params = generateDefaultLayoutParams();
            if (params == null) {
                throw new IllegalArgumentException(
                        "generateDefaultLayoutParams() cannot return null");
            }
        }
        addView(child, index, params);
    }

}

child_view1的布局为:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff0000"
    >
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="测试"
        android:layout_centerInParent="true"
        />
</RelativeLayout>
原因

match_parent变成wrap_content的原因是LayoutInflater在创建View的时候root为null,所以创建的View的layoutParams为null,在ViewGroup addView的时候就会调用
generateDefaultLayoutParams()方法生成layoutParams来addView,而对于generateDefaultLayoutParams不同的布局的实现一般都不一样,比如线性布局:

 @Override
    protected LayoutParams generateDefaultLayoutParams() {
        if (mOrientation == HORIZONTAL) {
            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        } else if (mOrientation == VERTICAL) {
            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        }
        return null;
    }

对于横向的线性布局,你看到的效果就是wrap_wrap;对于竖向的线性布局,你看到的效果就是match_wrap。

match_parent无效的解决办法
  • 1.inflate View的时候指定root并且attachToParent。
  • 2.inflate View的时候不指定root,addView的时候添加match_parent的LayoutParams。
  • 3.inflate View的时候不指定root,addView的时候也不添加match_parent的LayoutParams,改布局即可:在布局的根布局下面再包一层match_parent。

现象2:

为什么“现象1”-“match_parent无效的解决办法”的第3种办法可以解决问题呢?

Demo源码

child_view2:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff0000"
    >
    <RelativeLayout  android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#00ff00"
        >
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="测试"
        android:layout_centerInParent="true"
        />
    </RelativeLayout>
</RelativeLayout>

感觉系统应该是对布局的最外层的根布局可能做了什么骚操作,于是将根布局由match修改为固定值:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="300px"
    android:layout_height="200px"
    android:background="#ff0000"
    >
    <RelativeLayout  android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#00ff00"
        >
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="测试"
        android:layout_centerInParent="true"
        />
    </RelativeLayout>
</RelativeLayout>

再使用child_view2我又做了一组对比操作:

1)
 LayoutInflater.from(this).inflate(R.layout.child_view2,viewPlacer);

结果是正常的固定值显示

2)
View child_view2 = LayoutInflater.from(this).inflate(R.layout.child_view2,null);
viewPlacer.addView(child_view2);

结果是异常match_match显示。

3)
View child_view2 = LayoutInflater.from(this).inflate(R.layout.child_view2,viewPlacer,false);
viewPlacer.addView(child_view2);

结果是正常的固定值显示

所以,通过LayoutInflater直接inflate并且attachToParent填充Viewinflate生成View后再addView有什么区别呢?

答案:参考我的另一篇文章http://xinyiworld.top/wordpress/?p=4422


0 条评论

发表回复

您的电子邮箱地址不会被公开。