Skip to content

Commit 5d015fb

Browse files
committed
add StaticLayout dynamic text support.
1 parent ef2ad7b commit 5d015fb

File tree

3 files changed

+124
-33
lines changed

3 files changed

+124
-33
lines changed

app/src/main/java/com/example/ponycui_home/svgaplayer/MainActivity.java

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,19 @@
1414
import android.graphics.RectF;
1515
import android.os.Build;
1616
import android.os.Bundle;
17+
import android.os.Handler;
1718
import android.support.annotation.Nullable;
1819
import android.support.v7.app.AppCompatActivity;
20+
import android.text.BoringLayout;
21+
import android.text.Layout;
22+
import android.text.Spannable;
23+
import android.text.SpannableString;
24+
import android.text.SpannableStringBuilder;
25+
import android.text.StaticLayout;
1926
import android.text.TextPaint;
27+
import android.text.TextUtils;
28+
import android.text.style.AbsoluteSizeSpan;
29+
import android.text.style.ForegroundColorSpan;
2030
import android.util.AttributeSet;
2131
import android.view.View;
2232

@@ -69,10 +79,10 @@ private void loadAnimation() {
6979
SVGAParser parser = new SVGAParser(this);
7080
resetDownloader(parser);
7181
try {
72-
parser.parse(new URL("https://github.com/yyued/SVGA-Samples/blob/master/angel.svga?raw=true"), new SVGAParser.ParseCompletion() {
82+
parser.parse(new URL("https://github.com/yyued/SVGA-Samples/blob/master/kingset.svga?raw=true"), new SVGAParser.ParseCompletion() {
7383
@Override
7484
public void onComplete(@NotNull SVGAVideoEntity videoItem) {
75-
SVGADrawable drawable = new SVGADrawable(videoItem);
85+
SVGADrawable drawable = new SVGADrawable(videoItem, requestDynamicItemWithSpannableText());
7686
testView.setImageDrawable(drawable);
7787
testView.startAnimation();
7888
}
@@ -86,6 +96,45 @@ public void onError() {
8696
}
8797
}
8898

99+
/**
100+
* 进行简单的文本替换
101+
* @return
102+
*/
103+
private SVGADynamicEntity requestDynamicItem() {
104+
SVGADynamicEntity dynamicEntity = new SVGADynamicEntity();
105+
TextPaint textPaint = new TextPaint();
106+
textPaint.setColor(Color.WHITE);
107+
textPaint.setTextSize(28);
108+
dynamicEntity.setDynamicText("Pony 送了一打风油精给主播", textPaint, "banner");
109+
return dynamicEntity;
110+
}
111+
112+
/**
113+
* 你可以设置富文本到 ImageKey 相关的元素上
114+
* 富文本是会自动换行的,不要设置过长的文本
115+
* @return
116+
*/
117+
private SVGADynamicEntity requestDynamicItemWithSpannableText() {
118+
final SVGADynamicEntity dynamicEntity = new SVGADynamicEntity();
119+
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("Pony 送了一打风油精给主播");
120+
spannableStringBuilder.setSpan(new ForegroundColorSpan(Color.YELLOW), 0, 4, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
121+
TextPaint textPaint = new TextPaint();
122+
textPaint.setColor(Color.WHITE);
123+
textPaint.setTextSize(28);
124+
dynamicEntity.setDynamicText(new StaticLayout(
125+
spannableStringBuilder,
126+
0,
127+
spannableStringBuilder.length(),
128+
textPaint,
129+
0,
130+
Layout.Alignment.ALIGN_CENTER,
131+
1.0f,
132+
0.0f,
133+
false
134+
), "banner");
135+
return dynamicEntity;
136+
}
137+
89138
/**
90139
* 设置下载器,这是一个可选的配置项。
91140
* @param parser

library/src/main/java/com/opensource/svgaplayer/SVGACanvasDrawer.kt

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.opensource.svgaplayer
22

33
import android.graphics.*
4+
import android.text.StaticLayout
45
import android.widget.ImageView
56

67

@@ -17,6 +18,7 @@ class SVGACanvasDrawer(videoItem: SVGAVideoEntity, val dynamicItem: SVGADynamicE
1718
private val sharedPath = Path()
1819
private val sharedPath2 = Path()
1920
private val sharedContentTransform = Matrix()
21+
private val textBitmapCache: HashMap<String, Bitmap> = hashMapOf()
2022

2123
override fun drawFrame(frameIndex: Int, scaleType: ImageView.ScaleType) {
2224
super.drawFrame(frameIndex, scaleType)
@@ -45,7 +47,8 @@ class SVGACanvasDrawer(videoItem: SVGAVideoEntity, val dynamicItem: SVGADynamicE
4547

4648
private fun drawImage(sprite: SVGADrawerSprite) {
4749
val canvas = this.canvas ?: return
48-
(dynamicItem.dynamicImage[sprite.imageKey] ?: videoItem.images[sprite.imageKey])?.let {
50+
val imageKey = sprite.imageKey ?: return
51+
(dynamicItem.dynamicImage[imageKey] ?: videoItem.images[imageKey])?.let {
4952
sharedPaint.reset()
5053
sharedPaint.isAntiAlias = videoItem.antiAlias
5154
sharedPaint.isFilterBitmap = videoItem.antiAlias
@@ -72,40 +75,64 @@ class SVGACanvasDrawer(videoItem: SVGAVideoEntity, val dynamicItem: SVGADynamicE
7275
}
7376

7477
private fun drawText(drawingBitmap: Bitmap, sprite: SVGADrawerSprite) {
78+
if (dynamicItem.isTextDirty) {
79+
this.textBitmapCache.clear()
80+
dynamicItem.isTextDirty = false
81+
}
7582
val canvas = this.canvas ?: return
76-
dynamicItem.dynamicText[sprite.imageKey]?.let { drawingText ->
77-
dynamicItem.dynamicTextPaint[sprite.imageKey]?.let { drawingTextPaint ->
78-
val textBitmap = Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888)
79-
val textCanvas = Canvas(textBitmap)
80-
drawingTextPaint.isAntiAlias = true
81-
val bounds = Rect()
82-
drawingTextPaint.getTextBounds(drawingText, 0, drawingText.length, bounds)
83-
val x = (drawingBitmap.width - bounds.width()) / 2.0
84-
val targetRectTop = 0
85-
val targetRectBottom = drawingBitmap.height
86-
val y = (targetRectBottom + targetRectTop - drawingTextPaint.fontMetrics.bottom - drawingTextPaint.fontMetrics.top) / 2
87-
textCanvas.drawText(drawingText, x.toFloat(), y, drawingTextPaint)
88-
89-
sharedPaint.reset()
90-
sharedPaint.isAntiAlias = videoItem.antiAlias
91-
if (sprite.frameEntity.maskPath != null) {
92-
val maskPath = sprite.frameEntity.maskPath ?: return@let
93-
canvas.save()
94-
canvas.concat(sharedContentTransform)
95-
canvas.clipRect(0, 0, drawingBitmap.width, drawingBitmap.height)
96-
val bitmapShader = BitmapShader(textBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
97-
sharedPaint.shader = bitmapShader
98-
sharedPath.reset()
99-
maskPath.buildPath(sharedPath)
100-
canvas.drawPath(sharedPath, sharedPaint)
101-
canvas.restore()
102-
}
103-
else {
104-
sharedPaint.isFilterBitmap = videoItem.antiAlias
105-
canvas.drawBitmap(textBitmap, sharedContentTransform, sharedPaint)
83+
val imageKey = sprite.imageKey ?: return
84+
var textBitmap: Bitmap? = null
85+
dynamicItem.dynamicText[imageKey]?.let { drawingText ->
86+
dynamicItem.dynamicTextPaint[imageKey]?.let { drawingTextPaint ->
87+
textBitmapCache[imageKey]?.let {
88+
textBitmap = it
89+
} ?: kotlin.run {
90+
textBitmap = Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888)
91+
val textCanvas = Canvas(textBitmap)
92+
drawingTextPaint.isAntiAlias = true
93+
val bounds = Rect()
94+
drawingTextPaint.getTextBounds(drawingText, 0, drawingText.length, bounds)
95+
val x = (drawingBitmap.width - bounds.width()) / 2.0
96+
val targetRectTop = 0
97+
val targetRectBottom = drawingBitmap.height
98+
val y = (targetRectBottom + targetRectTop - drawingTextPaint.fontMetrics.bottom - drawingTextPaint.fontMetrics.top) / 2
99+
textCanvas.drawText(drawingText, x.toFloat(), y, drawingTextPaint)
100+
textBitmapCache.put(imageKey, textBitmap as Bitmap)
106101
}
107102
}
108103
}
104+
dynamicItem.dynamicLayoutText[imageKey]?.let {
105+
textBitmapCache[imageKey]?.let {
106+
textBitmap = it
107+
} ?: kotlin.run {
108+
var layout = StaticLayout(it.text, 0, it.text.length, it.paint, drawingBitmap.width, it.alignment, it.spacingMultiplier, it.spacingAdd, false)
109+
textBitmap = Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888)
110+
val textCanvas = Canvas(textBitmap)
111+
textCanvas.translate(0f, ((drawingBitmap.height - layout.height) / 2).toFloat())
112+
layout.draw(textCanvas)
113+
textBitmapCache.put(imageKey, textBitmap as Bitmap)
114+
}
115+
}
116+
textBitmap?.let { textBitmap ->
117+
sharedPaint.reset()
118+
sharedPaint.isAntiAlias = videoItem.antiAlias
119+
if (sprite.frameEntity.maskPath != null) {
120+
val maskPath = sprite.frameEntity.maskPath ?: return@let
121+
canvas.save()
122+
canvas.concat(sharedContentTransform)
123+
canvas.clipRect(0, 0, drawingBitmap.width, drawingBitmap.height)
124+
val bitmapShader = BitmapShader(textBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
125+
sharedPaint.shader = bitmapShader
126+
sharedPath.reset()
127+
maskPath.buildPath(sharedPath)
128+
canvas.drawPath(sharedPath, sharedPaint)
129+
canvas.restore()
130+
}
131+
else {
132+
sharedPaint.isFilterBitmap = videoItem.antiAlias
133+
canvas.drawBitmap(textBitmap, sharedContentTransform, sharedPaint)
134+
}
135+
}
109136
}
110137

111138
private fun drawShape(sprite: SVGADrawerSprite) {

library/src/main/java/com/opensource/svgaplayer/SVGADynamicEntity.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package com.opensource.svgaplayer
22

33
import android.graphics.Bitmap
44
import android.graphics.BitmapFactory
5+
import android.text.Layout
6+
import android.text.SpannableString
7+
import android.text.StaticLayout
58
import android.text.TextPaint
69
import java.io.ByteArrayInputStream
710
import java.io.ByteArrayOutputStream
@@ -20,6 +23,10 @@ class SVGADynamicEntity {
2023

2124
var dynamicTextPaint: HashMap<String, TextPaint> = hashMapOf()
2225

26+
var dynamicLayoutText: HashMap<String, StaticLayout> = hashMapOf()
27+
28+
internal var isTextDirty = false
29+
2330
fun setDynamicImage(bitmap: Bitmap, forKey: String) {
2431
this.dynamicImage.put(forKey, bitmap)
2532
}
@@ -44,14 +51,22 @@ class SVGADynamicEntity {
4451
}
4552

4653
fun setDynamicText(text: String, textPaint: TextPaint, forKey: String) {
54+
this.isTextDirty = true
4755
this.dynamicText.put(forKey, text)
4856
this.dynamicTextPaint.put(forKey, textPaint)
4957
}
5058

59+
fun setDynamicText(layoutText: StaticLayout, forKey: String) {
60+
this.isTextDirty = true
61+
this.dynamicLayoutText.put(forKey, layoutText)
62+
}
63+
5164
fun clearDynamicObjects() {
65+
this.isTextDirty = true
5266
this.dynamicImage.clear()
5367
this.dynamicText.clear()
5468
this.dynamicTextPaint.clear()
69+
this.dynamicLayoutText.clear()
5570
}
5671

5772
}

0 commit comments

Comments
 (0)