自定义ViewGroup实现流式布局

第一次写博客,感觉有点小激动呢!

看效果:


是根据match_zhang大神的流式布局的思路,然后自己实现的,其中onLayout方法和match_zhang大神的不一样。

大体思路:

1.测绘onMeasure;

2.摆放子控件 onLayout;

详细的match_zhang说的很详细了,我就不多说了。

因为受到某人(数学老师,谁说数学没用的)的启发,为什么onMeasure的时候明明已经计算出了行高,每个child对应的位置,何不保存起来呢,所以我新增了

保存子控件左上角坐标的List:

 private List<Point> viewPoints = new ArrayList<>();


保存子控件的坐标列表有了,那么重新修改onMeasure方法代码,测绘和定位一起做了,为了onLayout做准备

<pre name="code" class="java">    //在onMeasure里,测量所有子View的宽高,以及确定Viewgroup自己的宽高。
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //获取系统传递过来测量出的宽度 高度,以及相应的测量模式。
        //如果测量模式为 EXACTLY( 确定的dp值,match_parent),则可以调用setMeasuredDimension()设置,
        //如果测量模式为 AT_MOST(wrap_content),则需要经过计算再去调用setMeasuredDimension()设置
        int heightMeasure = MeasureSpec.getSize(heightMeasureSpec);
        int heigtMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthMeasure = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);

        //计算宽度 高度 //wrap_content测量模式下会使用到:
        int maxLineWidth = 0;
        int totalHeight = 0;
        //当前行高,宽
        int curLineHeight = 0;
        int curLineWidth = 0;

        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            //先测量子布局的宽高
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            MarginLayoutParams mlp = (MarginLayoutParams) child.getLayoutParams();
            int childWidth = child.getMeasuredWidth() + mlp.leftMargin + mlp.rightMargin;
            int childHeight = child.getMeasuredHeight() + mlp.topMargin + mlp.bottomMargin;
            //换行 当前行宽+需要放置的child宽度小于最大行宽,换行
            if (curLineWidth + childWidth > widthMeasure - getPaddingLeft() - getPaddingRight()) {
                //通过比较 当前行宽 和以前存储的最大行宽,得到最新的最大行宽,用于设置父控件的宽度
                maxLineWidth = Math.max(curLineWidth, maxLineWidth);
                //父控件的高度增加了,为当前高度+当前行的高度
                totalHeight = totalHeight + curLineHeight;
                //重置当前的行宽和行高
                curLineHeight = childHeight;
                curLineWidth = childWidth;

                //记录左上角坐标
                //换行坐标从下一行开始计算,必须等totalHeight设置完毕在初始化坐标
                Point point = new Point(mlp.leftMargin + getPaddingLeft(), totalHeight + getPaddingTop() + mlp.topMargin);
                viewPoints.add(i, point);
            } else {//摆放在同一行
                //记录左上角坐标
                Point point = new Point(getPaddingLeft()+curLineWidth + mlp.leftMargin, getPaddingTop() + totalHeight + mlp.topMargin);
                viewPoints.add(i, point);


                //不换行:叠加当前行宽 和 比较当前行高:
                curLineWidth = curLineWidth + childWidth;
                curLineHeight = Math.max(curLineHeight, childHeight);
            }
            //如果已经是最后一个View,要比较当前行的 宽度和最大宽度,叠加一共的高度
            if (i == getChildCount() - 1) {
                maxLineWidth = Math.max(maxLineWidth, curLineWidth);
                totalHeight = totalHeight + curLineHeight;
            }
        }
        //适配padding,如果是wrap_content,则除了子控件本身占据的大小+pidding
        setMeasuredDimension(widthMode != MeasureSpec.EXACTLY ? maxLineWidth + getPaddingLeft() + getPaddingRight() : widthMeasure,
                heigtMode != MeasureSpec.EXACTLY ? totalHeight + getPaddingTop() + getPaddingBottom() : heightMeasure);
    }


 

注意:当最后一个child测量高度时,totalHeight=totalHeight+curLineHeight;如果加child的高度,那么碰到最后一行行高测量错误的情况:

不能忘记重写generateLayoutParams是子控件支持margin属性:

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

因为在onMeasure中保存子控件的顶点坐标,那么onLayout中代码就相对比较简单了:

    @Override
    protected void onLayout(boolean change, int l, int t, int r, int b) {
        for (int i = 0; i < getChildCount(); i++) {
            Point p = viewPoints.get(i);
            View child = getChildAt(i);
            int cl = p.x;
            int cr = cl + child.getMeasuredWidth();
            int ct = p.y;
            int cb = ct + child.getMeasuredHeight();
            child.layout(cl, ct, cr, cb);
        }
    }

好了,附上布局文件activity_main(本人比较懒,在 match_zhang 大神基础上面修改的):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.baoying.flowlayoutdemo.FlowLayout
        android:background="#00c4ff"
        android:padding="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="有padding"
            android:padding="5dp"
            android:layout_margin="2dp"
            android:background="#ff7700"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="流式"
            android:padding="5dp"
            android:layout_margin="2dp"
            android:background="#ff7700"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Java"
            android:padding="15dp"
            android:layout_margin="2dp"
            android:background="#ff7700"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="RxJava"
            android:padding="5dp"
            android:layout_margin="2dp"
            android:background="#00ff44"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="2dp"
            android:padding="5dp"
            android:background="#ff7700"
            android:text="CardView" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="OKHttp"
            android:padding="25dp"
            android:layout_margin="2dp"
            android:background="#ff0008"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="2dp"
            android:padding="5dp"
            android:background="#d66f16"
            android:text="TabLayout" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="2dp"
            android:padding="5dp"
            android:background="#00aeff"
            android:text="FloatActionBar" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ViewPager"
            android:padding="5dp"
            android:layout_margin="2dp"
            android:background="#393cff"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="AsynkTask"
            android:padding="35dp"
            android:layout_margin="2dp"
            android:background="#ff6f00"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="群英传"
            android:padding="5dp"
            android:layout_margin="2dp"
            android:background="#048899"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="padding"
            android:padding="5dp"
            android:layout_margin="2dp"
            android:background="#8800ff"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="padding"
            android:padding="5dp"
            android:layout_margin="2dp"
            android:background="#ff8316"/>


    </com.baoying.flowlayoutdemo.FlowLayout>
</LinearLayout>


完整demo地址:http://download.csdn.net/detail/qq1012692771/9629963

参考:http://blog.csdn.net/zxt0601/article/details/50533658

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值