绘制多行文本之中插入图片

概要

在绘制多行文本的时候,经常会有多行图片中间插入一张图片的需求。此时文字应该做到在图片之外的空白区域完成绘制,如图:

可以看到在绘制文字时,当遇到图片时应该跳过图片区域继续绘制

实现

绘制图片

第一步先绘制图片,确定图片的位置,比如将图片绘制在正中间,那么通过调用

canvas.drawBitmap(bitmap, startOffset, IMAGE_PADDING, mPaint)

就完成了图片的绘制,这里的startOffest负责控制在x轴方向上的绘制,IMAGE_PADDING负责控制在y轴方向上的绘制

绘制文字

private val mFontMetrics = Paint.FontMetrics()
private val mPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    textSize = 16.dp
    getFontMetrics(mFontMetrics)
}

override fun onDraw(canvas: Canvas) {
        //绘制图片
        canvas.drawBitmap(bitmap, startOffset, IMAGE_PADDING, mPaint)

        var count: Int
        var start = 0
        var verticalOffset = -mFontMetrics.top
        var maxWidth: Float
        var countLeft:Int
        var countRight:Int
        while (start < text.length) {
            //文字没有触碰到图片,可以绘制整行
            if (verticalOffset + mFontMetrics.bottom < IMAGE_PADDING
                || verticalOffset + mFontMetrics.top > IMAGE_PADDING + bitmapHeight) {
                maxWidth = width.toFloat()
                count = mPaint.breakText(text, start, text.length, true, maxWidth, null)
                canvas.drawText(text, start, start + count, 0f, verticalOffset, mPaint)
            } else {
                //文字触碰到图片了,需要在图片左右分别绘制文字
                //绘制图片左边内容
                countLeft = mPaint.breakText(text, start, text.length, true, startOffset, null)
                canvas.drawText(text, start, start + countLeft, 0f, verticalOffset, mPaint)
                //绘制图片右边内容
                countRight = mPaint.breakText(text, start, text.length, true,
                    width - (startOffset + bitmapWidth), null)
                canvas.drawText(text, start +countLeft, start + countLeft + countRight, startOffset + bitmapWidth,
                    verticalOffset, mPaint)
                count = countLeft + countRight
            }
            verticalOffset += mPaint.fontSpacing
            start += count
        }
    }

这里通过Paint的getTextPaint方法获取绘制文字的FontMetrics,通过这个FontMetrics可以获取文本内容的ascent、descent、top和bottom值,即:

在这里插入图片描述

相关链接:
https://developer.android.com/reference/android/graphics/Paint.FontMetrics

start:每行绘制的起始位置,每次行绘制完成后要加上当前行的长度

verticalOffest:每行绘制的y轴偏移量,绘制第一行时,需要去除FontMetrics的top值,因为绘制内容是根据baseline来绘制,如果直接绘制时,会导致大部分内容被遮挡住。并且每行绘制完成时,在绘制下一行之前需要:

verticalOffset += mPaint.fontSpacing

fontSpacing是每行之间的间隔(API提供)

相关链接:
https://developer.android.com/reference/android/graphics/Paint
搜索getFontSpacing

当start起始值小于文本的总长度时循环绘制,每次循环时,通过:

        if (verticalOffset + mFontMetrics.bottom < IMAGE_PADDING
            || verticalOffset + mFontMetrics.top > IMAGE_PADDING + bitmapHeight) {

判断文字是否“触碰”Bitmap,可以反过来判断文字没有触碰的情况,主要2种情况:

1、文字的verticalOffest加上bottom值是否小于Bitmap的y轴偏移量(即IMAGE_PADDING值,这个自己随意设置)

2、文字的verticalOffest加上top值是否大于Bitmap的下边界的y值,如果是,那么已经超出Bitmap的区域了

以上2种情况是没有触碰的情况,那么就可以整行绘制:

            maxWidth = width.toFloat()
            count = mPaint.breakText(text, start, text.length, true, maxWidth, null)
            canvas.drawText(text, start, start + count, 0f, verticalOffset, mPaint)

通过Paint的breaTextF方法可以截取某段文字的长度,其中的maxWidth表示在这个长度下,text最多可以显示多少个文本,即返回值count,接着通过canvas.drawText绘制规定范围内的文本个数、

如果触碰了图片那么,需要对图片左右2边分别绘制内容

            countLeft = mPaint.breakText(text, start, text.length, true, startOffset, null)
            canvas.drawText(text, start, start + countLeft, 0f, verticalOffset, mPaint)
            //绘制图片右边内容
            countRight = mPaint.breakText(text, start, text.length, true,
                width - (startOffset + bitmapWidth), null)
            canvas.drawText(text, start +countLeft, start + countLeft + countRight, startOffset + bitmapWidth,
                verticalOffset, mPaint)
            count = countLeft + countRight

通过第一次breakText可以获取Bitmap左边的文本内容长度,最大范围长度为startOffest。进行绘制

通过第二次breakText可以获取Bitmap右边的文本内容长度,最大范围长度为:View的总宽度减去startOffest再减去Bitmap的宽度值。然后进行绘制,绘制的起始x左边为:startOffest加上Bitmap的宽度值

demo地址:https://download.csdn.net/download/weixin_45253393/46675766

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哒哒呵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值