addView() 新增按鈕。public class FlowLayout extends ViewGroup {
private final int horizontalSpacing = 16; // dp 轉 px
private final int verticalSpacing = 16;
private int maxPerRow = 3;
public FlowLayout(Context context) { super(context); init(); }
public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); }
public FlowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); }
private void init() {
horizontalSpacing = dp2px(horizontalSpacing);
verticalSpacing = dp2px(verticalSpacing);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int usedWidth = 0;
int rowHeight = 0;
int totalHeight = getPaddingTop();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) continue;
measureChild(child, widthMeasureSpec, heightMeasureSpec);
int childW = child.getMeasuredWidth();
int childH = child.getMeasuredHeight();
if (usedWidth + childW > widthSize - getPaddingRight() && usedWidth > 0) {
// 換行
totalHeight += rowHeight + verticalSpacing;
usedWidth = 0;
rowHeight = 0;
}
usedWidth += childW + (usedWidth == 0 ? 0 : horizontalSpacing);
rowHeight = Math.max(rowHeight, childH);
}
totalHeight += rowHeight + getPaddingBottom();
setMeasuredDimension(
widthMode == MeasureSpec.EXACTLY ? widthSize : usedWidth + getPaddingLeft() + getPaddingRight(),
heightMode == MeasureSpec.EXACTLY ? heightSize : totalHeight
);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int parentWidth = r - l;
int curX = getPaddingLeft();
int curY = getPaddingTop();
int rowHeight = 0;
int childCountInRow = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) continue;
int childW = child.getMeasuredWidth();
int childH = child.getMeasuredHeight();
if (curX + childW > parentWidth - getPaddingRight() && childCountInRow > 0) {
// 換行
curX = getPaddingLeft();
curY += rowHeight + verticalSpacing;
rowHeight = 0;
childCountInRow = 0;
}
child.layout(curX, curY, curX + childW, curY + childH);
curX += childW + horizontalSpacing;
rowHeight = Math.max(rowHeight, childH);
childCountInRow++;
}
}
private int dp2px(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
}
public void setMaxPerRow(int max) { this.maxPerRow = max; }
}
FlowLayout flowLayout = findViewById(R.id.flow_layout);
for (int i = 0; i < 10; i++) {
Button btn = new Button(this);
btn.setText("Btn " + (i+1));
btn.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
flowLayout.addView(btn);
}
// 添加完成後觸發重新佈局
flowLayout.requestLayout();
com.google.android.flexbox:flexbox-layout 已完美支援流式、換行、權重分配,無需自寫 ViewGroup。app:horizontalSpacing 在 XML 設定。自定義 FlowLayout 覆寫 onMeasure/onLayout 可實現按鈕流式排列與自動換行,動態添加後呼叫 requestLayout() 即可刷新。生產環境建議直接使用 FlexboxLayout 獲得更完整、穩定的支援。