您的位置:首页 > 移动开发 > Android开发

android--------自定义控件 之 ViewGroup

2018-05-05 00:30 405 查看
前面几篇讲了自定义控件的组合控件,地址:https://my.oschina.net/zhangqie/blog/1806507

今天这篇博文主要来说说 自定义控件的 ViewGroup。

什么是ViewGroup?

ViewGroup是一种容器。它包含零个或以上的View及子View

ViewGroup有什么作用?

ViewGroup内部可以用来存放多个View控件,并且根据自身的测量模式,来测量View子控件,并且决定View子控件的位置。这在下面会逐步讲解它是怎么测量及决定子控件大小和位置的。

自定义控件

public class FlowLayoutb extends ViewGroup {

private int horizontolSpacing;
private int verticalSpacing;

public FlowLayoutb(Context context) {
super(context);
init(context);
}

public FlowLayoutb(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}

private Line currentline;// 当前的行
private int useWidth = 0;// 当前行使用的宽度
private List<Line> mLines = new ArrayList<Line>();
private int width;

public FlowLayoutb(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

private void init(Context context) {
horizontolSpacing = Util.dip2px(13, context);
verticalSpacing = Util.dip2px(13, context);
}

// 测量 当前控件Flowlayout
// 父类是有义务测量每个子View的
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mLines.clear();
currentline = null;
useWidth = 0;

/**
* 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
*/
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
// 计算出所有的childView的宽和高
measureChildren(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingBottom() - getPaddingTop(); // 获取到宽和高
int childeWidthMode;
int childeHeightMode;
//  为了测量每个子View 需要指定每个子View测量规则
childeWidthMode = widthMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : widthMode;
childeHeightMode = heightMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : heightMode;
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childeWidthMode, width);
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childeHeightMode, height);
currentline = new Line();// 创建了第一行
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
// 测量每个子View
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
int measuredWidth = child.getMeasuredWidth();
useWidth += measuredWidth;// 让当前行加上使用的长度
if (useWidth <= width) {
currentline.addChild(child);//这时候证明当前的子View是可以放进当前的行里,放进去
useWidth += horizontolSpacing;
} else {
//换行
newLine(child);
}
}

if (!mLines.contains(currentline)) {
mLines.add(currentline);// 添加最后一行
}
int totalheight = 0;
for (Line line : mLines) {
totalheight += line.getHeight();
}
totalheight += verticalSpacing * (mLines.size() - 1) + getPaddingTop() + getPaddingBottom();

System.out.println(totalheight);
setMeasuredDimension(width + getPaddingLeft() + getPaddingRight(), resolveSize(totalheight, heightMeasureSpec));
}

private void newLine(View child) {
mLines.add(currentline);// 记录之前的行
currentline = new Line();// 创建新的一行
currentline.addChild(child);
useWidth = currentline.lineWidth;
}

private class Line {
int height = 0; //当前行的高度
int lineWidth = 0;
private List<View> children = new ArrayList<View>();

/**
* 添加一个子View
*
* @param child
*/
public void addChild(View child) {
children.add(child);
if (child.getMeasuredHeight() > height) {
height = child.getMeasuredHeight();
}
lineWidth += child.getMeasuredWidth();
}

public int getHeight() {
return height;
}

/**
* 返回子View的数量
*
* @return
*/
public int getChildCount() {
return children.size();
}

public void layout(int l, int t) {
lineWidth += horizontolSpacing * (children.size() - 1);
int surplusChild = 0;
int surplus = width - lineWidth;
if (surplus > 0 && children.size() > 0) {
surplusChild = surplus / children.size();
}
for (int i = 0; i < children.size(); i++) {
View child = children.get(i);
child.layout(l, t, l + child.getMeasuredWidth() + surplusChild, t + child.getMeasuredHeight());
l += child.getMeasuredWidth() + surplusChild;
l += horizontolSpacing;
}
}

}

// 分配每个子View的位置
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
l += getPaddingLeft();
t += getPaddingTop();
for (int i = 0; i < mLines.size(); i++) {
Line line = mLines.get(i);
line.layout(l, t);  //交给每一行去分配
t += line.getHeight() + verticalSpacing;
}
}

}

自定义ViewGroup的步骤:

继承ViewGroup,覆盖构造方法

重写onMeasure方法测量子控件和自身宽高

实现onLayout方法摆放子控件

效果图:





源码地址:https://github.com/DickyQie/android-custom-control

参考资料:
https://blog.csdn.net/shineflowers/article/details/48055879 https://blog.csdn.net/zxt0601/article/details/50533658 https://blog.csdn.net/shakespeare001/article/details/51089453
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息