大飞

大飞 关注TA

挑战一切!

大飞

大飞

关注TA

挑战一切!

  •  世界的顶端
  • 混口饭吃
  • 写了333,609字

该文章投稿至Nemo社区   Android  板块 复制链接


Android自定义flowlayout

发布于 2017/06/19 09:11 2,152浏览 0回复 10,064


package com.rongke.mifan.jiagang.view.flowLayout;


import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.rongke.mifan.jiagang.R;
import com.zyf.fwms.commonlibrary.utils.AutoUtils;
import com.zyf.fwms.commonlibrary.utils.CommonUtils;
import java.util.ArrayList;
import java.util.List;


public class FlowLayout extends ViewGroup {
    private List valueList=new ArrayList<>();
    private OnFlowClickLisener lisener;
    public static final int DEFAULT_SPACING = 20;
    /**
     * 横向间隔
     */
    private int mHorizontalSpacing = DEFAULT_SPACING;
    /**
     * 纵向间隔
     */
    private int mVerticalSpacing = DEFAULT_SPACING;
    /**
     * 是否需要布局,只用于第一次
     */
    boolean mNeedLayout = true;
    /**
     * 当前行已用的宽度,由子View宽度加上横向间隔
     */
    private int mUsedWidth = 0;
    /**
     * 代表每一行的集合
     */
    private final List mLines = new ArrayList();
    private Line mLine = null;
    /**
     * 最大的行数
     */
    private int mMaxLinesCount = Integer.MAX_VALUE;
    Handler handler = new Handler();
    private Context context;
    private boolean isMore;

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

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

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

