概要
在绘制多行文本的时候,经常会有多行图片中间插入一张图片的需求。此时文字应该做到在图片之外的空白区域完成绘制,如图:
可以看到在绘制文字时,当遇到图片时应该跳过图片区域继续绘制
实现
绘制图片
第一步先绘制图片,确定图片的位置,比如将图片绘制在正中间,那么通过调用
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