正常我们是直接通过设置这块来根据设计师提供的key往里头塞文字的,这块区域也是设计师设置的
SVGADynamicEntity().setDynamicText(
new StaticLayout(
text,
0,
text.length(),
textPaint,
0,
alignment,
0.0f,
0.0f,
false
),
"text"
)
然后我们想要它适配RTL的时候,我当时第一想法是,那不是直接控件X轴改变方向不就完了嘛
SVGAImageView svg = new SVGAImageView(context);
svg.setScaleX(-1);
额,但是实际情况你会发现它内部的文字它也是倒过来的,嗯,倒过来的,通过查看源码,我们发现其实它这块设置的文字最终是会给你转个TextBitmap进行遍历绘制的
override fun drawFrame(canvas: Canvas, frameIndex: Int, scaleType: ImageView.ScaleType) {
super.drawFrame(canvas, frameIndex, scaleType)
...
sprites.forEachIndexed { index, svgaDrawerSprite ->
// Save matte sprite
svgaDrawerSprite.imageKey?.let {
/// No matte layer included or VERSION Unsopport matte
if (!hasMatteLayer || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// Normal sprite
drawSprite(svgaDrawerSprite, canvas, frameIndex)
// Continue
return@forEachIndexed
}
}
...
releaseFrameSprites(sprites)
}
这块drawSprite就是根据imageKey,也就是设计师给我们准备的填充区域对应的key,对这块进行轮询绘制
private fun drawSprite(sprite: SVGADrawerSprite, canvas: Canvas, frameIndex: Int) {
drawImage(sprite, canvas)
drawShape(sprite, canvas)
drawDynamic(sprite, canvas, frameIndex)
}
其中的文字绘制就是那个drawImage嗯,就是它本质上就是给你绘制成bitmap了
private fun drawImage(sprite: SVGADrawerSprite, canvas: Canvas) {
val imageKey = sprite.imageKey ?: return
val isHidden = dynamicItem.dynamicHidden[imageKey] == true
if (isHidden) {
return
}
val bitmapKey = if (imageKey.endsWith(".matte")) imageKey.substring(0, imageKey.length - 6) else imageKey
val drawingBitmap = (dynamicItem.dynamicImage[bitmapKey] ?: videoItem.imageMap[bitmapKey])
?: return
val frameMatrix = shareFrameMatrix(sprite.frameEntity.transform)
val paint = this.sharedValues.sharedPaint()
paint.isAntiAlias = videoItem.antiAlias
paint.isFilterBitmap = videoItem.antiAlias
paint.alpha = (sprite.frameEntity.alpha * 255).toInt()
if (sprite.frameEntity.maskPath != null) {
val maskPath = sprite.frameEntity.maskPath ?: return
canvas.save()
val path = this.sharedValues.sharedPath()
maskPath.buildPath(path)
path.transform(frameMatrix)
canvas.clipPath(path)
frameMatrix.preScale((sprite.frameEntity.layout.width / drawingBitmap.width).toFloat(), (sprite.frameEntity.layout.height / drawingBitmap.height).toFloat())
if (!drawingBitmap.isRecycled) {
canvas.drawBitmap(drawingBitmap, frameMatrix, paint)
}
canvas.restore()
} else {
frameMatrix.preScale((sprite.frameEntity.layout.width / drawingBitmap.width).toFloat(), (sprite.frameEntity.layout.height / drawingBitmap.height).toFloat())
if (!drawingBitmap.isRecycled) {
canvas.drawBitmap(drawingBitmap, frameMatrix, paint)
}
}
dynamicItem.dynamicIClickArea.let {
it.get(imageKey)?.let { listener ->
val matrixArray = floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f)
frameMatrix.getValues(matrixArray)
listener.onResponseArea(imageKey, matrixArray[2].toInt()
, matrixArray[5].toInt()
, (drawingBitmap.width * matrixArray[0] + matrixArray[2]).toInt()
, (drawingBitmap.height * matrixArray[4] + matrixArray[5]).toInt())
}
}
drawTextOnBitmap(canvas, drawingBitmap, sprite, frameMatrix)
}
drawTextOnBitmap,重点关注这个,然后我们发现它最终会生成一个
private fun drawTextOnBitmap(canvas: Canvas, drawingBitmap: Bitmap, sprite: SVGADrawerSprite, frameMatrix: Matrix) {
if (dynamicItem.isTextDirty) {
this.drawTextCache.clear()
dynamicItem.isTextDirty = false
}
val imageKey = sprite.imageKey ?: return
var textBitmap: Bitmap? = null
dynamicItem.dynamicText[imageKey]?.let { drawingText ->
dynamicItem.dynamicTextPaint[imageKey]?.let { drawingTextPaint ->
drawTextCache[imageKey]?.let {
textBitmap = it
} ?: kotlin.run {
textBitmap = Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888)
val drawRect = Rect(0, 0, drawingBitmap.width, drawingBitmap.height)
val textCanvas = Canvas(textBitmap)
drawingTextPaint.isAntiAlias = true
val fontMetrics = drawingTextPaint.getFontMetrics();
val top = fontMetrics.top
val bottom = fontMetrics.bottom
val baseLineY = drawRect.centerY() - top / 2 - bottom / 2
textCanvas.drawText(drawingText, drawRect.centerX().toFloat(), baseLineY, drawingTextPaint);
drawTextCache.put(imageKey, textBitmap as Bitmap)
}
}
}
dynamicItem.dynamicBoringLayoutText[imageKey]?.let {
drawTextCache[imageKey]?.let {
textBitmap = it
} ?: kotlin.run {
it.paint.isAntiAlias = true
textBitmap = Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888)
val textCanvas = Canvas(textBitmap)
textCanvas.translate(0f, ((drawingBitmap.height - it.height) / 2).toFloat())
it.draw(textCanvas)
drawTextCache.put(imageKey, textBitmap as Bitmap)
}
}
dynamicItem.dynamicStaticLayoutText[imageKey]?.let {
drawTextCache[imageKey]?.let {
textBitmap = it
} ?: kotlin.run {
it.paint.isAntiAlias = true
var layout = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
var lineMax = try {
val field = StaticLayout::class.java.getDeclaredField("mMaximumVisibleLineCount")
field.isAccessible = true
field.getInt(it)
} catch (e: Exception) {
Int.MAX_VALUE
}
StaticLayout.Builder
.obtain(it.text, 0, it.text.length, it.paint, drawingBitmap.width)
.setAlignment(it.alignment)
.setMaxLines(lineMax)
.setEllipsize(TextUtils.TruncateAt.END)
.build()
} else {
StaticLayout(it.text, 0, it.text.length, it.paint, drawingBitmap.width, it.alignment, it.spacingMultiplier, it.spacingAdd, false)
}
textBitmap = Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888)
val textCanvas = Canvas(textBitmap)
textCanvas.translate(0f, ((drawingBitmap.height - layout.height) / 2).toFloat())
layout.draw(textCanvas)
drawTextCache.put(imageKey, textBitmap as Bitmap)
}
}
textBitmap?.let { textBitmap ->
val paint = this.sharedValues.sharedPaint()
paint.isAntiAlias = videoItem.antiAlias
paint.alpha = (sprite.frameEntity.alpha * 255).toInt()
if (sprite.frameEntity.maskPath != null) {
val maskPath = sprite.frameEntity.maskPath ?: return@let
canvas.save()
canvas.concat(frameMatrix)
canvas.clipRect(0, 0, drawingBitmap.width, drawingBitmap.height)
val bitmapShader = BitmapShader(textBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
paint.shader = bitmapShader
val path = this.sharedValues.sharedPath()
maskPath.buildPath(path)
canvas.drawPath(path, paint)
canvas.restore()
} else {
paint.isFilterBitmap = videoItem.antiAlias
val w = textBitmap.width
val h = textBitmap.height
val matrix = Matrix()
matrix.postScale(-1f, 1f) // 水平镜像翻转
val b= Bitmap.createBitmap(textBitmap, 0, 0, w, h, matrix, true)
canvas.drawBitmap(b, frameMatrix, paint)
}
}
}
textBitmap,然后我们对这个bitmap进行再次翻转就好了,翻转代码已经写里头了,嗯,就这吧--!代码解释的可能不太好,但是总归是功能是实现了--!