文字后面緊跟標(biāo)簽SpareLayout

來一波需求

有這樣一種需求,前面一個(gè)View,后面要帶著幾個(gè)標(biāo)簽,如果前面的View不太大,那么標(biāo)簽緊跟標(biāo)簽向前移動(dòng)(后三個(gè)條目),如果前面的View就很大,余下來足夠的空間放標(biāo)簽(第一個(gè)條目)


?酷我音樂的標(biāo)簽.png

有一個(gè)想法

自己定義一個(gè)ViewGroup,類似于水平布局的LinearLayout,優(yōu)先測量后面的View,最后將剩余的空間給第一個(gè)View,在layout的時(shí)候從左向右擺放,最終實(shí)現(xiàn)效果,給新的ViewGroup起名叫SpareLayout

  • 測量的時(shí)候從最后一個(gè)子View開始測量
  • 累加后面所有View的寬度,將剩余空間給第一個(gè)View
  • 高度使用最大高度的View
  • layout時(shí)從左向右擺放測量好的View

做一點(diǎn)實(shí)現(xiàn)

按上面的思路重寫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;
        }
        //測量一個(gè)子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);
    }
    //最后來測量第一個(gè)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);
    //儲(chǔ)存測量結(jié)果
    setMeasuredDimension(spareWidth + firstChild.getMeasuredWidth() + paddingWidth,
        maxHeight + paddingHeight);
}

測量得到了每一個(gè)View應(yīng)該的大小,接下來就是擺放所有的子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:
                //從上向下計(jì)算
                topStart = lp.topMargin + getPaddingTop();
                break;
            case Gravity.CENTER_VERTICAL:
                //vertical的居中,是指view居中(除去這個(gè)SpareLayout的padding和子View的margin居中)
                topStart = (t + getPaddingTop() + lp.topMargin +    //可以放view的空間上邊
                    b - getPaddingBottom() - lp.bottomMargin        //可以放view的空間下邊
                    - child.getMeasuredHeight()) / 2                //中心線
                    - t;                                            //計(jì)算出view的上邊
                break;
            case Gravity.BOTTOM:
                //從下向上算的
                topStart =
                    b - lp.bottomMargin - getPaddingBottom() - child.getMeasuredHeight() - t;
                break;
            default:
                //默認(rèn)是在上面
                topStart = lp.topMargin + getPaddingTop();
        }
        child.layout(leftStart, topStart, leftStart + child.getMeasuredWidth(),
            topStart + child.getMeasuredHeight());
        //累加左邊已經(jīng)使用的空間
        left += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
    }
}

這樣實(shí)現(xiàn)的效果:


?文本比較長的時(shí)候.png

?文本比較短的情況.png

提一些Tips

如果后面幾個(gè)標(biāo)簽已經(jīng)很大的情況沒有處理
以前為這個(gè)效果試驗(yàn)了各種方式,跑包的時(shí)間都比停下來寫這個(gè)控件時(shí)間長得多,以后注意不要再做這樣的事
使用linearLayout的weight屬性并沒有成功

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,058評(píng)論 25 709
  • 當(dāng)Android原生控件無法滿足需求時(shí)就要自定義View,只有掌握了View的測量過程 (measure)、布局過...
    小蕓論閱讀 7,921評(píng)論 7 54
  • 在《世間事》專題改版成《故事》專題之際,也迎來了《故事燴》第十六期。 文友們?cè)陂喿x《故事燴》,參與活動(dòng)的同時(shí),也驚...
    孤獨(dú)一刀閱讀 710評(píng)論 17 19
  • 剛結(jié)婚時(shí),兩人的愛情就像玫瑰花一樣,熾熱,激情。 有了孩子以后,兩人的關(guān)系像狂風(fēng)中的纖草,震顫,搖擺。 老了以后,...
    芒果兒閱讀 299評(píng)論 3 3
  • 古殿空山裹,名王有舊塋。秦陵和漢寢,不及此幽情 仿佛一個(gè)垂垂老矣的劍客,白發(fā)橫額,皺紋交錯(cuò),卻掩不住那提劍時(shí)...
    shindowy閱讀 1,185評(píng)論 0 1

友情鏈接更多精彩內(nèi)容