自定义Viewgroup机制
2018-12-28 本文已影响17人
奔跑吧李博
在我们做的项目中,如果需要做某些定制性高的自定义ViewGroup,如果有不错的Viewgroup机制功底,那做起来就会有奇效。富有创造性的viewgroup会让人看得瞠目结舌,所以从一定程度上说,写viewgroup的水平很看出一个开发人员的技术如何。比如Android自带的RelativeLayout、LinearLayout、FrameLayout、Viewpager等都是继承自ViewGroup类。
ViewGroup层级图:
Viewgroup绘制流程:
会通过onMeasure、onLayout、onDraw顺序的流程方法进行绘制。
onMeasure
ViewGroup的测量需要遍历所有子view,对所有子view进行测量。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childCount = this.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = this.getChildAt(i);
this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
int cw = child.getMeasuredWidth();
int ch = child.getMeasuredHeight();
}
}
onLayout
自定义viewgroup必须要实现onLayout方法,在其中需要遍历所有子view对其调用layout方法,设置各个子view的左上右下坐标。而viewgroup的宽高则会根据所有子view所需最大的宽高来设置自己的宽高。
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
int childCount = this.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = this.getChildAt(i);
LayoutParams lParams = (LayoutParams) child.getLayoutParams();
child.layout(lParams.left, lParams.top, lParams.left + childWidth,
lParams.top + childHeight);
}
}
onDraw
viewgroup会按照子类的排列顺序,调用子类的onDraw方法分别进行绘制。
示例:写一个水平的LinearLayout的功能。
public class MyLinearLayout extends ViewGroup {
public MyLinearLayout(Context context) {
super(context);
}
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View childView;
int startLeft = l; //父view当前的左边距位置,这里只每一个子view的起始左边距位置
for (int i = 0; i < getChildCount(); i++) {
childView = getChildAt(i);
int width = childView.getMeasuredWidth(); //当前子view的宽
int height = childView.getMeasuredHeight(); //当前子view的高
childView.layout(startLeft, t, startLeft + width, height);
startLeft += width;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec); //对所有子view进行测量
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heigthMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode == MeasureSpec.AT_MOST && heigthMode == MeasureSpec.AT_MOST) {
//宽高都为wrapcontent,则设置尺寸为子view所需宽高的最大值
setMeasuredDimension(totalWidth(), totalHeight());
} else if (widthMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(totalWidth(), heightSize);
} else if (heigthMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSize, totalHeight());
}
}
/**
* 计算子view所需最大宽度,宽度为所有子view之和
* @return
*/
private int totalWidth() {
int totalWidth = 0;
for (int i = 0; i < getChildCount(); i++) {
totalWidth += getChildAt(i).getMeasuredWidth();
}
return totalWidth;
}
/**
* 计算子view所需最大高度,为子view中最大高度
* @return
*/
private int totalHeight() {
int maxHeight = 0;
for (int i = 0; i < getChildCount(); i++) {
if (getChildAt(i).getMeasuredHeight() > maxHeight) {
maxHeight = getChildAt(i).getMeasuredHeight();
}
}
return maxHeight;
}
}
布局代码:
<com.example.apple.viewgroupuse.MyLinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<View
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#00BC1B"/>
<View
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#12B7F5"/>
<View
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#FF9F00"/>
<View
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#EF1200"/>
</com.example.apple.viewgroupuse.MyLinearLayout>
运行结果:
添加子view会水平线性摆放