我有篇文章你的自定义View是否真的支持Margin
讲到 子View的margin属性的支持须要在 自定义ViewGroup 经过generateLayoutParams设置,而子View的padding支持则是本身在onDraw中处理。node
generateLayoutParams大体以下:android
public class MyViewGroup extends ViewGroup { @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { // MyLayoutParams 继承自MarginLayoutParams return new MyLayoutParams(getContext(), attrs); } }
其实MarginLayoutParams 不光设置子View的margin属性,还设置了子View的layout_width和layout_height属性。见下面的代码:dom
public abstract class ViewGroup extends View { public static class MarginLayoutParams extends ViewGroup.LayoutParams { public MarginLayoutParams(Context c, AttributeSet attrs) { super(); TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout); //************************** 设置layout_width和layout_height********************* setBaseAttributes(a, R.styleable.ViewGroup_MarginLayout_layout_width, R.styleable.ViewGroup_MarginLayout_layout_height); int margin = a.getDimensionPixelSize( com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1); // ............ 略 a.recycle(); } } }
这里之因此提一下这个知识点,是为了说明子View的LayoutParams不能脱离parent存在,不然没法获取该参数。ide
咱们常常用到LayoutInflater下面的两个方法源码分析
public View inflate(int resource, ViewGroup root) public View inflate(int resource, ViewGroup root, boolean attachToRoot)
而方式一 只是间接调用了 方式二,因此只需分析和使用方式二便可。布局
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) { return inflate(resource, root, root != null); }
根据xml的ID获取xml解析器,而后又调用了另一个最重要的重载方法code
public View inflate(int resource,ViewGroup root, boolean attachToRoot) { final Resources res = getContext().getResources(); // 获取xml解析器 final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }
为了方便理解,我 删除 无关代码,并对某些部分稍有修改,其中createViewFromTag方法比较长,此方法仅仅是根据当前的xml标签经过反射生成View对象,代码并不难,可是注意该方法的一个parent参数,源码并未使用此参数,防止对本身产生误导,这里再也不贴出createViewFromTag的代码。xml
public View inflate(XmlPullParser parser, ViewGroup parent, boolean attachToRoot) { final Context inflaterContext = mContext; final AttributeSet attrs = Xml.asAttributeSet(parser); View result = parent; // Look for the parent node. int type; type = parser.next(); if (type != XmlPullParser.START_TAG) { throw new InflateException(parser.getPositionDescription() + ": No start tag found!"); } final String name = parser.getName(); // 根据当前的标签名(此处是xml的根节点)反射生成一个View对象,查看源码这个parent参数并没什么卵用,没用到。 final View temp = createViewFromTag(parent, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (parent != null) { // 若是传参parent != null时候,才建立LayoutParams //************************* 这个地方是致使咱们经常犯错误的关键 ************************* // generateLayoutParams -> { new LinearLayout.LayoutParams(attrs); } // LinearLayout.LayoutParams <init> -> { TypedArray a = context.obtainStyledAttributes; } params = parent.generateLayoutParams(attrs); if (!attachToRoot) { // 1. 若是parent != null && attachToRoot = false,就给xml的顶布局设置LayoutParams参数。 temp.setLayoutParams(params); } } // 此方法为递归调用,inflate 全部temp的直接下级children并添加到temp(ViewGroup)中,child若是有children则继续递归知道遍历完整个dom树。 rInflateChildren(parser, temp, attrs, true); // 2. 若是 parent != null && attachToRoot = true,则把temp(即整个xml视图)看成子view 添加到parent中 if (parent != null && attachToRoot) { // 添加子view(temp) ,并给temp设置LayoutParams parent.addView(temp, params); } // 此处决定 返回的是传入的parent参数仍是xml中的顶级布局 // 状况1: parent == null (attachToRoot不管true或false) 返回temp(temp无LayoutParams) // 状况2: parent != null && attachToRoot == false 返回 temp(temp有LayoutParams) // 状况3: parent != null && attachToRoot == true 返回 parent(temp有LayoutParams) if (parent == null || !attachToRoot) { result = temp; } return result; }
经过去除大量无关代码,分析起来就方便多了,注释说的很是明白了,这里再也不过多讲解。对象
实际开发Listview(RecycleView) 在Adapter 中使用inflater.inflate(R.layout.item, null);
设置layout_width,layout_height
无效的缘由就很是简单明了了。继承
而下面两种方式
inflater.inflate(R.layout.item, parent ,false); inflater.inflate(R.layout.item, parent ,true);
都会使设置xml的顶级Dom的layout_width和layout_height,惟一区别就是
这里temp指的是xml的根节点
1. parent == null (attachToRoot不管true或false) 返回temp(temp无LayoutParams) 2. parent != null && attachToRoot == false 返回 temp(temp有LayoutParams) 3. parent != null && attachToRoot == true 返回 parent(temp有LayoutParams)