美文网首页
文字后面紧跟标签SpareLayout

文字后面紧跟标签SpareLayout

作者: 常强儿 | 来源:发表于2017-11-14 20:36 被阅读43次

    来一波需求

    有这样一种需求,前面一个View,后面要带着几个标签,如果前面的View不太大,那么标签紧跟标签向前移动(后三个条目),如果前面的View就很大,余下来足够的空间放标签(第一个条目)


    �酷我音乐的标签.png

    有一个想法

    自己定义一个ViewGroup,类似于水平布局的LinearLayout,优先测量后面的View,最后将剩余的空间给第一个View,在layout的时候从左向右摆放,最终实现效果,给新的ViewGroup起名叫SpareLayout

    • 测量的时候从最后一个子View开始测量
    • 累加后面所有View的宽度,将剩余空间给第一个View
    • 高度使用最大高度的View
    • layout时从左向右摆放测量好的View

    做一点实现

    按上面的思路重写onMeasure和onLayout方法

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        //余下的空间
        int spareWidth = 0;
        //最大高度
        int maxHeight = 0;
        //子view从后向前测量
        for (int i = getChildCount() - 1; i > 0; i--) {
            View child = getChildAt(i);
            //不可见的跳过
            if (child.getVisibility() == GONE) {
                continue;
            }
            //测量一个子View,并处理padding,margin
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
            int marginWidth = lp.leftMargin + lp.rightMargin;
            int marginHeight = lp.topMargin + lp.bottomMargin;
            spareWidth += child.getMeasuredWidth() + marginWidth;
            maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + marginHeight);
        }
        //最后来测量第一个View,使用的方式是AT_MOST,宽度是剩余空间
        View firstChild = getChildAt(0);
        FrameLayout.LayoutParams lp = (LayoutParams) firstChild.getLayoutParams();
        int marginWidth = lp.leftMargin + lp.rightMargin;
        int marginHeight = lp.topMargin + lp.bottomMargin;
        int paddingWidth = getPaddingLeft() + getPaddingRight();
        int paddingHeight = getPaddingTop() + getPaddingBottom();
        int firstViewWidthSpec =
             MeasureSpec.makeMeasureSpec(widthSize - spareWidth - marginWidth - paddingWidth,
                MeasureSpec.AT_MOST);
        measureChild(firstChild, firstViewWidthSpec, heightMeasureSpec);
        maxHeight = Math.max(firstChild.getMeasuredHeight() + marginHeight, maxHeight);
        //储存测量结果
        setMeasuredDimension(spareWidth + firstChild.getMeasuredWidth() + paddingWidth,
            maxHeight + paddingHeight);
    }
    

    测量得到了每一个View应该的大小,接下来就是摆放所有的子View,看过来onLayout()

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int left = 0;
        //从左向右排放View
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
            int leftStart = left + lp.leftMargin + getPaddingLeft();
            int topStart;
    
            //处理vertical的gravity
            final int verticalGravity = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
            switch (verticalGravity) {
                case Gravity.TOP:
                    //从上向下计算
                    topStart = lp.topMargin + getPaddingTop();
                    break;
                case Gravity.CENTER_VERTICAL:
                    //vertical的居中,是指view居中(除去这个SpareLayout的padding和子View的margin居中)
                    topStart = (t + getPaddingTop() + lp.topMargin +    //可以放view的空间上边
                        b - getPaddingBottom() - lp.bottomMargin        //可以放view的空间下边
                        - child.getMeasuredHeight()) / 2                //中心线
                        - t;                                            //计算出view的上边
                    break;
                case Gravity.BOTTOM:
                    //从下向上算的
                    topStart =
                        b - lp.bottomMargin - getPaddingBottom() - child.getMeasuredHeight() - t;
                    break;
                default:
                    //默认是在上面
                    topStart = lp.topMargin + getPaddingTop();
            }
            child.layout(leftStart, topStart, leftStart + child.getMeasuredWidth(),
                topStart + child.getMeasuredHeight());
            //累加左边已经使用的空间
            left += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
        }
    }
    

    这样实现的效果:


    �文本比较长的时候.png
    �文本比较短的情况.png

    提一些Tips

    如果后面几个标签已经很大的情况没有处理
    以前为这个效果试验了各种方式,跑包的时间都比停下来写这个控件时间长得多,以后注意不要再做这样的事
    使用linearLayout的weight属性并没有成功

    相关文章

      网友评论

          本文标题:文字后面紧跟标签SpareLayout

          本文链接:https://www.haomeiwen.com/subject/ogofvxtx.html