设计思路:onmeasure时设置子控件大小自适应,onLayout时设置子控件位置
public class WrapLayout extends ViewGroup {
//横向间隔
private int horizontalSpacing;
//纵向间隔
private int verticalSpacing;
public WrapLayout(Context context) {
super(context);
}
public WrapLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public WrapLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr);
}
/**
* 初始化
*
* @param attrs
* @param defStyleAttr
*/
private void init(AttributeSet attrs, int defStyleAttr) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.WrapLayout, defStyleAttr, 0);
horizontalSpacing = typedArray.getInt(R.styleable.WrapLayout_horizontalSpacing, 0);
horizontalSpacing = pxFromDp(horizontalSpacing);
verticalSpacing = typedArray.getInt(R.styleable.WrapLayout_verticalSpacing, 0);
verticalSpacing = pxFromDp(verticalSpacing);
}
@Override
protected void onLayout(boolean b, int left, int top, int right, int bottom) {
if (b) {
//获取容器宽度
int width = right - left;
//获取左内边距
int paddingLeft = getPaddingLeft();
//获取上内边距
int paddingTop = getPaddingTop();
//获取右内边距
int paddingRight = getPaddingRight();
//设置初始点位的x坐标为左内边距
int x = paddingLeft;
//设置初始点位的y坐标为上内边距
int y = paddingTop;
//设置下一行高度坐标
int maxHeight = paddingTop;
//获取子控件个数
int count = getChildCount();
for (int i = 0; i < count; i++) {
View childView = getChildAt(i);
int measuredHeight = childView.getMeasuredHeight();
int measuredWidth = childView.getMeasuredWidth();
//超过一行宽度后需要换行
if (x + measuredWidth + paddingRight > width) {
y = maxHeight;
x = paddingLeft;
}
//获取下一行的起始点
maxHeight = Math.max(y + measuredHeight + verticalSpacing, maxHeight);
//设置子view的位置
childView.layout(x, y, x + measuredWidth, y + measuredHeight);
//设置下一个子view的宽度起点x坐标
x = x + measuredWidth + horizontalSpacing;
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//获取子控件个数
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
//获取子控件
View view = getChildAt(i);
//设置子控件大小为自适应
view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private int dpFromPx(final float px) {
return (int) (px / getResources().getDisplayMetrics().density);
}
private int pxFromDp(final float dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
}
控件属性
<resources>
<declare-styleable name="WrapLayout">
<attr name="verticalSpacing" format="integer"/>
<attr name="horizontalSpacing" format="integer"/>
</declare-styleable>
</resources>