Android 自定义view(三) 继承ViewGroup
来源:互联网 发布:集成电路设计软件 编辑:程序博客网 时间:2024/05/01 11:37
前言:
本篇博客讲解内容主要是来自 鸿洋的博客Android 自定义ViewGroup 实战篇 -> 实现FlowLayout
上篇文章 自定义view(二),继承view。今天来看自定义View的第二种情况,继承自viewgroup,虽然viewGroup也是继承view控件,但是ViewGroup和View还是有很多方法区别的,顾名思义,这是一个控件的集合控件。
1.
我们来通过自定义viewGroup来实现瀑布流的效果。效果图如下:
2.
继承viewGroup主要来重写onMearsure和onLayout,剩下的onDraw更多是用来绘图的,所以不需要来重写。所以我们来看看代码
onMeasure
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //获取测量模式和测量大小 int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); int modeWidth = MeasureSpec.getMode(widthMeasureSpec); int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); int modeHeight = MeasureSpec.getMode(heightMeasureSpec); /**以下代码主要是针对测量模式是wrap_content时需要动态的计算,如果是一些具体的大小或者match_parent时根本不不需要写onMearsure */ //整个自定义viewgroup的宽,高的最大值进行记录 int width = 0; int height = 0; //判断是否要换行时使用 int lineWidth = 0; int lineHeight = 0; //计算子view的个数 int cCount = getChildCount(); //for循环计算每个子view在viewgroup中的大小 for (int i = 0; i < cCount; i++) { View child = getChildAt(i); //measureChild这个方法来计算子view的大小和mode,这个viewGroup定义的一个方法,是个很重要的方法 measureChild(child, widthMeasureSpec, heightMeasureSpec); // 得到child的lp,MarginLayoutParams可以得到Margin数据。 MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); //measureChild方法过后,child.getMeasuredWidth()才能计算得到值,不然会为0 int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; //判断是否需要换行 if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) { //需要换行了,记录宽的最大值 width = Math.max(width, lineWidth); //换行后的新的lineWidth等于控件的宽 lineWidth = childWidth; height += lineHeight; lineHeight = childHeight; } else { //不需要换行 lineWidth += childWidth; lineHeight = Math.max(lineHeight, childHeight); } //最后一个view if (i == cCount - 1) { width = Math.max(lineWidth, width); height += lineHeight; } } //不同的mode进行一个简单的判断,viewGroup的真正的大小实际上就是通过这个方法进行设置的 setMeasuredDimension( // modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(), modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom()// ); }
上面的注释其实也是十分的详细了,上面有个很关键的方法,是进行测试子view的
measureChild(child, widthMeasureSpec, heightMeasureSpec);这个viewgroup的方法,我们来看下源码:
/** * Ask one of the children of this view to measure itself, taking into * account both the MeasureSpec requirements for this view and its padding. * The heavy lifting is done in getChildMeasureSpec. * * @param child The child to measure * @param parentWidthMeasureSpec The width requirements for this view * @param parentHeightMeasureSpec The height requirements for this view */ protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
getChildMeasureSpec()和measure()这两个方法的源码大家可以自己看看,主要是对子view进行测量。
3.
测量好了大小后,我们来看看是如何布局的,此时需要重写onLayout方法:
//记录所有的子view,并且再按行来进行一个区别记录 private List<List<View>> mAllViews = new ArrayList<List<View>>(); //记录没行的最大高度 private List<Integer> mLineHeight = new ArrayList<Integer>(); @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mAllViews.clear(); mLineHeight.clear(); int width = getWidth(); int lineWidth = 0; int lineHeight = 0; // 存储每一行所有的childView List<View> lineViews = new ArrayList<View>(); int cCount = getChildCount(); // 遍历所有的孩子 for (int i = 0; i < cCount; i++) { View child = getChildAt(i); MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); // 如果已经需要换行 if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width) { // 记录这一行所有的View以及最大高度 mLineHeight.add(lineHeight); // 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView mAllViews.add(lineViews); lineWidth = 0;// 重置行宽 lineViews = new ArrayList<View>(); } /** * 如果不需要换行,则累加 */ lineWidth += childWidth + lp.leftMargin + lp.rightMargin; lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin); lineViews.add(child); } // 记录最后一行 mLineHeight.add(lineHeight); mAllViews.add(lineViews); int left = 0; int top = 0; // 得到总行数 int lineNums = mAllViews.size(); for (int i = 0; i < lineNums; i++) { // 每一行的所有的views lineViews = mAllViews.get(i); // 当前行的最大高度 lineHeight = mLineHeight.get(i); // 遍历当前行所有的View for (int j = 0; j < lineViews.size(); j++) { View child = lineViews.get(j); if (child.getVisibility() == View.GONE) { continue; } MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); //计算childView的left,top,right,bottom int lc = left + lp.leftMargin; int tc = top + lp.topMargin; int rc =lc + child.getMeasuredWidth(); int bc = tc + child.getMeasuredHeight(); //以上所有的计算都是为了获取这四个参数,来进行子view的布局。 child.layout(lc, tc, rc, bc); left += child.getMeasuredWidth() + lp.rightMargin + lp.leftMargin; } left = 0; top += lineHeight; } }
代码也不是很难,细心一点。
4.
实际上,我们可以发现,该自定义控件实现的逻辑挺简单的,主要是:
(1)onMeasure中,通过计算每个子view的大小才确定viewgroup的大小。
(1)onLayout中,动态的计算子view的位置坐标然后进行一个布局。
由此也可以发现,自定义viewgroup最重要的也就是这两个方法,但是,其中很多小的细节也是需要我们注意到的,比如:
(a)只有子view调用了measure方法,才能调用getMeasureSpec获取到值,不然为0.
(b)用MarginLayoutParams来支持margin属性。
代码:
@Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); }
以上代码就可以简单实现自定义view的流式布局了。
最近搞了个Android技术分享的公众号,欢迎关注投稿。
- Android 自定义view(三) 继承ViewGroup
- Android自定义View(四)继承ViewGroup
- Android自定义控件-完全自定义(继承View ,ViewGroup)
- Android自定义View(三)继承View
- android 自定义View学习总结-继承自ViewGroup(2)
- 自定义View继承ViewGroup
- 自定义View继承ViewGroup
- android自定义View(viewGroup)
- android自定义View、ViewGroup
- Android继承自View和ViewGroup,自定义控件
- android 自定义View学习总结-继承自ViewGroup
- android 自定义View开发实战(三) 自定义ViewGroup--FourLayout
- Android View体系(十一)自定义ViewGroup
- android 自定义 view 之ViewGroup(四)
- Android 自定义View基础(三)--ViewGroup
- 自定义view继承viewgroup的解释
- 自定义View学习笔记之继承ViewGroup
- 自定义view继承viewgroup,实现组合按钮。
- virtualbox迁移至vcenter/vmware workstation
- Node.js websocket 使用 socket.io库实现实时聊天室
- Struts2标签嵌套以及遍历双层list
- 2.20.2 marital status
- 对cuda函数block中thread的理解(2)
- Android 自定义view(三) 继承ViewGroup
- 从莱布尼兹的微积分到我国十一五国家级规划教材
- java 序列化与反序列化
- HTML 参考手册- (HTML5 标准)
- el表达式和ognl表达式的区别
- JavaScript的基本介绍
- POJ 2728 Desert King (最优比例生成树)
- Codeforces #398 div2 A-D
- Arduino简单实例之三_土壤湿度传感器