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//需要设置movementMethod,否则mClickableSpan不生效textView.movementMethod = LinkMovementMethod.getInstance()}
特别注意:
1、$text 后面有空格,这是为了避免图片把最后一个字符替换掉,根据需求确认是否加空格。
2、图片点击时会有背景变色的点击效果,这里设置为透明。
3、设置textView.movementMethod = LinkMovementMethod.getInstance(),否则点击事件不生效。
4、图片居中问题,我现在设置的DynamicDrawableSpan.ALIGN_CENTER,想要要的效果是图片居中,但是上面显示的图片并没有居中。
解决图片居中问题:新建类继承ImageSpan,重写draw和getSize方法
package com.fht.kotlin.widgetimport android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.text.style.ImageSpan
import androidx.annotation.DrawableRes/*** @author fenghaitao* @time 2021/12/4 09:20*/
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 = drawablecanvas.save()var transY = ((bottom - top) - b.bounds.bottom) / 2 + topif (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 = drawableval rect = d.boundsif (fm != null) {val fmPaint = paint.getFontMetricsInt()val fontHeight = fmPaint.bottom - fmPaint.topval drHeight = rect.bottom - rect.topval top = drHeight / 2 - fontHeight / 4val bottom = drHeight / 2 + fontHeight / 4fm.ascent = -bottomfm.top = -bottomfm.bottom = topfm.descent = top}return rect.right}
}