前言
在开发过程中,我们经常会遇到在下面的样式需求,如果只有一行的话,我们使用ImageView+TextView可以很快的实现,但是如果有多行文字,那就需要进行一下特殊处理了。
ImageSpan的使用
ImageSpan可以用来给TextView实现图文混排,使用方式如下
SpannableStringBuilder ssb = new SpannableStringBuilder(content);
ssb.replace(start, start + sub.length(), Save);
ssb.setSpan(new ImageSpan(this.activity, R.mipmap.ic_mm_1), 0, 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
contentTV.setText(ssb);
效果如下
更多可以查看:https://blog.csdn.net/qq_16430735/article/details/50427978
TextView添加标签
首先我们先定义一个Tag的布局,一个非常简单的TextView
<?xml version=1.0 encoding=utf-8?>
<TextView xmlns:android=http://schemas.android.com/apk/res/android
xmlns:tools=http://schemas.android.com/tools
android:layout_width=wrap_content
android:layout_height=wrap_content
android:background=#34B5E5
android:textColor=#FFFFFF
tools:text=新人专享>
</TextView>
既然我们要进行图文混排,那么就需要将TextView转换为bitmap然后使用ImageSpan来替换
public class TagView extends AppCompatTextView {
public void setTag(String tag, String text) {
SpannableStringBuilder builder = new SpannableStringBuilder(tag);
builder.append( );
builder.append(text);
TextView tagView = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.detail_pg_tag, null);
tagView.setText(tag);
Bitmap bitmap = convertViewToBitmap(tagView);
Drawable d = new BitmapDrawable(getResources(), bitmap);
d.setBounds(0, 0, tagView.getWidth(), tagView.getHeight());
ImageSpan span = new ImageSpan(d);
builder.setSpan(span, 0, tag.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
setText(builder);
}
private static Bitmap convertViewToBitmap(View view) {
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.buildDrawingCache();
return view.getDrawingCache();
}
}
完整代码如上,首先加载tag布局,然后转换为Bitmap。
没有其他问题了吗?
当我们的Tag的文字大小与间距和TextView的一样的时候是非常完美的,但是当我们的Tag文字大小比较大,或者比较小的时候,Tag就无法进行居中对齐了。
ImageSpan居中对齐
我们可以继承ImageSpan并重写draw方法,然后加入自己的绘制逻辑。
public class CenteredImageSpan extends ImageSpan {
public CenteredImageSpan(Drawable d) {
super(d);
}
@Override
public void draw(@NonNull Canvas canvas, CharSequence text,
int start, int end, float x,
int top, int y, int bottom, @NonNull Paint paint) {
// image to draw
Drawable b = getDrawable();
// font metrics of text to be replaced
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int transY = (y + fm.descent + y + fm.ascent) / 2
- b.getBounds().bottom / 2;
canvas.save();
canvas.translate(0, transY);
b.draw(canvas);
canvas.restore();
}
}
几个参数的介绍
y:替换文字的基线坐标。
top:替换改行文字的最顶部位置。
bottom:替换改行文字的最底部位置,行间距属于上一行,因而bottom是行间距的底部位置。
参数中的y代表TextView中文字的基线位置,通过FontMetricsInt我们可以算出文字在TextView的中间位置,然后我们让图片的中间位置与TextView的中间位置对齐即可,因为图片的默认绘制是从上到下,顶部贴着TextView的顶部,所以图片的中间位置则是图片高度的一半。
具体计算逻辑
y +fontMetricsInt.descent得到字体的descent线坐标
y + fontMetricsInt.ascent得到字体的ascent线坐标
两者相加除以2,得到改行文字的中线坐标
drawable.getBounds().bottom/2得到图片中线坐标
然后两者相减,即可获得图片需要移动多长的距离可以居中
最后将相减得到的距离传给canvas.translate(x,transY)即可
参考链接:
https://www.jianshu.com/p/04fbfe2b73cf
https://juejin.im/post/5b0ed4286fb9a009f02a9a64#heading-5
https://blog.csdn.net/qq_16430735/article/details/50427978