先总结两点
- 自定义View在onDraw里面需要处理padding的影响,widthMeasureSpec和heightMeasureSpec是包含padding大小的。
- 子View的margin属性是由ViewGroup处理的,ViewGroup在onMeasure和onLayout时一定要考虑 ViewGroup自己的padding和子View的margin的影响。
你可能遇到过下面这样的错误。
java.lang.ClassCastException: android.view.ViewGroup$LayoutParams cannot be cast to
android.view.ViewGroup$MarginLayoutParams
找到异常产生的位置,追踪到ViewGroup.addView()方法,
查阅了FrameLayout和LinearLayout等都没有用过这个measureChildren呢,几乎全部都重写了,我们的自定义ViewGroup的measureChildren是不是应该是改成下面这样才对。
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
// ******************* 注意这里 ********************
measureChildWithMargins(child, widthMeasureSpec, heightMeasureSpec);
}
}
LinearLayout:
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();///4
viewGroup:
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);
}
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); ///1
}
if (preventRequestLayout) {
child.mLayoutParams = params;
} else {
child.setLayoutParams(params); ////2
}
public void setLayoutParams(ViewGroup.LayoutParams params) {
if (params == null) {
throw new NullPointerException("Layout parameters cannot be null");
}
mLayoutParams = params;///3
resolveLayoutParams();
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onSetLayoutParams(this, params);
}
requestLayout();
}
这样原因就清除了
解决:
参考
public class MyViewGroup extends ViewGroup {
// ..................... 其他代码省略 .....................
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MyLayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
return new MyLayoutParams(lp);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MyLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
public static class MyLayoutParams extends MarginLayoutParams {
public MyLayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public MyLayoutParams(int width, int height) {
super(width, height);
}
public MyLayoutParams(LayoutParams lp) {
super(lp);
}
}
}