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创建的的layoutParamsattachToRoot为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填充View与inflate生成View后再addView有什么区别呢?
答案:参考我的另一篇文章http://xinyiworld.top/wordpress/?p=4422
0 条评论