欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android Span富文本图文混排 - ImageSpan(图文垂直居中)

程序员文章站 2022-04-01 13:02:35
Spanable未完待续......

###为文字实现很丰富的特殊效果,当然少不了图文混排

so... 直接上效果(有直接使用和自定义垂直居中效果)

Android Span富文本图文混排 - ImageSpan(图文垂直居中)

 

##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相关使用...

>>Spannable

>>URLSpan/ClickableSpan

>>ReplacementSpan

>>ImageSpan

 

 

本文地址:https://blog.csdn.net/qq_20613731/article/details/107502304