自定義ViewGroup—FlowLayout

今天在網上看到兩篇關於自定義Flowlayout的實現方式,本身按着兩篇文章的思路也嘗試寫個demo學習一下,最後實現了效果。這個功能實現起來不算太複雜,主要是父容器的高度和換行的計算。下面是我最終實現的效果圖以及源碼.java

項目源碼

package com.hdd.androidreview.customView;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;

public class FlowLayout extends ViewGroup {

    private List<LineView> mLineViewList;
    private int marginLeft = 0;
    private int marginTop = 0;
    private int marginRight = 0;
    private int marginBottom = 0;

    public FlowLayout(Context context) {
        super(context);
    }

    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        LineView mLineView = null;
        mLineViewList = new ArrayList<>();
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int totalHeight = 0;//總高度
        int remaining = 0;//剩餘空間
        int usedWidth = 0;//已用寬度

        for (int i = 0; i < getChildCount(); i++) {
            View childView = getChildAt(i);

            //先測量子View
            measureChild(childView, widthMeasureSpec, widthMeasureSpec);
            //獲取Margin值
            MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
            marginLeft = Math.max(marginLeft, lp.rightMargin);
            marginTop = Math.max(marginTop, lp.topMargin);
            marginRight = Math.max(marginRight, lp.rightMargin);
            marginBottom = lp.bottomMargin;
            int childWidth = childView.getMeasuredWidth() + marginLeft + marginRight;
            //判斷LineView是否爲空
            if (mLineView == null) {
                mLineView = new LineView();
                mLineViewList.add(mLineView);
            }
            //計算剩餘空間
            remaining = widthSize - usedWidth;

            //判斷子view的測量寬度是否大於剩餘空間,若是大於則另起一行
            if (childWidth > remaining) {
                //另起一行,已用寬度應該爲零
                usedWidth = 0;
                //添加View
                mLineView = new LineView();
                mLineViewList.add(mLineView);
            }
            mLineView.addView(childView);
            //已用寬度累加
            usedWidth += childWidth;
            mLineView.setTotalWidth(usedWidth);
        }

        for (int i = 0; i < mLineViewList.size(); i++) {
            //總高度=全部行數相加
            totalHeight += mLineViewList.get(i).mHeight;
        }
        //父容器的總高度=上下padding的高度,在最後額外加一個底部marginBottom
        totalHeight += getPaddingTop() + getPaddingBottom() + marginBottom;
        setMeasuredDimension(widthSize, heightMode == MeasureSpec.EXACTLY ? heightSize : totalHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        int left = l + getPaddingLeft();
        int top = t + getPaddingTop();
        for (int i = 0; i < mLineViewList.size(); i++) {
            LineView lineView = mLineViewList.get(i);
            lineView.layout(left, top);
            top += lineView.mHeight;
        }
    }

    public class LineView {
        private int mHeight = 0;//View的高
        private int totalWidth = 0;//共佔用多少寬度
        private List<View> childViews = new ArrayList<>();


        public void addView(View child) {
            childViews.add(child);
            mHeight = child.getMeasuredHeight() + marginTop;
        }

        //設置這一行共佔用了多少寬度
        public void setTotalWidth(int totalWidth) {
            this.totalWidth = totalWidth;
        }

        public void layout(int l, int t) {
            int left = l;
            int top = t;
            int parentWidth = getMeasuredWidth();
            //求爲每一個子View平均分配的偏移寬度
            int cMeanWith = (parentWidth - totalWidth) / (childViews.size() + 1);

            //設置每一個View的位置
            for (int i = 0; i < childViews.size(); i++) {
                View childView = childViews.get(i);
                int childWidth = childView.getMeasuredWidth();
                int childHeight = childView.getMeasuredHeight();

                int cLeft = cMeanWith + left + marginLeft;
                int cRight = cMeanWith + left + childWidth + marginRight;
                int cTop = top + marginTop;
                int cBottom = top + childHeight + marginBottom;

                //設置View的具體位置
                childView.layout(cLeft, cTop, cRight, cBottom);
                left += childWidth + marginLeft + marginRight + cMeanWith;
            }
        }
    }
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }
}
複製代碼

參考

自定義FlowLayout佈局android

一篇文章搞懂Android 自定義viewgroup的難點git

相關文章
相關標籤/搜索