public class FlowLayout extends ViewGroup { private boolean centerHorizontal;//是否水平居中顯示 public FlowLayout(Context context) { super(context); } public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs); initFromAttributes(context, attrs); } public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initFromAttributes(context, attrs); } private void initFromAttributes(Context context, AttributeSet attrs) { /** //res/values/attr.xml //<declare-styleable name="FlowLayout"> // <!-- 是否橫向居中 --> // <attr name="centerHorizontal" format="boolean" /> //</declare-styleable> //下面的代碼配合res/values/attr.xml可以支持通過在xml中FlowLayout節點 //添加app:centerHorizontal="true"的方式設置水平居中屬性 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout); centerHorizontal = a.getBoolean(R.styleable.FlowLayout_centerHorizontal, centerHorizontal); a.recycle(); */ } @Override protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } @Override protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new LayoutParams(p.width, p.height); } public boolean isCenterHorizontal() { return centerHorizontal; } // 動態設置子view的居中狀態 public void setCenterHorizontal(boolean centerHorizontal) { if (this.centerHorizontal ^ centerHorizontal) { this.centerHorizontal = centerHorizontal; requestLayout(); } } /** * 負責設置子控件的測量模式和大小 根據所有子控件設置自己的寬和高 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 獲得它的父容器爲它設置的測量模式和大小 int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); int modeWidth = MeasureSpec.getMode(widthMeasureSpec); int modeHeight = MeasureSpec.getMode(heightMeasureSpec); // 如果是warp_content情況下,記錄寬和高 final int paddingWidth = getPaddingLeft() + getPaddingRight(); final int paddingHeight = getPaddingTop() + getPaddingBottom(); int width = paddingWidth; int height = paddingHeight; // 記錄每一行的寬度,width不斷取最大寬度 int lineWidth = paddingWidth; // 每一行的高度,累加至height int lineHeight = 0; mAllViews.clear(); mLineHeight.clear(); mLineWidth.clear(); // 存儲每一行所有的childView List<View> lineViews = new ArrayList<>(); int cCount = getChildCount(); // 遍歷每個子元素 for (int i = 0; i < cCount; i++) { View child = getChildAt(i); if (child.getVisibility() == GONE) { continue; } // 得到child的lp LayoutParams lp = (LayoutParams) child.getLayoutParams(); // 測量每一個child的寬和高 final int marginWidth = lp.rightMargin + lp.leftMargin; final int marginHeight = lp.topMargin + lp.bottomMargin; // 子控件測量的時候需要將父控件的padding和當前子控件的margin算進去 child.measure( getChildMeasureSpec(widthMeasureSpec, paddingWidth + marginWidth, lp.width), getChildMeasureSpec(heightMeasureSpec, paddingHeight + marginHeight, lp.height) ); // 當前子控件實際佔據的寬度 int childWidth = child.getMeasuredWidth() + marginWidth; // 當前子控件實際佔據的高度 int childHeight = child.getMeasuredHeight() + marginHeight; //如果加入當前child,則超出最大寬度,則得到目前最大寬度給width,累加height 然後開啓新行 if (lineWidth + childWidth > sizeWidth) { width = Math.max(width, lineWidth);// 取最大的 height += lineHeight;// 疊加當前高度 // 記錄這一行所有的View、總的寬度以及最大高度 mLineHeight.add(lineHeight); mLineWidth.add(lineWidth); // 將當前行的childView保存,然後開啓新的ArrayList保存下一行的childView mAllViews.add(lineViews); lineViews = new ArrayList<>(); // 重新開啓新行,開始記錄 lineWidth = paddingWidth; lineHeight = 0; } // 否則累加值lineWidth,lineHeight取最大高度 lineWidth += childWidth; lineHeight = Math.max(lineHeight, childHeight); // 如果是最後一個,則將當前記錄的最大寬度和當前lineWidth做比較 if (i == cCount - 1) { width = Math.max(width, lineWidth); height += lineHeight; } lineViews.add(child); } // 記錄最後一行 mLineHeight.add(lineHeight); mLineWidth.add(lineWidth); mAllViews.add(lineViews); setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight : height); } /** * 存儲所有的View,按行記錄 */ private List<List<View>> mAllViews = new ArrayList<>(); /** * 記錄每一行的最大高度 */ private List<Integer> mLineHeight = new ArrayList<>(); private List<Integer> mLineWidth = new ArrayList<>(); @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int paddingLeft = getPaddingLeft(); final int paddingRight = getPaddingRight(); final int paddingTop = getPaddingTop(); final int paddingBottom = getPaddingBottom(); int width = getWidth() - paddingLeft - paddingRight; int lineHeight; List<View> lineViews; int left = paddingLeft; int top = paddingTop; // 得到總行數 int lineNums = mAllViews.size(); for (int i = 0; i < lineNums; i++) { // 每一行的所有的views lineViews = mAllViews.get(i); // 當前行的最大高度 lineHeight = mLineHeight.get(i); if (centerHorizontal) { int leftRightSpace = width - (mLineWidth.get(i) - paddingLeft - paddingRight);//左右兩端空白空間 left += leftRightSpace / 2; } // 遍歷當前行所有的View for (int j = 0; j < lineViews.size(); j++) { View child = lineViews.get(j); if (child.getVisibility() == View.GONE) { continue; } LayoutParams lp = (LayoutParams) child.getLayoutParams(); //計算childView的left,top,right,bottom int lc = left + lp.leftMargin+20; int tc = top + lp.topMargin; int rc = lc + child.getMeasuredWidth(); int bc = tc + child.getMeasuredHeight(); rc = Math.min(rc, getWidth() - paddingRight - lp.rightMargin); bc = Math.min(bc, getHeight() - paddingBottom - lp.bottomMargin); child.layout(lc, tc, rc, bc);//佈局子控件 left += child.getMeasuredWidth() + lp.rightMargin + lp.leftMargin; } left = paddingLeft; top += lineHeight; } } public static class LayoutParams extends MarginLayoutParams { public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } public LayoutParams(MarginLayoutParams source) { super(source); } public LayoutParams(int width, int height) { super(width, height); } } }
流式佈局FlowLayout可以直接使用
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.