Android 实现图文混排
需求:
- 在文字中间添加图片或者在文字后面添加图片;
- 文字换行后,图片在第二行的后面;
- 图片加点击事件,文字没有点击事件。
实现方案:
- 使用TextView + Imageview实现,第1点好实现,第2点不好搞;
- 自定义TextView实现;
- 使用ImageSpan + TextView实现。
今天说的是第三种实现:使用ImageSpan + TextView实现
效果图:
跨度标志:
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE //前后都不包括
- Spannable.SPAN_INCLUSIVE_EXCLUSIVE //前包括后不包括
- Spannable.SPAN_EXCLUSIVE_INCLUSIVE //前不包括后包括
- Spannable.SPAN_INCLUSIVE_INCLUSIVE //前后都包括
实现方法:
private fun setEndImageSpan(textView: TextView) {
val mEndImageSpan = ImageSpan(this, R.drawable.ic_device_selected, DynamicDrawableSpan.ALIGN_CENTER)
val text = textView.text.toString()
val mSpan = SpannableStringBuilder("$text ").apply {
setSpan(mEndImageSpan, length - 1, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
val mClickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
(widget as TextView).highlightColor =
resources.getColor(android.R.color.transparent)
}
}
mSpan.setSpan(mClickableSpan, mSpan.length - 1, mSpan.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.text = mSpan
textView.movementMethod = LinkMovementMethod.getInstance()
}
特别注意: 1、$text 后面有空格,这是为了避免图片把最后一个字符替换掉,根据需求确认是否加空格。 2、图片点击时会有背景变色的点击效果,这里设置为透明。 3、设置textView.movementMethod = LinkMovementMethod.getInstance(),否则点击事件不生效。 4、图片居中问题,我现在设置的DynamicDrawableSpan.ALIGN_CENTER,想要要的效果是图片居中,但是上面显示的图片并没有居中。
解决图片居中问题:新建类继承ImageSpan,重写draw和getSize方法
package com.fht.kotlin.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.text.style.ImageSpan
import androidx.annotation.DrawableRes
class ImageCenterSpan constructor(context: Context, @DrawableRes resourceId: Int, aligin: Int)
: ImageSpan(context, resourceId, aligin) {
private var aligin: Int = ALIGN_CENTER
init {
this.aligin = aligin
}
override fun draw(canvas: Canvas, text: CharSequence?, start: Int,
end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
val b = drawable
canvas.save()
var transY = ((bottom - top) - b.bounds.bottom) / 2 + top
if (aligin == ALIGN_BASELINE) {
transY -= paint.fontMetricsInt.descent
} else if (aligin == ALIGN_BOTTOM) {
transY = bottom - b.bounds.bottom
}
canvas.translate(x, transY.toFloat())
b.draw(canvas)
canvas.restore()
}
override fun getSize(paint: Paint, text: CharSequence?, start: Int,
end: Int, fm: Paint.FontMetricsInt?): Int {
val d = drawable
val rect = d.bounds
if (fm != null) {
val fmPaint = paint.getFontMetricsInt()
val fontHeight = fmPaint.bottom - fmPaint.top
val drHeight = rect.bottom - rect.top
val top = drHeight / 2 - fontHeight / 4
val bottom = drHeight / 2 + fontHeight / 4
fm.ascent = -bottom
fm.top = -bottom
fm.bottom = top
fm.descent = top
}
return rect.right
}
}
|