FlowLayout 自定義流式布局

上效果圖

image.png
package com.zt.flowlayout.weget;

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 int mHorizontalSpacing = dp2px(16);
    //縱向分割 寬度
    private int mVerticalSpacing = dp2px(8);

    private List<List<View>> allLineViews = new ArrayList<>();
    private List<Integer> lineHeights = new ArrayList<>();


    /**
     * 通過new 創(chuàng)建對象
     *
     * @param context
     */
    public FlowLayout(Context context) {
        super(context);
    }

    /**
     * 通過反射
     *
     * @param context
     * @param attrs
     */
    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    /**
     * 防止內(nèi)存抖動(dòng) , 每次清空而不是 重新創(chuàng)建
     */
    private void clearMeasureParams() {
        allLineViews.clear();
        lineHeights.clear();
    }

    /**
     * 框架的寬高
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        clearMeasureParams();

        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();
        //獲取父控件的寬度高度
        int selfWidth = MeasureSpec.getSize(widthMeasureSpec);  //ViewGroup解析的父親給我的寬度
        int selfHeight = MeasureSpec.getSize(heightMeasureSpec); // ViewGroup解析的父親給我的高度

        //保存當(dāng)前行所有view
        List<View> lineViews = new ArrayList<>();
        //當(dāng)前行高度
        int lineHeight = 0;
        //當(dāng)前行寬度
        int lineWidthUsed = 0;


        int parentNeededWidth = 0;  // measure過程中,子View要求的父ViewGroup的寬
        int parentNeededHeight = 0; // measure過程中,子View要求的父ViewGroup的高


        //獲取所有孩子計(jì)算 遞歸計(jì)算孩子所需寬高
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            LayoutParams lp = childView.getLayoutParams();
            int childMeasureSpecWidth = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, lp.width);
            int childMeasureSpecHeight = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, lp.height);
            childView.measure(childMeasureSpecWidth, childMeasureSpecHeight);

            //當(dāng)前view 的具體寬 高
            int childMeasuredWidth = childView.getMeasuredWidth();
            int childMeasuredHeight = childView.getMeasuredHeight();


            //判斷是否需要換行
            if (lineWidthUsed + childMeasuredWidth + mHorizontalSpacing > selfWidth) {
                //保存當(dāng)前行 所有控件
                allLineViews.add(lineViews);
                //保存當(dāng)前行高
                lineHeights.add(lineHeight);
                //記錄 當(dāng)前父控件所需寬高
                parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed);
                parentNeededHeight += lineHeight + mVerticalSpacing;


                //當(dāng)前行 相關(guān)保存數(shù)據(jù) 重置
                lineViews = new ArrayList<>();
                lineHeight = 0;
                lineWidthUsed = 0;
            }

            //保存當(dāng)前行所有控件
            lineViews.add(childView);
            //計(jì)算出最大高度//如每個(gè)控件高度不一樣
            lineHeight = Math.max(lineHeight, childMeasuredHeight);
            //當(dāng)前行寬度
            lineWidthUsed = lineWidthUsed + childMeasuredWidth + mHorizontalSpacing;

            if (i == childCount - 1) {//防止最后一個(gè)控件 是需要換行
                //保存當(dāng)前行 所有控件
                allLineViews.add(lineViews);
                //保存當(dāng)前行高
                lineHeights.add(lineHeight);
                //記錄 當(dāng)前父控件所需寬高
                parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed);
                parentNeededHeight += lineHeight;

            }
        }
        //再度量自己,保存
        //根據(jù)子View的度量結(jié)果,來重新度量自己ViewGroup
        // 作為一個(gè)ViewGroup,它自己也是一個(gè)View,它的大小也需要根據(jù)它的父親給它提供的寬高來度量
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int realWidth = (widthMode == MeasureSpec.EXACTLY) ? selfWidth : parentNeededWidth;
        int realHeight = (heightMode == MeasureSpec.EXACTLY) ? selfHeight : parentNeededHeight;
        setMeasuredDimension(realWidth, realHeight);
    }

    /**
     * 確定 每個(gè)子view 的位置
     *
     * @param changed
     * @param l
     * @param t
     * @param r
     * @param b
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //確定第一個(gè)控件的xy位置 就是 paddingTop 和 paddingLeft w
        int curL = getPaddingLeft();
        int curT = getPaddingTop();

        int lineCount = allLineViews.size();
        for (int i = 0; i < lineCount; i++) {
            //獲取所有行數(shù) 的控件
            List<View> views = allLineViews.get(i);
            //獲取當(dāng)前行高
            int height = lineHeights.get(i);
            //當(dāng)前行數(shù)有幾個(gè)控件
            int nowLienView = views.size();
            for (int j = 0; j < nowLienView; j++) {
                //確定第一個(gè)控件位置
                View view = views.get(j);
                //調(diào)用過 onMeasure 這個(gè)就有值,
                //右邊位置 需要當(dāng)前寬度 加上左邊位置
                int curR = view.getMeasuredWidth() + curL;
                //下邊位置 需要當(dāng)前高度 加上上邊位置
                int curB = view.getMeasuredHeight() + curT;
                view.layout(curL, curT, curR, curB);
                //第二個(gè)或者之后的 x 軸需要改變也就是 curL 需要加上當(dāng)前控件的寬 和 橫向分割寬度
                curL = curR + mHorizontalSpacing;
            }
            //第二行 left 需要重置,top 需要加上 上一行的高度 和 縱向分割寬度
            curL = getPaddingLeft();
            curT = curT + height + mVerticalSpacing;
        }

    }


    public int dp2px(float dpValue) {
        float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

}


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容