Android流式布局

Android流式布局

版权声明:本文为博主原创文章,未经博主允许不得转载。

接下来给大家带来一个简单的自定义示例,不多说,先上图:

效果图
这尼玛是什么东西(汗怎么成这样子了),不用在意这些细节,让我们往下看。

不管了,直接上代码

package com.boge.library;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

/**
 * @author boge
 * @version 1.0
 * @date 2016/11/14
 */

public class FlowLayout extends ViewGroup {
    /**
     * 存储行的集合,管理行
     */
    private List<Line> mLines = new ArrayList<>();

    /**
     * 水平和竖直的间距
     */
    private float vertical_space;
    private float horizontal_space;

    /***当前行*/
    private Line line;

    // 行的最大宽度,除去边距的宽度
    private int mMaxWidth;

    public FlowLayout(Context context) {
        this(context,null,0);
    }

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

    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
        vertical_space = typedArray.getDimension(R.styleable.FlowLayout_vertical_space, 0);
        horizontal_space = typedArray.getDimension(R.styleable.FlowLayout_horizontal_space, 0);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mLines.clear();
        line = null;

        // 获取总宽度
        int width = MeasureSpec.getSize(widthMeasureSpec);

        mMaxWidth = width - getPaddingRight() - getPaddingLeft();
        // ******************** 测量孩子 ********************
        // 遍历获取孩子
        int childCount = this.getChildCount();
        Log.i("test", "count:"+childCount);
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            // 测量孩子的宽和高
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);

            //测量完需要将孩子添加到管理行的孩子的集合中,将行添加到管理行的集合中
            if(line == null){
                //第一次添加孩子
                line = new Line(mMaxWidth, horizontal_space);
                //添加孩子
                line.addView(childView);
                //添加行
                mLines.add(line);
            } else {
                if(line.canAddView(childView)){
                    line.addView(childView);
                } else {
                    //添加到下一行
                    line = new Line(mMaxWidth, horizontal_space);
                    //添加孩子
                    line.addView(childView);
                    //添加行
                    mLines.add(line);
                }
            }
        }

        // ******************** 测量自己 *********************
        // 测量自己只需要计算高度,宽度肯定会被填充满的
        int height = getPaddingTop() + getPaddingBottom();
        for (int i = 0; i < mLines.size(); i++) {
            // 所有行的高度
            height += mLines.get(i).height;
        }
        // 所有竖直的间距
        height += (mLines.size()-1) * vertical_space;

        setMeasuredDimension(width, height);
    }

    @Override
    protected void onLayout(boolean b, int l, int t, int r, int buttom) {
        // 这里只负责高度的位置,具体的宽度和子孩子的位置让具体的行去管理

        l = getPaddingLeft();
        t = getPaddingTop();

        for (int k = 0 ; k < mLines.size() ; k++){
            Line line = mLines.get(k);
            line.layout(l, t);

            // 更新高度
            t += line.height;
            if (k != mLines.size() - 1) {
                // 不是最后一条就添加间距
                t += vertical_space;
            }
        }
    }

    public void setAdapter(TagAdapter adapter){
        List<String> list = adapter.getmDataList();
        for (int i = 0 ; i < list.size() ; i++){
            View view = adapter.getView(i, null, this);
            addView(view);
            final int finalI = i;
            view.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    if(onTagItemClick != null){
                        onTagItemClick.onItemCilck(view, finalI);
                    }
                }
            });
        }
        invalidate();
    }

    public interface OnTagItemClick{
        void onItemCilck(View view, int position);
    }

    private OnTagItemClick onTagItemClick;

    public void setOnTagItemClick(OnTagItemClick onTagItemClick) {
        this.onTagItemClick = onTagItemClick;
    }

    /**
     * 内部类,行管理器,管理每一行的孩子
     */
    public class Line{
        // 定义一个行的集合来存放子View
        private List<View> views = new ArrayList<>();
        // 行的最大宽度
        private int maxWidth;
        // 行中已经使用的宽度
        private int usedWidth;
        // 行的高度
        private int height;
        // 孩子之间的距离
        private float space;

        // 通过构造初始化最大宽度和边距
        public Line(int maxWidth, float horizontalSpace) {
            this.maxWidth = maxWidth;
            this.space = horizontalSpace;
        }

        /**
         * 添加孩子
         * @param childView
         */
        public void addView(View childView) {
            //获取到孩子节点的宽和高
            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            if(views.size() == 0){
                // 集合里没有孩子的时候
                if(childWidth > maxWidth){
                    usedWidth = maxWidth;
                } else {
                    usedWidth = childWidth;
                }
                height = childHeight;
            } else {
                usedWidth += childWidth + space;
                height = childHeight > height ? childHeight : height;
            }
            views.add(childView);
        }

        /**
         * 当前行是否能够添加孩子节点
         * @param childView
         * @return
         */
        public boolean canAddView(View childView) {
            //集合没有数据
            if(views.size() == 0)
                return true;
            //获取到孩子节点的宽和高
            int childWidth = childView.getMeasuredWidth();
            //孩子的宽是否大于该行剩下的宽
            return maxWidth - usedWidth - space > childWidth ? true : false;
        }

        /**
         * 指定孩子显示的位置
         *
         * @param t
         * @param l
         */
        public void layout(int l, int t) {
            //剩下的宽度平分
            int avg = (maxWidth - usedWidth) / views.size();
            for(View view : views){
                //获取到孩子节点的宽和高
                int childWidth = view.getMeasuredWidth();
                int childHeight = view.getMeasuredHeight();
                //把平分的宽度放到View中在重新测量
                view.measure(MeasureSpec.makeMeasureSpec(childWidth + avg, MeasureSpec.EXACTLY)
                            ,MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
                childWidth = view.getMeasuredWidth();
                // 指定位置
                view.layout(l, t, childWidth+l, childHeight+t);

                l += childWidth + space;
            }
        }
    }
}

好了,大功告成
所有的代码都在我的GitHub里面
https://github.com/mianmian520/FlowLayoutDemo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值