Android 自定义ViewGroup
来源:互联网 发布:插画师dorami辞软件 编辑:程序博客网 时间:2024/06/05 17:09
ViewGroup是View的容器类,里面会包含多个View。经常用的LinearLayout,RelativeLayout等都是ViewGroup的子类。
还是从方法开始说明ViewGroup,Android 自定义View(二)函数分析 中已经有说明了一下方法函数的意思,ViewGroup的实现方法有必要的两个 onMeasure 和 onLayout 和自定义View的不同的是:
onDraw在自定义ViewGroup是,一般是调用了子类的onDraw方法,ViewGroup是View的容器,本身一般不需要draw额外的修饰,所以往往在onDraw方法里面,只需要调用ViewGroup的onDraw默认实现方法即可。(自定义View时onLayout是空方法,ViewGroup是onLayout却是必须实现的)
onMeasure
Measure过程还是测量ViewGroup的大小,如果layout_widht和layout_height是match_parent或具体的dp大小,直接调用setMeasuredDimension()方法,设置ViewGroup的宽高即可,如果是wrap_content,我们需要遍历所有的子View,然后对每个子View进行测量,然后根据子View的排列规则,计算出最终ViewGroup的大小。onLayout
layout过程其实就是对子View的位置进行排列,onLayout方法给我一个机会,来按照我们想要的规则自定义子View排列。
一个简单的栗子(ViewGroup 中的 View 排成一列)看下 onMeasure 和 onLayout 的代码实现:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec,heightMeasureSpec); measureChildren(widthMeasureSpec,heightMeasureSpec); }
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int height = 0; View child; for(int i = 0,size = getChildCount();i < size;i++) { child = getChildAt(i); child.layout(0, height, child.getMeasuredWidth(),height + child.getMeasuredHeight()); height += child.getMeasuredHeight(); } }
要实现View一列显示,然后每个子View的宽度是一样的,并且每个子View的left和right是一样的。所以每个子View只有top和bottom不一样。我们首先定义个高度height初始为0,然后得到所有子View的个数,依次设置每个子View的top和bottom。top就是定义的height,bottom则为height加上子View的高度。设置完后height累加。
LayoutParams
LayoutParams存储了子View在加入ViewGroup中时的一些参数信息,在继承ViewGroup类时,一般也需要新建一个新的LayoutParams类,就像SDK中我们熟悉的LinearLayout.LayoutParams,RelativeLayout.LayoutParams类等一样,那么可以这样做,在你定义的ViewGroup子类中,新建一个LayoutParams类继承与ViewGroup.LayoutParams。实际使用:
@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); }}
实例应用
自定义一个根据屏幕宽度自动换行的ViewGroup:
public class LineBreakLayout extends ViewGroup implements View.OnClickListener { private final static int VIEW_MARGIN = 2; private int widthMargin = VIEW_MARGIN;//view width space private int heightMargin = VIEW_MARGIN;//view height space private LineLayoutItemListener mListener; public LineBreakLayout(Context context) { super(context); } public LineBreakLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public LineBreakLayout(Context context, AttributeSet attrs) { super(context, attrs); } public void setViewMargin(int widthMargin, int heightMargin) { this.widthMargin = widthMargin; this.heightMargin = heightMargin; } public void setOnLineLayoutItemListener(LineLayoutItemListener listener) { mListener = listener; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //得到ViewGroup的初始宽高 final int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec) + getPaddingBottom()+getPaddingTop(); int line_height = 0; //获取第一个子View的起始点位置 int xpos = getPaddingLeft(); int ypos = getPaddingTop(); //计算每一个子View的尺寸,并算出ViewGroup的高度 for (int index = 0; index < getChildCount(); index++) { final View child = getChildAt(index); if (child.getVisibility() == GONE) { continue; } child.setId(index); child.setOnClickListener(this); final LayoutParams lp = child.getLayoutParams(); //算出子View宽的MeasureSpec值 int wSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.UNSPECIFIED); //算出子View高的MeasureSpec值 int hSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.UNSPECIFIED); //让子View记住自己宽高的MeasureSpec值,子View的 //函数传入的就是这里算出来的这两个值 child.measure(wSpec, hSpec); //设置完MeasureSpec值后调用View.getMeasuredWidth()函数算出View的宽度 final int childw = child.getMeasuredWidth(); //记录最大行高(子View的高度有可能不一样,行高取最大高度) line_height = Math.max(line_height, child.getMeasuredHeight() + heightMargin); if (xpos + childw + widthMargin > width) { //初始坐标的x偏移值+子View宽度>ViewGroup宽度 就换行 xpos = getPaddingLeft();//坐标x偏移值归零 ypos += line_height; //坐标y偏移值再加上本行的行高也就是换行 } //算出下一个子View的起始点x偏移值 xpos += childw + widthMargin; } if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) { //对高度期望值没有限制 height = ypos + line_height + heightMargin; } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { //达不到指定高度则缩小高度 if (ypos + line_height < height) { height = ypos + line_height + heightMargin; } } else { height = ypos + line_height + heightMargin; } //设置ViewGroup宽高值 setMeasuredDimension(width, height); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { final int count = getChildCount(); int parentWidth = right - left; top = 0;//clear top distance int row = 0;// which row lay you view relative to parent int lengthX = 0; // right position of child relative to parent int lengthY = top; // bottom position of child relative to parent //计算每一个子View的尺寸,并算出ViewGroup的高度 for (int i = 0; i < count; i++) { final View child = this.getChildAt(i); int width = child.getMeasuredWidth(); int height = child.getMeasuredHeight(); lengthX += width + widthMargin; lengthY = row * (height + heightMargin) + heightMargin + height + top; // if it can't drawing on a same line , skip to next line if (lengthX > parentWidth) { lengthX = width + widthMargin; row ++; lengthY = row * (height + heightMargin) + heightMargin + height + top; } child.layout(lengthX - width, lengthY - height, lengthX, lengthY); } } @Override public void onClick(View v) { if (mListener != null) { int position = v.getId(); mListener.onLineItemClick(this, v, position); } } public interface LineLayoutItemListener { void onLineItemClick(ViewGroup parent, View view, int position); }}
- 【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
- 机器学习(5) MNIST数据集
- java 面向对象 之泛型 以及泛型的构造方法 设置多个泛型 通配符
- 智能pos秘钥体系
- Mysql脚本升级之根据查询条件进行insert动作
- 51Nod-1049 最大子段和【DP】
- Android 自定义ViewGroup
- WIN下安装TensorFlow
- 清理weblogic缓存
- 离散题目2
- 文件路径读取
- dbForge Studio for MySQL v7.2发布,视图和界面全面更新|附下载
- Spring Boot 使用 Druid 和监控配置
- vpn
- LruCache