Android Canvas绘制不同属性多行文本,如何避免位置重叠?
解决Canvas多行文本下方绘制不同样式文本的重叠问题
现有Canvas扩展函数drawMultilineText用于绘制多行文本,手动指定y坐标绘制下方文本时,会因原文本长度变化导致重叠,可通过以下两种方案解决:
方案1:让drawMultilineText返回文本总高度
修改原绘制函数,返回生成的StaticLayout总高度,以此计算下一段文本的起始y坐标:
fun Canvas.drawMultilineText( text: CharSequence, textPaint: TextPaint, width: Int, x: Float, y: Float, start: Int = 0, end: Int = text.length, alignment: Layout.Alignment = Layout.Alignment.ALIGN_NORMAL, spacingMult: Float = 1f, spacingAdd: Float = 0f, includePad: Boolean = true, ellipsizedWidth: Int = width, ellipsize: TextUtils.TruncateAt? = null ): Int { val cacheKey = "$text-$start-$end-$textPaint-$width-$alignment-" + "$spacingMult-$spacingAdd-$includePad-$ellipsizedWidth-$ellipsize" val staticLayout = StaticLayoutCache[cacheKey] ?: StaticLayout.Builder.obtain(text, start, end, textPaint, width) .setAlignment(alignment) .setLineSpacing(spacingAdd, spacingMult) .setIncludePad(includePad) .setEllipsizedWidth(ellipsizedWidth) .setEllipsize(ellipsize) .build() staticLayout.draw(this, x, y) return staticLayout.height } private fun StaticLayout.draw(canvas: Canvas, x: Float, y: Float) { canvas.withTranslation(x, y) { draw(this) } } private object StaticLayoutCache { private const val MAX_SIZE = 50 private val cache = lruCache<String, StaticLayout>(MAX_SIZE) operator fun set(key: String, staticLayout: StaticLayout) { cache.put(key, staticLayout) } operator fun get(key: String): StaticLayout? { return cache[key] } }
使用示例
val textPaint = TextPaint().apply { textSize = 10f } // 绘制第一段文本并获取高度 val firstTextHeight = canvas.drawMultilineText(myString, textPaint, canvas.width, 100f, 100f) val textPaint2 = TextPaint().apply { textSize = 5f } // 第二段文本起始y = 第一段起始y + 第一段高度 + 自定义间距 val secondTextY = 100f + firstTextHeight + 10f canvas.drawMultilineText("This is text underneath myString", textPaint2, canvas.width, 100f, secondTextY)
方案2:单独实现文本高度计算函数
若不想修改原绘制函数,可新增高度计算函数,提前获取文本高度再确定绘制位置:
fun calculateMultilineTextHeight( text: CharSequence, textPaint: TextPaint, width: Int, start: Int = 0, end: Int = text.length, alignment: Layout.Alignment = Layout.Alignment.ALIGN_NORMAL, spacingMult: Float = 1f, spacingAdd: Float = 0f, includePad: Boolean = true, ellipsizedWidth: Int = width, ellipsize: TextUtils.TruncateAt? = null ): Int { val cacheKey = "$text-$start-$end-$textPaint-$width-$alignment-" + "$spacingMult-$spacingAdd-$includePad-$ellipsizedWidth-$ellipsize" val staticLayout = StaticLayoutCache[cacheKey] ?: StaticLayout.Builder.obtain(text, start, end, textPaint, width) .setAlignment(alignment) .setLineSpacing(spacingAdd, spacingMult) .setIncludePad(includePad) .setEllipsizedWidth(ellipsizedWidth) .setEllipsize(ellipsize) .build() if (StaticLayoutCache[cacheKey] == null) { StaticLayoutCache[cacheKey] = staticLayout } return staticLayout.height }
使用示例
val textPaint = TextPaint().apply { textSize = 10f } // 提前计算第一段文本高度 val firstTextHeight = calculateMultilineTextHeight(myString, textPaint, canvas.width) // 绘制第一段文本 canvas.drawMultilineText(myString, textPaint, canvas.width, 100f, 100f) val textPaint2 = TextPaint().apply { textSize = 5f } val secondTextY = 100f + firstTextHeight + 10f canvas.drawMultilineText("This is text underneath myString", textPaint2, canvas.width, 100f, secondTextY)
两种方案均利用StaticLayout的高度属性,确保下一段文本自动排在前一段下方,避免内容重叠。
内容的提问来源于stack exchange,提问作者user19729496
相关产品推荐
相关产品推荐

