在我们做的项目中,如果需要做某些定制性高的自定义ViewGroup,如果有不错的Viewgroup机制功底,那做起来就会有奇效。富有创造性的viewgroup会让人看得瞠目结舌,所以从一定程度上说,写viewgroup的水平很看出一个开发人员的技术如何。比如Android自带的RelativeLayout、LinearLayout、FrameLayout、Viewpager等都是继承自ViewGroup类。
ViewGroup层级图:
Viewgroup绘制流程:
会通过onMeasure、onLayout、onDraw顺序的流程方法进行绘制。
onMeasure
ViewGroup的测量需要遍历所有子view,对所有子view进行测量。
@Overrideprotected 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会水平线性摆放
作者:奔跑吧李博
链接:https://www.jianshu.com/p/70cd485fa34a