文字后面紧跟标签SpareLayout

来一波需求

有这样一种需求,前面一个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属性并没有成功

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,392评论 25 707
  • 当Android原生控件无法满足需求时就要自定义View,只有掌握了View的测量过程 (measure)、布局过...
    小芸论阅读 7,219评论 7 54
  • 在《世间事》专题改版成《故事》专题之际,也迎来了《故事烩》第十六期。 文友们在阅读《故事烩》,参与活动的同时,也惊...
    孤独一刀阅读 587评论 17 19
  • 刚结婚时,两人的爱情就像玫瑰花一样,炽热,激情。 有了孩子以后,两人的关系像狂风中的纤草,震颤,摇摆。 老了以后,...
    芒果儿阅读 265评论 3 3
  • 古殿空山裹,名王有旧茔。秦陵和汉寝,不及此幽情 仿佛一个垂垂老矣的剑客,白发横额,皱纹交错,却掩不住那提剑时...
    shindowy阅读 1,006评论 0 1