Android 自定义ViewGroup
来源:互联网 发布:卖家淘宝客佣金链接 编辑:程序博客网 时间:2024/05/18 02:56
上一篇学习了Android自定义View的流程及步骤,这篇学习下如何去自定义一个ViewGroup,我们知道ViewGroup就是View的容器类,它内部包含了许多个控件,即一组view,而我们经常用的LinearLayout,RelativeLayout等都是ViewGroup的子类 , 所以它的整个绘制过程相对于View会复杂一点,但还是三个步骤measure,layout,draw。
构造函数
public MyLinearLayout(Context context) { this(context, null); } public MyLinearLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
代码中三个构造方法的调用时机:
在代码中直接new一个MyLinearLayout实例的时候,会调用第一个构造函数.
在xml布局文件中调用MyLinearLayout的时候,会调用第二个构造函数.
在xml布局文件中调用MyLinearLayout,并且MyLinearLayout标签中还有自定义属性时,这里调用的还是第二个构造函数.
也就是说,系统默认只会调用MyLinearLayout的前两个构造函数,至于第三个构造函数的调用,通常是我们自己在构造函数中主动调用的(例如上面代码中, 第一个和第二个构造函数通过this最终都会调用到第三个构造函数).
Measure
上一篇中我们提到了SpecMode三种模式,这里也是一样,如果layout_widht和layout_height是match_parent或具体的值时,就直接调用setMeasuredDimension()方法即可,如果layout_widht和layout_height是wrap_content时,这时我们需要遍历所有的子View,然后对每个子View进行测量,计算出最终ViewGroup的大小,(UNSPECIFIED 这个模式主要用于系统内部多次Measure的情形,一般来说用不到这个模式,不需要关注),
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int childCount = this.getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); measureChild(child, widthMeasureSpec, heightMeasureSpec); int childWidth= child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); }}
上面代码中getChildCount()方法是获取子View的数量,measureChild()方法,是调用子View的测量方法,从而通过child.getMeasuredWidth(),child.getMeasuredHeight()获取每个子view的宽高.
Layout
layout过程其实就是对子View的位置进行排列.
@Overrideprotected 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); }}
上面代码中child.layout(left,top,right,bottom)方法就是对每个子View的位置进行设置.
Draw
通常情况下, 我们一般不需要像自定义View一样重写onDraw() , 所以在这也不再介绍.
LayoutParams
通常情况下,我们需要让子view支持margin属性,如下:
<cn.lw.view.MyLinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorAccent"> <Button android:layout_width="200dp" android:layout_height="50dp" android:background="#ffffff" android:layout_margin="10dp" android:text="button1"/></cn.lw.view.MyLinearLayout>
然后在onMeasure方法中,让viewGroup每个子view的高度都加上设置了margin的高度,代码如下:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int lineHeight = 0; int n = getChildCount(); for (int i = 0; i < n; i++) { View view = getChildAt(i); measureChild(view, widthMeasureSpec, heightMeasureSpec); MarginLayoutParams p = (MarginLayoutParams) view.getLayoutParams(); int childheight = view.getMeasuredHeight() + p.topMargin + p.bottomMargin; lineHeight += childheight; } if (n == 0) { setMeasuredDimension(0, 0); } else { setMeasuredDimension(widthSize, heightMode == MeasureSpec.EXACTLY ? heightSize : lineHeight); } }
这里,需要注意的是 MarginLayoutParams p = (MarginLayoutParams) view.getLayoutParams();如果不重写LayoutParams相关的代码,这样直接转换会出现问题。所以,我们需要重写如下代码:让它返回MarginLayoutParams类型的对象:
/** * 获取布局文件中的布局参数 */ @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(),attrs); } /** * 获取默认的布局参数 */ @Override protected LayoutParams generateDefaultLayoutParams() { return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); } /** * 生成自己的布局参数 */ @Override protected LayoutParams generateLayoutParams(LayoutParams p) { return new MarginLayoutParams(p); }
简单的viewGroup例子:
public class MyLinearLayout extends ViewGroup { public MyLinearLayout(Context context) { this(context, null); } public MyLinearLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * 获取布局文件中的布局参数 */ @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(),attrs); } /** * 获取默认的布局参数 */ @Override protected LayoutParams generateDefaultLayoutParams() { return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); } /** * 生成自己的布局参数 */ @Override protected LayoutParams generateLayoutParams(LayoutParams p) { return new MarginLayoutParams(p); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int lineHeight = 0; int n = getChildCount(); for (int i = 0; i < n; i++) { View view = getChildAt(i); measureChild(view, widthMeasureSpec, heightMeasureSpec); MarginLayoutParams p = (MarginLayoutParams) view.getLayoutParams(); int childheight = view.getMeasuredHeight() + p.topMargin + p.bottomMargin; lineHeight += childheight; } if (n == 0) { setMeasuredDimension(0, 0); } else { setMeasuredDimension(widthSize, heightMode == MeasureSpec.EXACTLY ? heightSize : lineHeight); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int mWidth = getWidth(); int count = getChildCount(); int lineHeight = 0; for (int i = 0; i < count; i++) { View child = getChildAt(i); MarginLayoutParams p = (MarginLayoutParams) child.getLayoutParams(); int left = (mWidth / 2) - (child.getMeasuredWidth() / 2); int lc = left; int tc = p.topMargin + lineHeight; int rc = child.getMeasuredWidth() + left; int bc = tc + child.getMeasuredHeight(); child.layout(lc, tc, rc, bc); lineHeight += child.getMeasuredHeight() + p.bottomMargin + p.topMargin; } }}
上面例子很简单,就是让子view垂直并且居中排列,主要通过onMeasure方法遍历所有view算出viewGroup的高度,然后在layout中通过child.layout(lc, tc, rc, bc)方法对每个子view进行布局,效果图如下:
完整代码链接:https://github.com/ivluowei/ViewTest
- 【Android】Android自定义ViewGroup
- android之自定义ViewGroup
- Android中自定义ViewGroup
- android自定义View(viewGroup)
- Android中自定义ViewGroup
- Android 自定义ViewGroup
- Android 自定义ViewGroup
- Android 自定义viewgroup
- Android 自定义ViewGroup
- android--自定义ViewGroup
- Android中自定义ViewGroup
- Android 自定义ViewGroup (一)
- Android 自定义ViewGroup (二)
- Android中自定义ViewGroup
- Android自定义ViewGroup详解
- Android-自定义ViewGroup
- Android 自定义ViewGroup
- Android自定义ViewGroup
- 数据结构实验之链表二:逆序建立链表
- 自己写“俄罗斯方块”(二).加入OnTimer()函数,实现落地效果
- 9月30日云栖精选夜读:阿里巴巴摘得LSVC桂冠 打造领先AI视频技术
- HTTP状态值详解
- question (单调栈)
- Android 自定义ViewGroup
- UVa10047-The Monocycle(bfs)
- 策略模式
- 云博士智能诊断功能全新升级
- Java 方法区及堆、栈(转了慢慢看)
- 数据库优化2
- btree索引和hash索引的区别
- EditText单击触发onclick事件处理
- 在一个activity中kill其他的activity以及退出整个应用程序