当前位置:网站首页>让你的 Lottie 支持文字区域内自动换行
让你的 Lottie 支持文字区域内自动换行
2022-08-01 03:08:00 【俺不理解】
让你的 Lottie 支持文字区域内自动换行
老规矩,先说背景和结果,再说解决过程。如果和你遇到的问题一样,再看后边的解决过程吧
前言
最近遇到一个棘手的问题,设计同学给了一个lottie 动画,动画内有三个字段可以动态替换,如图:
但是其中的 inputTag
在替换时,出现了一些问题
问题一 :长文本不会自动换行
问题二:多行文本未在区域内展示
发现其实设计已经设置了 input 图层的文字区域范围,理论上来讲应该可以自动换行才对,于是我在lottie 官网预览了一下,发现却是正常的
经过一番苦战,最后解决了这个问题,如果和你遇到的问题,就接着往后看吧、如果你需要直接的解决方案,可以直接看二、解决问题
一、分析问题
根据上边看到的问题一二,不难看出
- 没有解析到 textLayer 的正确显示范围
- 同时也没有再显示范围内自动换行
分析 json 源数据
可以看到,设计妹子给的json是没问题的,的确有包含图层尺寸以及偏移量等数据,那么真相只有一个 - lottie 解析库并没有解析处理 sz 、 ps 值
查看源码
直接定位到 TextLayer 对文字的绘制部分
private void drawTextWithFont(
DocumentData documentData, Font font, Matrix parentMatrix, Canvas canvas) {
... // 上边是一些值的读取处理,我们不管他
// 处理多行及绘制
// Split full text in multiple lines
List<String> textLines = getTextLines(text);
int textLineCount = textLines.size();
for (int l = 0; l < textLineCount; l++) {
String textLine = textLines.get(l);
float textLineWidth = strokePaint.measureText(textLine);
// Apply horizontal justification
applyJustification(documentData.justification, canvas, textLineWidth);
// Center text vertically
float multilineTranslateY = (textLineCount - 1) * lineHeight / 2;
float translateY = l * lineHeight - multilineTranslateY;
canvas.translate(0, translateY);
// Draw each line
drawFontTextLine(textLine, documentData, canvas, parentScale);
// Reset canvas
canvas.setMatrix(parentMatrix);
}
}
getTextLines() ?
private List<String> getTextLines(String text) {
// Split full text by carriage return character
String formattedText = text.replaceAll("\r\n", "\r")
.replaceAll("\n", "\r");
String[] textLinesArray = formattedText.split("\r");
return Arrays.asList(textLinesArray);
}
这样也叫 获取多行文本嘛?怪不得我的长文本只有一行,原来是看不到换行符不回头啊。等等,再看看 json 的解析,会不会压根没有解析sz 、 ps两个字段的值
查看 document 生成源码 DocumentDataParser
public class DocumentDataParser implements ValueParser<DocumentData> {
public static final DocumentDataParser INSTANCE = new DocumentDataParser();
// 解析的字段名
private static final JsonReader.Options NAMES = JsonReader.Options.of(
"t",
"f",
"s",
"j",
"tr",
"lh",
"ls",
"fc",
"sc",
"sw",
"of"
);
private DocumentDataParser() {
}
@Override
public DocumentData parse(JsonReader reader, float scale) throws IOException {
String text = null;
String fontName = null;
float size = 0f;
Justification justification = Justification.CENTER;
int tracking = 0;
float lineHeight = 0f;
float baselineShift = 0f;
int fillColor = 0;
int strokeColor = 0;
float strokeWidth = 0f;
boolean strokeOverFill = true;
reader.beginObject();
while (reader.hasNext()) {
switch (reader.selectName(NAMES)) {
... // 都是已有的跟多行无关的
default:
reader.skipName();
reader.skipValue();
}
}
reader.endObject();
return new DocumentData(text, fontName, size, justification, tracking, lineHeight,
baselineShift, fillColor, strokeColor, strokeWidth, strokeOverFill);
}
}
可以看到,果然不出我们所料,压根没有解析 sz、ps 字段值,更无需谈处理的事儿了
二、解决问题
经过我们缜密的分析,那么我们需要做的事就比较明确了,大概包括以下几个步骤:
- 补充 sz、ps 字段的解析
- 重写 getTextLines() 方法,正确获取多行文本
- 重写 drawTextWithFont() 方法,正确计算 align 偏移
OK,那么逐个模块来解决问题:
1. 补充 sz、ps 字段的解析
public class DocumentDataParser implements ValueParser<DocumentData> {
public static final DocumentDataParser INSTANCE = new DocumentDataParser();
// 解析的字段名
private static final JsonReader.Options NAMES = JsonReader.Options.of(
"t",
"f",
"s",
"j",
"tr",
"lh",
"ls",
"fc",
"sc",
"sw",
"of",
// 补充两个字段名
"sz",
"ps"
);
private DocumentDataParser() {
}
@Override
public DocumentData parse(JsonReader reader, float scale) throws IOException {
String text = null;
String fontName = null;
float size = 0f;
Justification justification = Justification.CENTER;
int tracking = 0;
float lineHeight = 0f;
float baselineShift = 0f;
int fillColor = 0;
int strokeColor = 0;
float strokeWidth = 0f;
boolean strokeOverFill = true;
// 补充 尺寸
double[] viewSize = new double[] {
-1, -1};
// 补充 偏移
double[] offset = new double[2];
reader.beginObject();
while (reader.hasNext()) {
switch (reader.selectName(NAMES)) {
... // 都是已有的跟多行无关的
// 补充对这两个字段的解析
case 11:
JsonUtils.jsonToArray(reader, viewSize);
break;
case 12:
JsonUtils.jsonToArray(reader, offset);
break;
default:
reader.skipName();
reader.skipValue();
}
}
reader.endObject();
// 加入构造方法
return new DocumentData(text, fontName, size, justification, tracking, lineHeight,
baselineShift, fillColor, strokeColor, strokeWidth, strokeOverFill, viewSize, offset);
}
}
- 其中 JsonUtils 中的方法为
static void jsonToArray(JsonReader reader, double[] array) {
try {
reader.beginArray();
for (int i = 0; i < array.length; i++) {
array[i] = reader.nextDouble();
}
while (reader.hasNext()) {
reader.skipValue();
}
reader.endArray();
} catch (IOException e) {
e.printStackTrace();
}
}
- 在 DocumentData 中补充这两个字段
public class DocumentData {
... 其他属性
public final float[] viewSize;
public final float[] offset;
public DocumentData(String text, String fontName, float size, Justification justification, int tracking,
float lineHeight, float baselineShift, @ColorInt int color, @ColorInt int strokeColor,
float strokeWidth, boolean strokeOverFill, double[] viewSize, double[] offset) {
... 其他属性
// 存储 viewSize,因为 json 中的 为 dp 值,这里我们要转为 像素值
this.viewSize = new float[]{
(float) (viewSize[0] * Utils.dpScale()), (float) (viewSize[1] * Utils.dpScale())};
// 存储 偏移
this.offset = new float[]{
(float) (offset[0]), (float) (offset[1])};
}
...
}
2. 重写 getTextLines() 方法,正确获取多行文本
为了方便我们实现长文本的多行实现,这里使用 StaticLayout 来做文本多行测量
- 在 TextLayer中补充方法 obtainStaticLayout()
private StaticLayout obtainStaticLayout(DocumentData documentData, String text) {
double maxWidth = documentData.viewSize[0];
TextPaint paint = new TextPaint(fillPaint);
return Utils.getStaticLayout(text, paint, ((int) maxWidth), documentData);
}
- 其中 Utils.getStaticLayout 为
public static StaticLayout getStaticLayout(String text, TextPaint paint, int width, DocumentData documentData) {
if (width < 0) {
return null;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return StaticLayout.Builder.obtain(text, 0, text.length(), paint, width)
.setLineSpacing(0, documentData.lineHeight / documentData.size)
.build();
} else {
return new StaticLayout(
text, paint, width,
Layout.Alignment.ALIGN_NORMAL,
documentData.lineHeight / documentData.size,
0f,
true);
}
}
然后重写 getTextLines方法
private List<String> getTextLines(String text, DocumentData documentData, StaticLayout sl) {
if (documentData.viewSize[0] < 0) {
// Split full text by carriage return character
// 未设置尺寸的还是沿用以前的实现方案
String formattedText = text.replaceAll("\r\n", "\r")
.replaceAll("\n", "\r");
String[] textLinesArray = formattedText.split("\r");
return Arrays.asList(textLinesArray);
}
double maxHeight = documentData.viewSize[1];
List<String> lines = new LinkedList<>();
int line = 0;
// 把每行可显示的文字逐行获取出来
while (line < sl.getLineCount() && sl.getLineTop(line) < maxHeight) {
int lineStart = sl.getLineStart(line);
int lineEnd = sl.getLineEnd(line);
lines.add(text.substring(lineStart, lineEnd));
line++;
}
return lines;
}
3. 重写 drawTextWithFont() 方法,正确计算 align 偏移
最后,重写 drawTextWithFont 中的部分代码,来使我们的改动生效
private void drawTextWithFont(
DocumentData documentData, Font font, Matrix parentMatrix, Canvas canvas) {
... // 上边是一些值的读取处理,我们不管他
// 处理多行及绘制
// Split full text in multiple lines
StaticLayout sl = obtainStaticLayout(documentData, text);
List<String> textLines = getTextLines(text, documentData, sl);
int textLineCount = textLines.size();
// 计算实际的首行位置
float textLayerHeight = getRealTextLayerHeight(sl, documentData, lineHeight, textLineCount);
for (int l = 0; l < textLineCount; l++) {
String textLine = textLines.get(l);
float textLineWidth = strokePaint.measureText(textLine);
canvas.save();
// Apply horizontal justification
applyJustification(documentData, canvas, textLineWidth);
// 计算本行实际的位置
float translateY = l * lineHeight - textLayerHeight / 2;
// 让 offset 也加入计算
canvas.translate(-documentData.offset[0], translateY - documentData.offset[1]);
// Draw each line
drawFontTextLine(textLine, documentData, canvas, parentScale);
// Reset canvas
canvas.restore();
}
}
/** * 计算 TextLayer 实际的显示高度 */
private float getRealTextLayerHeight(StaticLayout sl, DocumentData documentData,
float lineHeight, int textLineCount) {
if (sl == null || documentData.viewSize[1] <= 0) {
return (textLineCount - 1) * lineHeight;
} else {
int line = 1;
float height;
while ((height = line * lineHeight) < documentData.viewSize[1]) {
line++;
}
return height;
}
}
drawTextGlyphs 方法也同样调用了 getTextLines 方法,也参考 drawTextWithFont 做同样的改动即可
三、解决结果
到此为止就完成了对换行逻辑的补充,运行起来查看一下改动结果如何
可以看到与官网对 这个 lottie 的预览效果是一致的。OK 大功告成~
PS: 这个问题真的是足足困扰了我前后有三天!!!一开始不知道 lottie 可以动态替换文本,尝试自己做这个复杂动画。后来发现可以用lottie,结果显示有问题。我以为是 设计那边的问题,后来发现 iOS 可以。定位到是 lottie 解析库的锅,才开始自己改,又改了好久……
结果是好的! 加油!
边栏推荐
- <JDBC> 批量插入 的四种实现方式:你真的get到了吗?
- Introduction to machine learning how to?
- Lua introductory case of actual combat 1234 custom function and the standard library function
- 对无限debugger的一种处理方式
- Inheritance Considerations
- 大佬们,MySQL cdc source在增量过程中回收 replication slave 和 r
- 软件测试基础理论知识—用例篇
- 简单易用的任务队列-beanstalkd
- Soft Exam Senior System Architect Series: Basic Knowledge of System Development
- The device node structure is converted into a platform_device structure
猜你喜欢
初出茅庐的小李第114篇博客项目笔记之机智云智能浇花器实战(3)-基础Demo实现
普通用户无法访问hgfs目录
IDEA无法识别module(module右下角没有蓝色小方块)
The bigger and bigger the project is, I split it like this
MYSQL query interception optimization analysis
Introduction to machine learning how to?
win10 fixed local IP
项目越写越大,我是这样做拆分的
[Search topic] After reading the inevitable BFS solution to the shortest path problem
You need to know the TCP wave four times
随机推荐
【数据分析】基于matlab GUI学生成绩管理系统【含Matlab源码 1981期】
Handwritten binary search tree and test
【入门教程】Rollup模块打包器整合
Dart 命名参数语法
移动端页面秒开优化总结
【分层强化学习】HIRO:Data-Efficient Hierarchical Reinforcement Learning
How is the tree structure of the device tree reflected?
Unity在BuildIn渲染管线下实现PlanarReflection的初级方法
Ordinary users cannot access HGFS directory
ARM cross compilation
软考高级系统架构设计师系列之:信息系统基础知识
【元胞自动机】基于matlab界面聚合元胞自动机模拟【含Matlab源码 2004期】
Device tree - conversion from dtb format to struct device node structure
One service layer needs to call the other two service layers to obtain data and assemble it into the final data. The data is all lists. How to design the cache?
[cellular automata] based on matlab interface aggregation cellular automata simulation [including Matlab source code 2004]
MYSQL Index Analysis
纽约大学等 | TM-Vec:用于快速同源检测和比对的模版建模向量
Guys, MySQL cdc source recycles replication slave and r in incremental process
win10 fixed local IP
win10 固定本机IP