首先说下layoutParams,我们也很了解,顾名思义就是布局参数,比如在布局文件中layout_width和layout_height,那这两个参数是view本身的属性么?为啥我们编写自定义viewGoup时通常要写
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
}
其实呢,它是viewGroup的一个内部类,我们简单来分析下它:
public LayoutParams(Context c, AttributeSet attrs) {
TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
setBaseAttributes(a,
R.styleable.ViewGroup_Layout_layout_width,
R.styleable.ViewGroup_Layout_layout_height);
a.recycle();
}
/**
* Creates a new set of layout parameters with the specified width
* and height.
*
* @param width the width, either {@link #WRAP_CONTENT},
* {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
* API Level 8), or a fixed size in pixels
* @param height the height, either {@link #WRAP_CONTENT},
* {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
* API Level 8), or a fixed size in pixels
*/
public LayoutParams(int width, int height) {
this.width = width;
this.height = height;
}
/**
* Copy constructor. Clones the width and height values of the source.
*
* @param source The layout params to copy from.
*/
public LayoutParams(LayoutParams source) {
this.width = source.width;
this.height = source.height;
}
我们看下谁调用了它的第一个构造,很明显是viewGroup里的一个
generateLayoutParams()方法,熟悉view.inflate的人知道,原来在
XmlPullParser解析以后
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
boolean pendingRequestFocus = false;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
pendingRequestFocus = true;
consumeChildElements(parser);
} else if (TAG_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException(" cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException(" must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
if (pendingRequestFocus) {
parent.restoreDefaultFocus();
}
if (finishInflate) {
parent.onFinishInflate();
}
}
很显然当这个你写的这个自定义view被xml的布局文件被解析的时候
其viewGroup在addview前都会调用generateLayoutParams而这个方法的参数就是这个view在xml的属性,所以如果我们这个方法不重写,那子这个viewGroup里的子view就不支持margin了,因为他根本取不到这几个layout_marginleft,layout_marginRight,等值
那在看看addview里面
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);
当我们在代码里父view add子view时,子view没有LayoutParams,可以看到系统会给默认的generateDefaultLayoutParams(),当然了,我们写自定义view最好也要复写这个方法,万一比人是在代码里添加的子view,怎么不能设置margin了,最后,我们在addviewInner里
看到了
private void addViewInner(View child, int index, LayoutParams params,
boolean preventRequestLayout) {
if (mTransition != null) {
// Don't prevent other add transitions from completing, but cancel remove
// transitions to let them complete the process before we add to the container
mTransition.cancel(LayoutTransition.DISAPPEARING);
}
if (child.getParent() != null) {
throw new IllegalStateException("The specified child already has a parent. " +
"You must call removeView() on the child's parent first.");
}
if (mTransition != null) {
mTransition.addChild(this, child);
}
if (!checkLayoutParams(params)) {
params = generateLayoutParams(params);
}
checkLayoutParams和generateLayoutParams(params)这是啥意思呢
其实这是检查你的params是否是正确的,当然子类可以复写这两个条件,一般来说,checkLayoutParamsl里条件是p!=null而且不是你的自定义viewGroup的定义的layoutParams的子类,这样父view可以帮你生成一个符合其viewGroup的params,具体例子请看LinearLayout
/**
xml生成
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LinearLayout.LayoutParams(getContext(), attrs);
}
/**
生成默认的
*/
@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;
}
/**
addview时params搞错了,纠错
*/
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
if (sPreserveMarginParamsInLayoutParamConversion) {
if (lp instanceof LayoutParams) {
return new LayoutParams((LayoutParams) lp);
} else if (lp instanceof MarginLayoutParams) {
return new LayoutParams((MarginLayoutParams) lp);
}
}
return new LayoutParams(lp);
}
// Override to allow type-checking of LayoutParams.
//查看params类型是否正确
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LinearLayout.LayoutParams;
}
对于layoutParams大致的都分析完了,没看懂的我相信看了LinearLayout的例子也明白了吧
ps:其实generateLayoutParams方法的作用其实就是定义你的控件下所有子控件所使用的layoutParams类 通过这种形式使你的控件可以按自己想要的方式和属性来操作它的子view,这也是为何view.inflate中的根view设置padding有效,而设置margin和width,height无效的原因,并不是网上大多数人说的去掉最外面一层,本质还是有差别的