文本的描述中穿插图片更容易引起使用者的兴趣和关注,Android中常使用的处理富文本的类,如SpannableString
、SpannableStringBuilder
等允许在字符串中应用不同的样式、颜色、字体效果等。
文本中穿插图片可以使用ImageSpan
或者DynamicDrawableSpan
来实现:
kotlin
val content = "秋天,在不知不觉中,悄然到来。叶的飞去,不是因为风的追求,而是树的不挽留!"
val tag = "秋天,在不知不觉中,悄然到来。"
val spannableString = SpannableString(content)
val index = content.indexOf(tag) + tag.length
val tag2 = "秋天"
val index2 = content.indexOf(tag2)
spannableString.setSpan(
BackgroundColorSpan(0x66000000),
index2,
index2 + tag2.length,
Spannable.SPAN_INCLUSIVE_EXCLUSIVE
)
val drawable: Drawable? = ContextCompat.getDrawable(this, R.drawable.leaf)
drawable?.let {
it.setBounds(0, 0, PxUtil.dp2px(35f), PxUtil.dp2px(35f))
val imageSpan = ImageSpan(it)
spannableString.setSpan(imageSpan, index, index + 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
}
r.tv1.text = spannableString
观察效果发现,图片切件与文本没有居中。ImageSpan
类有重载构造函数可以传递参数verticalAlignment
设置垂直对齐方式,但是ImageSpan.ALIGN_CENTER
有版本限制,而且即使传递该参数,图片与文本也没有垂直对齐。ImageSpan
继承自DynamicDrawableSpan
,DynamicDrawableSpan的draw方法中有对ALIGN_CENTER参数进行处理:
通过代码可以看到,绘制图片切件的时候只是依据当前显示范围进行了垂直居中,而并没有针对文本的显示进行居中处理。想要实现图片与文本在垂直方向上居中,可以自定义view,重写DynamicDrawableSpan的getSize
与draw
方法,自定义view实现效果如下:
kotlin
class CenterVerticalImageSpan(
val context: Context,
private val resId: Int,
val width: Int,
val height: Int
) :
ImageSpan(context, resId) {
private var mDrawableRef: WeakReference<Drawable>? = null
override fun getSize(
paint: Paint,
text: CharSequence?,
start: Int,
end: Int,
fm: Paint.FontMetricsInt?
): Int {
val d: Drawable = getCachedDrawable()
val rect = d.bounds
fm?.let {
val fontHeight = it.descent - it.ascent
val fontCenterY = it.ascent + fontHeight / 2
val drawableHeight = rect.height()
// 重新设置文本位置
val ascentTop = it.ascent - it.top
it.top = fontCenterY - drawableHeight / 2
it.ascent = it.top + ascentTop
val descentBottom = it.bottom - it.descent
it.bottom = fontCenterY + drawableHeight / 2
it.descent = it.bottom - descentBottom
}
return rect.right
}
override fun draw(
canvas: Canvas,
text: CharSequence?,
start: Int,
end: Int,
x: Float,
top: Int,
y: Int,
bottom: Int,
paint: Paint
) {
val drawable = getCachedDrawable()
canvas.save()
val fm = paint.fontMetricsInt
val fontHeight = fm.descent - fm.ascent
val fontCenterY = y + fm.descent - fontHeight / 2
val transY = fontCenterY - drawable.bounds.height() / 2f
canvas.translate(x, transY)
drawable.draw(canvas)
canvas.restore()
}
override fun getDrawable(): Drawable {
val drawable: Drawable? = ContextCompat.getDrawable(context, resId)
drawable?.setBounds(0, 0, width, height)
?: run {
if (BuildConfig.DEBUG) {
throw RuntimeException("???")
}
}
return drawable!!
}
private fun getCachedDrawable(): Drawable {
val wr = mDrawableRef
var d: Drawable? = null
wr?.let {
d = it.get()
} ?: run {
d = drawable
mDrawableRef = WeakReference<Drawable>(d)
}
return d!!
}
}