TextView文本尾部添加標籤,支持自動換行

TextView文本尾部添加標籤,支持自動換行

需求

開發過程中我們經常會遇到文字尾部添加標籤的需求,看是很簡單,其實蠻難做的。比如我們的設計稿如下:

打眼一看,一個水平方向線性佈局就解決了,內部寫兩個TextView就行。

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="20dp">

    <TextView
        android:id="@+id/tv3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="天行健,君子以自強不息;"
        android:textColor="#000"
        android:textSize="15sp" />

    <TextView
        android:id="@+id/tv4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/room_member_role_bg"
        android:paddingLeft="6dp"
        android:paddingRight="6dp"
        android:text="王蛋蛋的芭比"
        android:textColor="#000"
        android:textSize="15sp" />

</LinearLayout>

emmmmm,但是,要是文字是多行的呢,我們的標籤還能順利的放在後面麼?

如果第一行文本過長,我們就會得到下面的結果:
尾部標籤顯示不全:

或者如下,尾部標籤直接不顯示。

如果文字最後一行的剩餘空間放不下我們的尾部標籤,比較通用的做法是讓標籤換行。

我們想要的是這樣的:

接下來我們一步步實現這個需求。

使用SpannableStringBuilder + ImageSpan實現

使用過SpannableStringBuilder的同學都驚歎於它的強大,通過SpannableStringBuilder#setSpan()我們可以給TextView的文本設置獨特的樣式,比如加粗、斜體、字體大小、字體顏色、背景顏色、圖片、刪除線、下劃線、點擊事件等等。

我們這裏也使用SpannableStringBuilder + 特定Span的方式來實現。

我們的尾部標籤實質上是給特定文本添加一個drawable背景,經過調研發現,雖然我們可以通過ReplacementSpan等方式給文字繪製drawable背景,但是背景上方的文字卻不顯示,這個方案就暫時夭折了。

回過頭來,我們發現SpannableStringBuilder + ImageSpan可以實現將圖片自動換行,並且如果剩餘空間不足時圖片會自動換行,如下所示:

從第二個TextView可以看出,當前行的剩餘空間不夠放置ImageSpan圖片時,會自動換行。這樣就解決了我們的自動換行的問題。

我們接下來看下ImageSpan的構造方法,會發現它不僅支持Drawable,還支持Bitmap對象,具體如下:

我們還知道,View有個getDrawingCache()方法,它可以生成當前View的Bitmap緩存。

此時我們的實現思路可以串起來了,具體如下圖:

代碼實現

具體分爲四步:
①創建TextView對象,設置drawable背景,設置字體樣式,設置間距,設置文本等
②將View生成Bitmap對象
③根據Bitmap對象生成ImageSpan對象
④將ImageSpan對象設置到SpannableStringBuilder的對應位置

具體代碼如下:註釋寫的很清楚,這裏就不再贅述。

private void addTagToTextView(TextView target, String title, String tag) {
    if (TextUtils.isEmpty(title)) {
        title = "";
    }

    String content = title + tag;


    /**
     * 創建TextView對象,設置drawable背景,設置字體樣式,設置間距,設置文本等
     * 這裏我們爲了給TextView設置margin,給其添加了一個父容器LinearLayout。不過他倆都只是new出來的,不會添加進任何佈局
     */
    LinearLayout layout = new LinearLayout(this);
    TextView textView = new TextView(this);
    textView.setText(tag);
    textView.setBackground(getResources().getDrawable(R.drawable.room_member_role_bg));
    textView.setTextSize(12);
    textView.setTextColor(Color.parseColor("#FDA413"));
    textView.setIncludeFontPadding(false);
    textView.setPadding(ScreenUtils.dip2px(this, 6), 0,
            ScreenUtils.dip2px(this, 6), 0);
    textView.setHeight(ScreenUtils.dip2px(this, 17));
    textView.setGravity(Gravity.CENTER_VERTICAL);

    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    // 設置左間距
    layoutParams.leftMargin = ScreenUtils.dip2px(this, 6);
    // 設置下間距,簡單解決ImageSpan和文本豎直方向對齊的問題
    layoutParams.bottomMargin = ScreenUtils.dip2px(this, 3);
    layout.addView(textView, layoutParams);

    /**
     * 第二步,測量,繪製layout,生成對應的bitmap對象
     */
    layout.setDrawingCacheEnabled(true);
    layout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
    // 給上方設置的margin留出空間
    layout.layout(0, 0, textView.getMeasuredWidth() + ScreenUtils.dip2px(this, (6 + 3)), textView.getMeasuredHeight());
    // 獲取bitmap對象
    Bitmap bitmap = Bitmap.createBitmap(layout.getDrawingCache());
    //千萬別忘最後一步
    layout.destroyDrawingCache();

    /**
     * 第三步,通過bitmap生成我們需要的ImageSpan對象
     */
    ImageSpan imageSpan = new ImageSpan(this, bitmap);


    /**
     * 第四步將ImageSpan對象設置到SpannableStringBuilder的對應位置
     */
    SpannableStringBuilder ssb = new SpannableStringBuilder(content);
    //將尾部tag字符用ImageSpan替換
    ssb.setSpan(imageSpan, title.length(), content.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
    target.setText(ssb);
}

項目地址:Android_Base_Demo

具體代碼請看:SpannableStringBuilderActivity

頁面入口展示:

頁面入口展示

當然了,實現這種需求的方式有很多種,這裏的這種方式比較取巧。大家如果有好的實現方式,還望不吝賜教,比心。

參考

Textview轉化成Bitmap對象

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章