overflowWidget: TextOverflowWidget(
align: TextOverflowAlign.center,
child: Container(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'\u2026 ',
style: TextStyle(height: 1),
),
InkWell(
child: Image.asset(
'assets/candies.png',
width: 20,
height: 20,
),
onTap: () {
launch('https://github.com/fluttercandies/extended_text');
},
)
],
),
),
),
);
![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1eb79f3c05fa441ba3534510a634e91e~tplv-k3u1fbpfcp-watermark.image)
无法指定文本溢出(省略号)的位置
----------------
[无法指定文本溢出(省略号)的位置](
),比如下面两种常见场景。
1. 省略号在中间
![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e4197637ce364aeaaad6a536ee775153~tplv-k3u1fbpfcp-watermark.image)
2. 省略号在前面
![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8feebdf5481d439c82984a00d2220d32~tplv-k3u1fbpfcp-watermark.image)
`Flutter` 不支持,那么下面我们看看 `原生` 以及 `web` 端是否支持。(如有不对,望指正。)
| 平台 | 开头 | 中间 | 结尾 |
| --- | --- | --- | --- |
| android | `android:ellipsize = "start"` | `android:ellipsize = "middle"` | `android:ellipsize = "end"` |
| Ios | `NSLineBreakByTruncatingHead` | `NSLineBreakByTruncatingMiddle` | `NSLineBreakByTruncatingTail` |
| web | `text-overflow: ellipsis clip` | 不支持 | `text-overflow: clip ellipsis` |
| flutter | 不支持 | 不支持 | `TextOverflow.ellipsis` |
注意的是 `android` 只有在一行的情况才有效,需要设置 `android:singleline = "true"`;`web` 不支持中间;Ios 是其中支持最好的。
可以看到,`原生` 以及 `web` 端对该功能的支持比 `Flutter` 好太多了。那么我们 `Flutter` 就没办法了吗?当然不是,之前解决 [文本溢出(省略号)没法自定义](
) ,其原理完全可以应用在这个功能上面,以下为最终效果图。
![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/36c3d10e05904d0083759daeba95745e~tplv-k3u1fbpfcp-watermark.image)
### 原理
#### performLayout
在 [performLayout](
) 方法中,`TextPainter` 经过 `layout` 就可以知道当前文本是否有溢出。
final Size textSize = _textPainter.size;
final bool textDidExceedMaxLines = _textPainter.didExceedMaxLines;
size = constraints.constrain(textSize);
final bool didOverflowHeight = size.height < textSize.height || textDidExceedMaxLines;
final bool didOverflowWidth = size.width < textSize.width;
// TODO(abarth): We're only measuring the sizes of the line boxes here. If
// the glyphs draw outside the line boxes, we might think that there isn't
// visual overflow when there actually is visual overflow. This can become
// a problem if we start having horizontal overflow and introduce a clip
// that affects the actual (but undetected) vertical overflow.
final bool hasVisualOverflow = didOverflowWidth || didOverflowHeight;
#### 计算不溢出的情况
这个还是要依靠 `TextPainter`,通过[二分查找](
),找到 `不溢出的` 和 `溢出` 临界点 `X`。
| 开头 | 中间 | 结尾 |
| --- | --- | --- |
| `[0,X]` | `[m,X]`,`m` 为中间文本的索引位置 | 文本不需要改变,无需计算 |
该 `Range` 范围内的文本将不会显示。
#### 处理文本
比如 `abcdef`, 我们找到的 `Range` 为 `[2,3]` ,即最终显示 `ab...ef`。考虑支持选择复制,所以我们这里不能简单丢掉 `cd`。这里利用到 [SpecialTextSpan](
) 的一个功能。
> 你见到的并不是真实的
SpecialTextSpan(
‘abef’,
actualText: ‘abcdef’,
);
#### 溢出内容绘制
| 开头 | 中间 | 结尾 |
| --- | --- | --- |
| 画在文本开始位置 | 画在文本中间位置 | 画在文本结尾位置 |
实际上,我们要做的时候,在这些位置绘制溢出内容,并且遮住下面的文字。为了达到这个目的,
1. 根据 `Range` 获得 `TextSelection`,利用 `TextPainter.getBoxesForSelection` 获取该段遮蔽文字的区域 `overflowRect` 。
2. 用 `overflowRect` 跟溢出内容的大小取并集。
3. 移动 `Range` 继续取并集, 直到 `overflowRect` 跟溢出内容的大小完全包容。(确保溢出内容显示以及遮蔽文字不会露出来)
代码在[\_setOverflowRect](
) 方法中。
#### 遮蔽文字
在之前的版本中,由于 `Paint()..blendMode = BlendMode.clear` 无法对文字进行清除,只好利用 `canvas.clipPath(path)` 将 `overflowRect` 部分的文字裁剪掉。现在官方已经修复掉这个问题,现在你可以这样做。(实际中小伙伴经常提起不知道怎么做摄像头的蒙层,其实你可以利用这个办法,将蒙层挖一个孔。)
if (_overflowRect != null) {
context.canvas.saveLayer(_offset & size, Paint());
}
// 绘制文字
_paint(context, offset);
if (_overflowRect != null) {
// 清除掉这块区域的文字
context.canvas.drawRect(
_overflowRect!.shift(_offset), Paint()…blendMode = BlendMode.clear);
context.canvas.restore();
}
#### 更多的细节
其实上面只是大概讲了一下思路,其中是包含很多计算的,感兴趣的童鞋,可以看看这个[文件](
).
### 缺点
* 利用 `TextPainter` 的一些方法来实现最终计算,`TextPainter` 其实也是有很多限制的,比如 对 `WidgetSpan` 的计算错误,虽然已经利用一些 `workaround` 来规避了。但是官方引擎的每次改动都可能对结果有影响,反正是被坑过,懂的都懂。
* 对面 `middle` 这种情况,文字每一行行高如果相差太大的话,极可能会导致计算失败,`TextPainter` 并没有提供一个 `api`,来直接告诉我们这些信息。
换行和溢出不友好
--------
[换行和溢出不友好](
), 比如在 `Flutter` 中显示下面一段话。
`您的糖果服务已续期,服务订单号: 12345678910987654321110。`
| 现实 | 期望 |
| --- | --- |
| | |
| ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8836daabfe1f4badbcd77304fc78da19~tplv-k3u1fbpfcp-watermark.image) | ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7f4b15ad986b4223bae6cc1b3b3615ad~tplv-k3u1fbpfcp-watermark.image) |
| ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e2953bbc280c44af99d070168dc12467~tplv-k3u1fbpfcp-watermark.image) | ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a66c1426a5224c17ad27516f8f2dddc0~tplv-k3u1fbpfcp-watermark.image) |
| | |
这个问题也不是 `Flutter` 独有的,关系到对 `word` 排版的处理。目前的解决方案,是向文本中插入 [zero-width space](
)(`\u{200B}`).
Characters(‘abc’).join(’\u{200B}’);
当然你可以直接将 `ExtendedText.joinZeroWidthSpace` 设置为 `true`。
ExtendedText(
joinZeroWidthSpace: true,
)
或者你也可以利用 [ExtendedTextLibrary](
) 库中的扩展方法。
1. 文本
String input=‘abc’.joinChar(’\u{200B}’);
2. InlineSpan
### 尾声
如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
![](https://img-blog.csdnimg.cn/img_convert/cffb132bb31a00f5e7618cb6cbd9eaff.png)
PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
![](https://img-blog.csdnimg.cn/img_convert/9e77312db7ee761f4c3c13b5327b540e.png)
#### 架构篇
**《Jetpack全家桶打造全新Google标准架构模式》**
![](https://img-blog.csdnimg.cn/img_convert/6c05c51798e7ed8a5b36ffc5e59ca5fd.png)
**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**
6571)]
#### 架构篇
**《Jetpack全家桶打造全新Google标准架构模式》**
[外链图片转存中...(img-GuNeVGJP-1630663156572)]
**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**