1. 程式人生 > >修改FlowLayout原始碼,讓其支援設定最大行數

修改FlowLayout原始碼,讓其支援設定最大行數

這裡所說的FlowLayout元件是來自鴻洋提供的一個流式佈局的框架
框架原始碼看這裡
框架作者部落格介紹看這裡

廢話不多說,直接上程式碼,主要就是修改FlowLayout的onMeasure和onLayout方法,同時我這裡還對onLayout方法優化了一下,減少了一次for迴圈處理行view,行高,行寬等資訊

public class FlowLayout extends ViewGroup {
    private static final String TAG = "FlowLayout";
    protected static final int LEFT =
-1; protected static final int CENTER = 0; protected static final int RIGHT = 1; protected List<List<View>> mAllViews = new ArrayList<List<View>>();//記錄所有行 protected List<Integer> mLineHeight = new ArrayList<Integer>();//記錄所有行高 protected List<Integer>
mLineWidth = new ArrayList<Integer>();//記錄所有行寬 protected List<View> lineViews = new ArrayList<>();//臨時記錄每行的view protected int mGravity; private int maxLine = -1;//最大行數 public void setMaxLine(int maxLine) { this.maxLine = maxLine; } public FlowLayout(Context context,
AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TagFlowLayout); mGravity = ta.getInt(R.styleable.TagFlowLayout_tag_gravity, LEFT); int layoutDirection = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()); if (layoutDirection == LayoutDirection.RTL) { if (mGravity == LEFT) { mGravity = RIGHT; } else { mGravity = LEFT; } } ta.recycle(); } public FlowLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FlowLayout(Context context) { this(context, null); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mAllViews.clear();//記錄所有行的view mLineHeight.clear();//記錄每一行的高度 mLineWidth.clear();//記錄每一行的寬度 lineViews.clear();//記錄每一行的view int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); int modeWidth = MeasureSpec.getMode(widthMeasureSpec); int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); int modeHeight = MeasureSpec.getMode(heightMeasureSpec); // wrap_content 最終寬高 int width = 0; int height = 0; //當前已用行寬高 int lineWidth = 0; int lineHeight = 0; int cCount = getChildCount(); for (int i = 0; i < cCount; i++) { View child = getChildAt(i); if (child.getVisibility() == View.GONE) { continue; } //測量子view measureChild(child, widthMeasureSpec, heightMeasureSpec); MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); //子View寬高 int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) { if (maxLine > 0 && mAllViews.size() + 1 >= maxLine) { //+1是因為後面還有最後一行 break;//超過最大行數跳出迴圈 } //需要換行 width = Math.max(width, lineWidth);//記錄最大行寬 height += lineHeight;//累加包裹內容所需的高度 //換行,儲存上一行資料 mLineHeight.add(lineHeight); mLineWidth.add(lineWidth); mAllViews.add(lineViews); //重置新行變數 lineWidth = 0;//重新賦值行寬 lineHeight = 0;//重新賦值行高 lineViews = new ArrayList<View>(); } //記錄當前行資料 lineWidth += childWidth;//累加行寬 lineHeight = Math.max(lineHeight, childHeight);//取當前行最大高度作為行高 lineViews.add(child); } //新增最後一行資料 //包裹內容所需的最大寬度 width = Math.max(lineWidth, width); height += lineHeight; mLineHeight.add(lineHeight); mLineWidth.add(lineWidth); mAllViews.add(lineViews); setMeasuredDimension( //父控制元件寬高確定則用確定的,否則用測量後的 modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(), modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom()// ); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //總寬 int width = getWidth(); //當前已用行寬高 int lineHeight = 0; //下面是對每一行的View進行佈局 int left = getPaddingLeft(); int top = getPaddingTop(); int lineNum = mAllViews.size(); for (int i = 0; i < lineNum; i++) { //獲取當前行和行高 lineViews = mAllViews.get(i); lineHeight = mLineHeight.get(i); // set gravity int currentLineWidth = this.mLineWidth.get(i); switch (this.mGravity) { case LEFT: left = getPaddingLeft(); break; case CENTER: left = (width - currentLineWidth) / 2 + getPaddingLeft(); break; case RIGHT: // 適配了rtl,需要補償一個padding值 ,從右邊向左開始佈局 left = width - (currentLineWidth + getPaddingLeft()) - getPaddingRight(); // 適配了rtl,需要把lineViews裡面的陣列倒序排,從右邊開始存放view Collections.reverse(lineViews); break; } //開始一行行地佈局子view for (int j = 0; j < lineViews.size(); j++) { View child = lineViews.get(j); if (child.getVisibility() == View.GONE) { continue; } MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); int lc = left + lp.leftMargin; int tc = top + lp.topMargin; int rc = lc + child.getMeasuredWidth(); int bc = tc + child.getMeasuredHeight(); child.layout(lc, tc, rc, bc); //更新下一個view新增到當前行的left left += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; } //更新下一個view新增到下一行的top top += lineHeight; } } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } @Override protected LayoutParams generateDefaultLayoutParams() { return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } @Override protected LayoutParams generateLayoutParams(LayoutParams p) { return new MarginLayoutParams(p); } /** * 獲取指定行數內的item個數 * * @return 每行的個數之和 * @lineNum 總行數 */ public int getTotalByLine(int lineNum) { int count = 0; if (lineNum <= mAllViews.size()) { for (int i = 0; i < lineNum; i++) { List<View> line = mAllViews.get(i); count += line.size(); } } return count; } }