自定义一个TagLayout


自定义一个标签自动排版显示的布局。效果:


  思路  一、:确定布局的宽,高。

             宽度确定:所有行中,宽度最宽的行即为这个布局的宽。

             高度确定 :所有行的高度加起来  即 为这个布局的高度。



            二、摆放子View思路

                     如果能够确定每一个Child的 right 和 bottom ,或者 left,top,那么就可以遍历每一个child, 直接摆放即可。

                     那么就围绕这个思路 来计算每一个child的right和bottom.

                      这里我用ArrayList<int[]>  数据格式 来存储每一个child的right和bottom。int[] 数组里面存放着right ([o])和bottom( [1] ).


             三、确定换行时机 和 每一个child的right和bottom 。

                      遍历每一个child,获取child的measureWidth 和marginLeft marginRight,并且累加求和,注意,这里的累加求和就是每一个child的right 了。

                      当:

                        (tempLineWidth + currentChildWidth) > parentWidth  成立时 即表示该换行了。当换行时 就可以确定这一行所有child的bottom或者top了。


   代码:onMeasure

             

/**
     * 父容器体提供的宽高大小规格
     *
     * @param widthMeasureSpec  最大宽度(不能超过)
     * @param heightMeasureSpec 最大高度
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //根据父容器给定的规格和工具类, 得到每一个子view 想要的的大小。
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            // 注意这里的margin 属性不能用来计算宽高,因为属于位置概念 而不是大小概念。
            int childWantWidth = child.getLayoutParams().width;//+ lp.leftMargin + lp.rightMargin;
            int childWantHeight = child.getLayoutParams().height;//+ lp.topMargin + lp.bottomMargin;
            //注意 ,child想要的宽高 不包括 margin属性。所以这里不需要 加上 margin属性。
            int childMeasuredWidth = getChildMeasureSpec(widthMeasureSpec, 0, childWantWidth);
            int childMeasureHeight = getChildMeasureSpec(heightMeasureSpec, 0, childWantHeight);
            // 子view 自己测量 大小
            child.measure(childMeasuredWidth, childMeasureHeight);
//            measureChild(child, widthMeasureSpec, heightMeasureSpec);
//            measureChildWithMargins(child,widthMeasureSpec,0,heightMeasureSpec,0);

        }

        //测量viewgroup自己

        //根据父容器给定的规格(widthMeasureSpec,heightMeasureSpec)拿到自己的规格
        int widthMode = MeasureSpec.getMode(widthMeasureSpec); //mode
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int atMostWidth = MeasureSpec.getSize(widthMeasureSpec);  //value
        int atMostHeight = MeasureSpec.getSize(heightMeasureSpec);

        //根据拿到的规格计算出各种mode下的测量值。

        int viewGroupMeasuredWidth = 0;
        int viewGroupMeasuredHeight = 0;

        //每一行的高度
        int lineHeight = 0;
        int lineWidth = 0;
        // 遍历子view  同时测量viewgroup的宽高。
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();


            // 获取当前行高度
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            if ((lineWidth + child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin) > atMostWidth) {
                //需要换行显示
                viewGroupMeasuredWidth = atMostWidth;//Math.max(atMostWidth, viewGroupMeasuredWidth);
                //重新下一行
                lineWidth = 0;
                lineWidth += child.getMeasuredWidth();
                //累加当前行高度。
                viewGroupMeasuredHeight += lineHeight;
                lineHeight = 0;
                lineHeight = Math.max(lineHeight, childHeight);
            } else {
                // 累加 子view的宽度
                lineWidth += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
                lineHeight = Math.max(lineHeight, childHeight);
            }

            //在最后一个child确定 viewgroup 大小
            if (i == childCount - 1) {
                //? 如果最后一个控件宽度很大 怎么办,在最后取最大值。否则在循环中取累加值
                //测试 一个宽度很大的view 600dp
                viewGroupMeasuredWidth = Math.max(viewGroupMeasuredWidth, atMostWidth);

                lineHeight = Math.max(lineHeight, childHeight);
                viewGroupMeasuredHeight += lineHeight;
            }
        }

        if (widthMode == MeasureSpec.EXACTLY) {
            viewGroupMeasuredWidth = atMostWidth;
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            viewGroupMeasuredHeight = atMostHeight;
        }

        setMeasuredDimension(viewGroupMeasuredWidth, viewGroupMeasuredHeight);

    }


摆放的 代码:onLayout

/**
     * //摆放 采用(left,top) 和(right,bottom)这两个点来固定子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) {
        //摆放 采用(left,top) 和(right,bottom)这两个点来固定子view的位置
        int left = 0;
        int top = 0;
        int right = 0;
        int bottom = 0;

        int childCount = getChildCount();
        int parentWidth = getMeasuredWidth();
        ArrayList<Integer> lineViewRight = new ArrayList<>(); //保存所有view的right坐标
        //保存所有view的right和bottom。
        // right:(marginLeft+measuredWidth+marginRight),
        // bottom:(marginTop+ measuredHeight+marginBottom)
        ArrayList<int[]> wh = new ArrayList();

        int tempLineWidth = 0; //当前行view的累加宽度(也就是right)
        int tempLineHeight = 0; //当前行最高高度
        int newLineTag = 0;   //换行标记
        int fianlHeight = 0;  //当前view的bottom
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            int currentChildWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            int currentChildHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

	 // 核心计算 代码
            if ((tempLineWidth + currentChildWidth) > parentWidth) { //换行


                //保存上一行所有view的right和bottom
                fianlHeight += tempLineHeight;
                for (int j = 0; j < newLineTag; j++) {
                    int finalR = lineViewRight.get(j);
                    int[] rightHeight = {finalR, fianlHeight};
                    wh.add(rightHeight);
                }

                lineViewRight.clear();
                newLineTag = 0;
                tempLineHeight = 0;

                //宽
                tempLineWidth = 0;
                tempLineWidth += currentChildWidth;

                lineViewRight.add(currentChildWidth);
                newLineTag++;
                //高
                tempLineHeight = Math.max(tempLineHeight, currentChildHeight);
            } else {
                newLineTag++;
                //宽 继续追加排列
                tempLineWidth += currentChildWidth;
                lineViewRight.add(tempLineWidth); //记录每一view的right。

                //高
                tempLineHeight = Math.max(tempLineHeight, currentChildHeight);
            }

            //最后一行
            if (i == childCount - 1) {
                fianlHeight += tempLineHeight;
                for (int j = 0; j < lineViewRight.size(); j++) {
                    int finalR = lineViewRight.get(j);
                    int[] rightHeight = {finalR, fianlHeight};
                    wh.add(rightHeight);
                }
            }

        }

        System.out.println("childcount:" + childCount + "--wh:" + wh.size());
        if (childCount != wh.size()) {
            throw new IllegalArgumentException("layout 摆放计算失败。");
        }


        int len = wh.size();
        for (int i = 0; i < len; i++) {
            View child = getChildAt(i);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

            int[] widthHeight = wh.get(i);
            int childRight = widthHeight[0];
            int childBottom = widthHeight[1];

            left = childRight - child.getMeasuredWidth() - lp.rightMargin;
            top = childBottom - child.getMeasuredHeight() - lp.bottomMargin;
            right = childRight - lp.rightMargin;//或者left+child.getMeasuredWidth();
            bottom = childBottom - lp.bottomMargin;

            child.layout(left, top, right, bottom);

        }


    }




 /**
     * 如果自定义viewgroup 在测量时需要margin 属性用来计算的,复写父类此方法。
     * 返回MarginLayoutParams 即可。
     *
     * @param attrs
     * @return
     */
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }


使用:

  <example.nzh.com.beisaier.view.MyView2
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="3dp"
        android:background="#a8a8a8">


        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="@drawable/bg_week_nomal"
            android:text="方法" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="@drawable/bg_week_nomal"
            android:text="苹果" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="@drawable/bg_week_nomal"
            android:text="西红柿" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="@drawable/bg_week_nomal"
            android:text="香蕉苦瓜" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="@drawable/bg_week_nomal"
            android:text="方法方法方法方法方法" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="@drawable/bg_week_nomal"
            android:text="方法方法" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="@drawable/bg_week_nomal"
            android:text="方法方法" />


    </example.nzh.com.beisaier.view.MyView2>







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值