Android Span富文本图文混排 - ImageSpan(图文垂直居中)
程序员文章站
2022-04-01 13:02:35
Spanable未完待续......
###为文字实现很丰富的特殊效果,当然少不了图文混排
so... 直接上效果(有直接使用和自定义垂直居中效果)
##1 ImageSpan:
ImageSpan(context, resourceId,ImageSpan.ALIGN_CENTER)//默认为ALIGN_BOTTOM
//ALIGN_BOTTOM,ALIGN_BASELINE,ALIGN_CENTER
#使用
sb.insert(index, TEXT_SPACE)//占位,该位置用来实现图片
sb.setSpan(
ImageSpan(context, resourceId,ImageSpan.ALIGN_CENTER),
index,
index + TEXT_SPACE.length,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE
)
cc:上图中前半部分,使用ImageSpan来实现的,会发现如果是单行使用了属性DynamicDrawableSpan.ALIGN_CENTER,且添加显示的图片高度小于或等于文字高度时,该属性可以很好的起作用,但是如果是多行,其拼接后的效果有点...(具体可看看它源码实现,下面贴上DynamicDrawableSpan)
##2 DynamicDrawableSpan源码主要的两个方法:(供参考)
@Override
public int getSize(@NonNull Paint paint, CharSequence text,
@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@Nullable Paint.FontMetricsInt fm) {
Drawable d = getCachedDrawable();
Rect rect = d.getBounds();
if (fm != null) {
fm.ascent = -rect.bottom;
fm.descent = 0;
fm.top = fm.ascent;
fm.bottom = 0;
}
return rect.right;
}
@Override
public void draw(@NonNull Canvas canvas, CharSequence text,
@IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
int top, int y, int bottom, @NonNull Paint paint) {
Drawable b = getCachedDrawable();
canvas.save();
int transY = bottom - b.getBounds().bottom;
if (mVerticalAlignment == ALIGN_BASELINE) {
transY -= paint.getFontMetricsInt().descent;
} else if (mVerticalAlignment == ALIGN_CENTER) {
transY = (bottom - top) / 2 - b.getBounds().height() / 2;
}
canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
}
##3 图文混排,绝大多数需求实现是需要和文字对齐(图文垂直居中)
需要重写上面两个方法来实现自定义CenterImageSpan
class CenterImageSpan(context: Context, mResourceId: Int) :
ImageSpan(context, mResourceId, DynamicDrawableSpan.ALIGN_CENTER) {
var isSmallImage = false
override fun getSize(
paint: Paint,
text: CharSequence,
start: Int,
end: Int,
fm: Paint.FontMetricsInt?
): Int {
val drawable = drawable
val rect = drawable.bounds
if (fm != null) {
val fmPaint = paint.fontMetricsInt
val fontH = fmPaint.descent - fmPaint.ascent //文字行的高度(具体字符顶部到底部的真实高度)
val imageH = rect.bottom - rect.top // 图片的高度
//如果图片的高度 <= 文本的高度,可以直接使用 ImageSpan.ALIGN_CENTER 来实现垂直居中
//即将当前图片所在行的图片相对于文字的高度居中,但仅支持单行有效
if (imageH > fontH) {
isSmallImage = false
//这里直接将文字行的高度调整至Image高度对应的top和bottom,即将当前图片所在行的文字相对于图片的高度居中
fm.ascent = fmPaint.ascent - (imageH - fontH) / 2
fm.top = fmPaint.ascent - (imageH - fontH) / 2
fm.bottom = fmPaint.descent + (imageH - fontH) / 2
fm.descent = fmPaint.descent + (imageH - fontH) / 2
} else {
//如果是小图片,就直接使用DynamicDrawableSpan.getSize()里面但方法
isSmallImage = true
fm.ascent = -rect.bottom
fm.descent = 0
fm.top = fm.ascent
fm.bottom = 0
}
}
return rect.right
}
override fun draw(
canvas: Canvas,
text: CharSequence,
start: Int,
end: Int,
x: Float,
top: Int,
y: Int,
bottom: Int,
paint: Paint
) {
// 这里说明下:如果图片高度大于文字高度,通过重写getSize,使当前图片所在行的文字相对于图片的高度居中,可以直接用DynamicDrawableSpan.draw方法
val b = drawable
canvas.save()
var transY = bottom - b.bounds.bottom
if (isSmallImage) {
//但是对于多行使用小图标时,需要我们稍加改变使其居中
transY -= ((bottom - top) / 2 - b.bounds.height() / 2)
}
canvas.translate(x, transY.toFloat())
b.draw(canvas)
canvas.restore()
}
}
使用:
sb.insert(index, TEXT_SPACE)//占位,该位置用来实现图片
sb.setSpan(
CenterImageSpan(context, resourceId),//CenterImageSpan > ImageSpan
index,
index + TEXT_SPACE.length,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE
)
附上Activity示例:
private const val TEXT_CONTENT =
"Today is Jul 25th Today is Jul 25thToday is Jul 25thToday is Jul 25th"
private const val TEXT_SPACE = "占位"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_image_span)
text_type_image_size_center.text = displaySpannableSuperWithPic()
}
private fun displaySpannableSuperWithPic(): Spannable {
val sb = SpannableStringBuilder(TEXT_CONTENT)
sb.insert(37, TEXT_SPACE)//占位
var image = CenterImageSpan(this, R.drawable.ic_launcher_background)
val index = sb.indexOf(TEXT_SPACE)
sb.setSpan(
image,
index,
index + TEXT_SPACE.length,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE
)
return sb
}
cc:开头的效果中后半部分,使用CenterImageSpan来实现,具体说明已贴在其中,欢迎回复讨论...
Spanable相关使用...
本文地址:https://blog.csdn.net/qq_20613731/article/details/107502304