开发过程当中咱们常常会遇到文字尾部添加标签的需求,看是很简单,其实蛮难作的。好比咱们的设计稿以下:java
打眼一看,一个水平方向线性布局就解决了,内部写两个TextView就行。android
<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,可是,要是文字是多行的呢,咱们的标签还能顺利的放在后面么?git
若是第一行文本过长,咱们就会获得下面的结果: 尾部标签显示不全:github
或者以下,尾部标签直接不显示。缓存
若是文字最后一行的剩余空间放不下咱们的尾部标签,比较通用的作法是让标签换行。bash
咱们想要的是这样的:app
接下来咱们一步步实现这个需求。布局
使用过SpannableStringBuilder的同窗都惊叹于它的强大,经过SpannableStringBuilder#setSpan()
咱们能够给TextView的文本设置独特的样式,好比加粗、斜体、字体大小、字体颜色、背景颜色、图片、删除线、下划线、点击事件等等。字体
咱们这里也使用SpannableStringBuilder + 特定Span
的方式来实现。ui
咱们的尾部标签实质上是给特定文本添加一个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
页面入口展现:
固然了,实现这种需求的方式有不少种,这里的这种方式比较取巧。你们若是有好的实现方式,还望不吝赐教,比心。