题记:如果遇到子项布局的样式在动态加载到父布局时发生了改变,不妨考虑一下布局加载方式和参数设置
背景
问题的发生是这样的:
我需要将RadioButton动态插入到RadioGroup中,RadioButton为自定义样式,文字居左,按钮居右,布局和添加布局代码如下,上图说话
radioGroup = findViewById(R.id.radio_group);
radioGroup.removeAllViews();
View view = View.inflate(this,R.layout.item_radio_button,null);
((RadioButton) view).setText("多拍/错拍");
radioGroup.addView(view);
理想:
现实:
在inflate的root参数设为null的基础上,试验过将CheckBox和RadioButton动态加载到LinearLayout和RadioGroup中,产生结果如下:
父(子)布局 | LinearLayout | RadioGroup |
---|---|---|
CheckBox | 布局没发生改变 | 布局发生改变 |
RadioButton | 布局没发生改变 | 布局发生改变 |
看结果来说,应该是RadioGroup的问题,但是通过addView一层一层看,发现其实是infate参数设置惹的祸,不是RadioGroup的锅,先解释一下引发的原因,然后具体对inflate加载进行说明。
背后的锅
1.进入addView方法,其params的来源为child.getLayoutParams()
情况一:inflate设为null,则params = null;
情况二:inflate有root,则params != null;
public void addView(View child, int index) {
if (child == null) {
throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
}
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = generateDefaultLayoutParams();
if (params == null) {
throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
}
}
addView(child, index, params);
}
2.params的赋值:
情况一:params == null,params = generateDefaultLayoutParams();每个ViewGroup的generateDefaultLayoutParams方法设置不同,有的为WRAP_CONTENT,有的为MATCH_CONTENT
情况二:params != null,params = root.generateLayoutParams(attrs);(参见inflate方法内),root会为创建的View对象提供LayoutParams参数
加载布局的方案
LayoutInflater之inflate
- 三个参数的inflate方法,主要是对root和attachToRoot的设置,主要分3种情况:
// 3个参数的构造函数
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot){......}
1.root=null
这种情况下,不管attachToRoot为true还是false,子项根结点的属性都是无效的
2.root!=null,attachToRoot=true
这种情况下会直接将子布局加载到父布局中,且子布局根结点设置的属性是有效的,注意此时不需要操作addView这一步,因为已经加载过了
// 返回的view.getClass为class android.widget.RadioGroup
View view = LayoutInflater.from(this).inflate(R.layout.item_radio_button,radioGroup,true);
3.root!=null,attachToRoot=false,表示不将子布局加载到父布局中,但是可以实现子布局根结点的属性不失效,但又不处于某个容器中,这种情况下需要执行addView方法
// 返回的view.getClass为class android.support.v7.widget.AppCompatRadioButton
View view = LayoutInflater.from(this).inflate(R.layout.item_radio_button,radioGroup,false);
- 两个参数的inflate方法
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
这种方式将root和attachToRoot做了关联,简化成两种情况:root=null&&attachToRoot=false和root!=null&&attachToRoot=true,情况参照三种参数的情况
View之inflate
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context);
return factory.inflate(resource, root);
}
可以看到,View的inflate方法最终也调用了LayoutInflate方法两个参数的情况