今天这篇博文主要来说说 自定义控件的 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 mLines = new ArrayList();

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 children = new ArrayList();

/**

* 添加一个子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方法摆放子控件

效果图:

3d90df78327e3e1101da56134d1be286.png 

cc1d283b4b96c278e9b9e8cf5597f10d.png

参考资料:

https://blog.csdn.net/shineflowers/article/details/48055879

https://blog.csdn.net/zxt0601/article/details/50533658

https://blog.csdn.net/shakespeare001/article/details/51089453

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