    /**
     * 初始化
     * @param context
     * @param attrs
     */
    private void initView(Context context, AttributeSet attrs) {
        this.context=context;
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
        mHorizontalSpacing = a.getInt(R.styleable.FlowLayout_horizontal_spacing, DEFAULT_SPACING);
        mVerticalSpacing = a.getInt(R.styleable.FlowLayout_vertical_spacing, DEFAULT_SPACING);

    }
    private TextView getDefualtTextView(){
        TextView  textView= (TextView) View.inflate(context,R.layout.flow_text_view,null);
        textView.setWidth(CommonUtils.dip2px(context,71));
        textView.setHeight(CommonUtils.dip2px(context,25));
        textView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                TextView textView= (TextView) v;
                int position = (int) textView.getTag();
                if(textView.isSelected()){
                    textView.setSelected(false);
                    if(valueList.contains(textView.getText().toString())){
                        valueList.remove(textView.getText().toString());
                    }

                }else {
                    if(!isMore){
                        resetView();
                    }
                    textView.setSelected(true);
                    if(!valueList.contains(textView.getText().toString())){
                        valueList.add(textView.getText().toString());
                    }
                }
            }
        });
        AutoUtils.autoTextSize(textView);
        return textView;
    }
    private void resetView(){
        valueList.clear();
        for(int i=0;ifalse);
        }
    }

    /**
     *
     * @param list  显示的列表
     * @param isMore  是否多选
     * @param lisener 回调
     */
    public void setView(List list,boolean isMore,OnFlowClickLisener lisener){
        valueList.clear();
        this.lisener=lisener;
        this.isMore=isMore;
        addViewList(list);
    }

    public interface OnFlowClickLisener{
        void onClick(List valueList);
    }


    private void addViewList(List list) {
        removeAllViews();
        for(int i=0;i/**
     * 水平宽度
     *
     * @param spacing
     */
    public void setHorizontalSpacing(int spacing) {
        if (mHorizontalSpacing != spacing) {
            mHorizontalSpacing = spacing;
            requestLayoutInner();
        }
    }

    /**
     * 上下宽度
     *
     * @param spacing
     */
    public void setVerticalSpacing(int spacing) {
        if (mVerticalSpacing != spacing) {
            mVerticalSpacing = spacing;
            requestLayoutInner();
        }
    }

    /**
     * 最大行数
     *
     * @param count
     */
    public void setMaxLines(int count) {
        if (mMaxLinesCount != count) {
            mMaxLinesCount = count;
            requestLayoutInner();
        }
    }

    private void requestLayoutInner() {
        handler.post(new Runnable() {
            @Override
            public void run() {
                requestLayout();
            }
        });
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingRight() - getPaddingLeft();
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();

        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

        restoreLine();// 还原数据,以便重新记录
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(sizeWidth, modeWidth == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : modeWidth);
            int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(sizeHeight, modeHeight == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : modeHeight);
            // 测量child
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

            if (mLine == null) {
                mLine = new Line();
            }
            int childWidth = child.getMeasuredWidth();
            mUsedWidth += childWidth;// 增加使用的宽度
            if (mUsedWidth <= sizeWidth) {// 使用宽度小于总宽度,该child属于这一行。
                mLine.addView(child);// 添加child
                mUsedWidth += mHorizontalSpacing;// 加上间隔
                if (mUsedWidth >= sizeWidth) {// 加上间隔后如果大于等于总宽度,需要换行
                    if (!newLine()) {
                        break;
                    }
                }
            } else {// 使用宽度大于总宽度。需要换行
                if (mLine.getViewCount() == 0) {// 如果这行一个child都没有,即使占用长度超过了总长度,也要加上去,保证每行都有至少有一个child
                    mLine.addView(child);// 添加child
                    if (!newLine()) {// 换行
                        break;
                    }
                } else {// 如果该行有数据了,就直接换行
                    if (!newLine()) {// 换行
                        break;
                    }
                    // 在新的一行,不管是否超过长度,先加上去,因为这一行一个child都没有,所以必须满足每行至少有一个child
                    mLine.addView(child);
                    mUsedWidth += childWidth + mHorizontalSpacing;
                }
            }
        }

        if (mLine != null && mLine.getViewCount() > 0 && !mLines.contains(mLine)) {
            // 由于前面采用判断长度是否超过最大宽度来决定是否换行,则最后一行可能因为还没达到最大宽度,所以需要验证后加入集合中
            mLines.add(mLine);
        }

        int totalWidth = MeasureSpec.getSize(widthMeasureSpec);
        int totalHeight = 0;
        final int linesCount = mLines.size();
        for (int i = 0; i < linesCount; i++) {// 加上所有行的高度
            totalHeight += mLines.get(i).mHeight;
        }
        totalHeight += mVerticalSpacing * (linesCount - 1);// 加上所有间隔的高度
        totalHeight += getPaddingTop() + getPaddingBottom();// 加上padding
        // 设置布局的宽高,宽度直接采用父view传递过来的最大宽度,而不用考虑子view是否填满宽度,因为该布局的特性就是填满一行后,再换行
        // 高度根据设置的模式来决定采用所有子View的高度之和还是采用父view传递过来的高度
        setMeasuredDimension(totalWidth, resolveSize(totalHeight, heightMeasureSpec));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (!mNeedLayout || changed) {//没有发生改变就不重新布局
            mNeedLayout = false;
            int left = getPaddingLeft();//获取最初的左上点
            int top = getPaddingTop();
            final int linesCount = mLines.size();
            for (int i = 0; i < linesCount; i++) {
                final Line oneLine = mLines.get(i);
                oneLine.layoutView(left, top);//布局每一行
                top += oneLine.mHeight + mVerticalSpacing;//为下一行的top赋值
            }
        }
    }

    /**
     * 还原所有数据
     */
    private void restoreLine() {
        mLines.clear();
        mLine = new Line();
        mUsedWidth = 0;
    }

    /**
     * 新增加一行
     */
    private boolean newLine() {
        mLines.add(mLine);
        if (mLines.size() < mMaxLinesCount) {
            mLine = new Line();
            mUsedWidth = 0;
            return true;
        }
        return false;
    }

    // ==========================================================================
    // Inner/Nested Classes
    // ==========================================================================

    /**
     * 代表着一行,封装了一行所占高度,该行子View的集合,以及所有View的宽度总和
     */
    class Line {
        int mWidth = 0;// 该行中所有的子View累加的宽度
        int mHeight = 0;// 该行中所有的子View中高度的那个子View的高度
        List views = new ArrayList();

        public void addView(View view) {// 往该行中添加一个
            views.add(view);
            mWidth += view.getMeasuredWidth();
            int childHeight = view.getMeasuredHeight();
            mHeight = mHeight < childHeight ? childHeight : mHeight;//高度等于一行中最高的View
        }

        public int getViewCount() {
            return views.size();
        }

        public void layoutView(int l, int t) {// 布局
            int left = l;
            int top = t;
            int cnull
点赞(0)
点了个评