通过直接继承ViewGroup来实现自定义ViewGroup的方法简介
本文通过实现一个可以垂直滑动的列表型ViewGroup,介绍通过继承ViewGroup来实现自定义ViewGroup的方法。
Contents
1. 创建继承ViewGroup的自定义ViewGroup
首先创建VerticalScrollView直接继承自ViewGroup:
public class VerticalScrollView extends ViewGroup { }
2. 自定义ViewGroup的属性和构造器
类似于自定义View,也可以用同样的方法为自定义ViewGroup添加属性,并在构造器中获取这些属性。具体方法可以参考通过直接继承View来实现自定义View的方法简介,这里不在赘述。
3. 自定义ViewGroup的测量
由于VerticalScrollView可以用于盛放其他View,对VerticalScrollView的测量就需要首先通过measureChildren() 对其中盛放的子元素进行测量。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int measuredWidth = 0; int measuredHeight = 0; final int childCount = getChildCount(); measureChildren(widthMeasureSpec, heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthSpec = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightSpec = MeasureSpec.getMode(heightMeasureSpec); ...
然后开始对VerticalScrollView的测量过程。首先考虑VerticalScrollView中没有盛放任何子元素的情况,此时设置VerticalScrollView的长和宽都为0:
if (childCount == 0) { setMeasuredDimension(0, 0); }
对于VerticalScrollView的宽和高都被设置为wrap_content的情况,VerticalScrollView的宽和高由其中盛放的子元素决定,即VerticalScrollView的宽度是子元素的宽度,VerticalScrollView的高度是子元素的高度总和(这里假设VerticalScrollView中盛放的子元素尺寸都相同):
} else if (widthSpec == MeasureSpec.AT_MOST && heightSpec == MeasureSpec.AT_MOST) { final View childView = getChildAt(0); measuredWidth = childView.getMeasuredWidth(); measuredHeight = childView.getMeasuredHeight() * childCount; setMeasuredDimension(measuredWidth, measuredHeight); }
类似地,继续考虑VerticalScrollView的宽和高有一个设置为wrap_content的情况:
} else if (heightSpec == MeasureSpec.AT_MOST) { final View childView = getChildAt(0); measuredHeight = childView.getMeasuredHeight() * childCount; setMeasuredDimension(widthSize, measuredHeight); } else if (widthSpec == MeasureSpec.AT_MOST) { final View childView = getChildAt(0); measuredWidth = childView.getMeasuredWidth(); setMeasuredDimension(measuredWidth, heightSize); }
4. 自定义ViewGroup的布局
VerticalScrollView是一个垂直列表,需要把其中的子元素按垂直方向放置:
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childTop = 0; final int childCount = getChildCount(); mChildrenSize = childCount; for (int i = 0; i < childCount; i++) { final View childView = getChildAt(i); if (childView.getVisibility() != View.GONE) { final int childHeight = childView.getMeasuredHeight(); mChildHeight = childHeight; childView.layout(0, childTop, childView.getMeasuredWidth(), childTop + childHeight); childTop += childHeight; } } }
这里遍历所有子元素,累积子元素顶部的位置childTop 依次为各个子元素布局。
5. 自定义ViewGroup的使用
首先在布局文件中加入VerticalScrollView:
<com.nex3z.examples.simplecustomview.VerticalScrollView android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"> </com.nex3z.examples.simplecustomview.VerticalScrollView>
然后就可以为VerticalScrollView添加子元素:
mContainer = (VerticalScrollView) findViewById(R.id.container); LayoutInflater inflater = getLayoutInflater(); Random rnd = new Random(); for (int i = 0; i < 10; ++i) { ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.item, mContainer, false); BlockView blockView = (BlockView) layout.findViewById(R.id.block); int color = Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256)); blockView.setBlockColor(color); mContainer.addView(layout); }
6. 完整代码
完整代码可以在这里找到。
7. 参考资料
《Android开发艺术探索》 第4章 View的工作原理