一般来说,自定义一个viewgroup至少需重写onmeasure()、onLayout()方法,如果对于布局有特定的需要,还需重写LayoutParam。
好了,现在来说具体的步骤:
一般而言有自定义属性,所以需得写构造方法。不是本文的重点,略过。
下面将一次详细的说明onMeasure()、onLayout()的实现。
onMeasure()具体实现
横向依次排列,首先定义一个宽度,初始值为0,每排一个view,就加入view的宽度和view的左右margin值,最后加上自身的左右padding值,就是viewgroup的宽度。
对于高度而言,子view中height加上其上下margin最大值就是其高度。然后再调用setMeasuredDimension();
在onMeasure()中最重要的是 要measure每个子view。
下面看具体代码:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//第一步--获取宽高模型
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int usedWidth = 0;
int usedHeight = 0;
int usedMaxHeight = 0;
Log.i("tag","kuan"+MeasureSpec.getSize(widthMeasureSpec)+" gao"+ MeasureSpec.getSize(heightMeasureSpec));
//第二步--测量每个子view,并计算出所有子view的总长
for(int i = 0; i < getChildCount(); i++){
View child = getChildAt(i);
if(child == null)
continue;
LayoutParams lp = (LayoutParams) child.getLayoutParams();
measureChildWithMargins(child,widthMeasureSpec,usedWidth,heightMeasureSpec,usedHeight);
// if(widthMode == MeasureSpec.EXACTLY){
// usedWidth += lp.width + lp.layout_left_space + lp.leftMargin + lp.rightMargin;
// }else{
usedWidth += child.getMeasuredWidth() + lp.layout_left_space + lp.leftMargin + lp.rightMargin;
// }
if(heightMode == MeasureSpec.EXACTLY){
usedMaxHeight = Math.max(usedMaxHeight,lp.height + lp.topMargin + lp.bottomMargin);
}else{
usedMaxHeight = Math.max(usedMaxHeight,child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
}
}
//第三步--计算出已知所用的长度,然后生成相关的宽高值
int widthSize = usedWidth + getPaddingLeft() + getPaddingRight();
int heightSize = usedMaxHeight + getPaddingTop() + getPaddingBottom();
int widthSpec = 0;
int heightSpec = 0;
// Check against our minimum width
widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
// Reconcile our calculated size with the widthMeasureSpec
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
widthSpec = resolveSize(widthSize,widthMeasureSpec);
heightSpec = resolveSize(heightSize,heightMeasureSpec);
}else{
widthSpec = resolveSizeAndState(widthSize, widthMeasureSpec, 0);
heightSpec = resolveSizeAndState(heightSize,heightMeasureSpec,0);
}
widthSize = widthSpec & MEASURED_SIZE_MASK;
heightSize = heightSpec & MEASURED_SIZE_MASK;
//第四步--设置测量值
setMeasuredDimension(widthSize,heightSize);
}
onLayout()具体实现
在这里面也要layout每个子view。在这里就可以用view的get宽高方法了,看代码:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.i("tag_layout","left" + l);
int startLeft = getPaddingLeft();
final int startTop = getPaddingTop();
for(int i = 0; i < getChildCount(); i++){
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
startLeft += lp.leftMargin + lp.layout_left_space;
int viewRight = startLeft + child.getMeasuredWidth();
int viewBottom = startTop + child.getMeasuredHeight() + lp.topMargin;
child.layout(startLeft,startTop + lp.topMargin,viewRight,viewBottom);
startLeft = viewRight + lp.rightMargin;
}
}
对于LayoutParam有特殊需求的,重写LayoutParam
我这里定义了每个子view的横向间隔,看代码:
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a = c.obtainStyledAttributes(attrs,R.styleable.Custom_Layout);
layout_left_space = a.getDimensionPixelSize(R.styleable.Custom_Layout_layout_left_space,0);
a.recycle();
}
另外,对于自定义ViewGroup,还需重写以下三个方法:
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(),attrs);
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
如果要做ondraw(),有两种方法:
第一种
在构造方法中
setWillNotDraw(false);
第二种 在构造方法中 setBackgroundResource(R.drawable.ic_launcher);
第二种 在构造方法中 setBackgroundResource(R.drawable.ic_launcher);
具体请参考
ViewGroup为什么不会调用onDraw
ok,本文完毕,觉得还可以的哥们顶下。