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
頁面入口展示:
當然了,實現這種需求的方式有很多種,這裏的這種方式比較取巧。大家如果有好的實現方式,還望不吝賜教,比心。