为了账号安全,请及时绑定邮箱和手机立即绑定

Flutter 31: 图解 TextPainter 与 TextSpan 小尝试

标签:
Android

      大家在学习 Flutter 时一定会用过 Text,而对于一些复杂文本的处理可能会选择 RichText,再进一步,使用 RichText 就一定要用 TextSpan ,小菜本以为可以做为一个小知识点进行简单学习,但是随着深入尝试发现 TextSpan 涉及东西很多,很值得研究,因此单独整理一篇小博文。

      RichText 富文本核心即 TextSpan,而 TextSpan 结构很像 Android 中的 ViewGroup 树型结构。

webp

TextSpan 树形结构.png

webp

RichText 日常用法

      小菜理解为 RichText 是进阶版的 Text,如下直接看实例:

  1. TextDirection 用来控制文字位置,居左或居右边;当与 TextAlign 属性共存时,优先看整体,以 TextAlign 为准;

Widget richTextWid01() {  return RichText(
      text: TextSpan(
          text: 'TextDirection.ltr 文字默认居左',
          style: TextStyle(fontSize: 16.0, color: Colors.black)),
      textDirection: TextDirection.ltr);
}Widget richTextWid02() {  return RichText(
      text: TextSpan(
          text: 'TextDirection.rtl 文字默认居右',
          style: TextStyle(fontSize: 16.0, color: Colors.black)),
      textDirection: TextDirection.rtl);
}Widget richTextWid03() {  return RichText(
      text: TextSpan(
          text: 'textDirection 与 textAlign 同时设置,优先看整体,文字居中',
          style: TextStyle(fontSize: 16.0, color: Colors.black)),
      textDirection: TextDirection.rtl,
      textAlign: TextAlign.center);
}
  1. RichText 可以借助 TextSpan 实现文字的多种效果,小菜认为有点像文字效果拼接,每个 TextSpan 可以设置单独效果;

Widget richTextWid04() {  return RichText(
      text: TextSpan(
          text: '多种样式,如:',
          style: TextStyle(fontSize: 16.0, color: Colors.black),
          children: <TextSpan>[
            TextSpan(
                text: '红色',
                style: TextStyle(fontSize: 18.0, color: Colors.red)),
            TextSpan(
                text: '绿色',
                style: TextStyle(fontSize: 18.0, color: Colors.green)),
            TextSpan(
                text: '蓝色',
                style: TextStyle(fontSize: 18.0, color: Colors.blue)),
            TextSpan(
                text: '白色',
                style: TextStyle(fontSize: 18.0, color: Colors.white)),
            TextSpan(
                text: '紫色',
                style: TextStyle(fontSize: 18.0, color: Colors.purple)),
            TextSpan(
                text: '黑色',
                style: TextStyle(fontSize: 18.0, color: Colors.black))
          ]),
      textAlign: TextAlign.center);
}
  1. TextSpan 可以借助 recognizer 设置点击事件,包括点击/长按等;

final TapGestureRecognizer recognizer = TapGestureRecognizer();void initState() {  super.initState();
  recognizer.onTap = () {
    Toast.show('您好,欢迎点赞或关注!', context,
        duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
  };
}Widget richTextWid05() {  return RichText(
      text: TextSpan(
          text: 'recognizer 为手势识别者,可设置点击事件,',
          style: TextStyle(fontSize: 17.0, color: Colors.black),
          children: <TextSpan>[
        TextSpan(
            text: '点我试试',
            style: TextStyle(fontSize: 17.0, color: Colors.blue),
            recognizer: recognizer)
      ]));
}

TextPainter 日常用法

      RichText 的使用很方便,但如果在深入了解 TextSpan 就有很多趣味了;Flutter 提供了和 Android 类似的 Canvas 绘制方法,但是 Canvas 却不支持 drawText,如果想要实现绘制文字,就需要 TextPainter 而其内部主要是由 TextSpan 实现。

      使用 TextPainter 时需要继承 CustomPainter,并实现 paintshouldRepaint 方法,主要是在 paint 中进行绘制 TextPainter。与 RichText 功能相同,可以完全实现 RichText 效果;

      TextPainter 绘制需要实现 layoutpaint 方法,即绘制位置与绘制范围。

  1. TextDirectionTextAlign 效果与 RichText 一致,但是 TextPainter 绘制时需要设置 layout 的最大最小范围,而此时,文字位置与 layout 有关;当文字长度小于设置的 minWidth 最小宽度时,以 minWidth 宽度为限制居左/居右/居中等;而当文字长度大于设置的 minWidth 最小宽度时,以 maxWidth 最大宽度为限制,包括换行等;

TextPainter(
    text: TextSpan(
        text: 'TextDirection.ltr 文字默认居左',
        style: TextStyle(fontSize: 16.0, color: Colors.black)),
    textDirection: TextDirection.ltr)
  ..layout(maxWidth: Screen.width, minWidth: Screen.width)
  ..paint(canvas, Offset(0.0, 0.0));
TextPainter(
    text: TextSpan(
        text: 'TextDirection.rtl 文字默认居右',
        style: TextStyle(fontSize: 16.0, color: Colors.black)),
    textDirection: TextDirection.rtl)
  ..layout(maxWidth: Screen.width, minWidth: Screen.width)
  ..paint(canvas, Offset(0.0, 24.0));
TextPainter(
    text: TextSpan(
        text: 'textDirection 与 textAlign 同时设置,优先看整体,文字居中',
        style: TextStyle(fontSize: 16.0, color: Colors.black)),
    textDirection: TextDirection.rtl,
    textAlign: TextAlign.center)
  ..layout(maxWidth: Screen.width, minWidth: Screen.width)
  ..paint(canvas, Offset(0.0, 48.0));
TextPainter(
    text: TextSpan(
        text: '文字位置与 layout 的最大最小宽度有关',
        style: TextStyle(fontSize: 16.0, color: Colors.black)),
    textDirection: TextDirection.rtl)
  ..layout(maxWidth: Screen.width - 100.0, minWidth: Screen.width - 200.0)
  ..paint(canvas, Offset(0.0, 90.0));
TextPainter(
    text: TextSpan(
        text: '文字长度较小',
        style: TextStyle(fontSize: 16.0, color: Colors.black)),
    textDirection: TextDirection.rtl)
  ..layout(maxWidth: Screen.width - 100.0, minWidth: Screen.width - 140.0)
  ..paint(canvas, Offset(0.0, 124.0));
  1. 而对于绘制多效果的样式也是很方便,与 RichText 基本一致;

TextPainter(
    text: TextSpan(
        text: '多种样式,如:',
        style: TextStyle(fontSize: 16.0, color: Colors.black),
        children: <TextSpan>[
          TextSpan(
              text: '红色',
              style: TextStyle(fontSize: 18.0, color: Colors.red)),
          TextSpan(
              text: '绿色',
              style: TextStyle(fontSize: 18.0, color: Colors.green)),
          TextSpan(
              text: '蓝色',
              style: TextStyle(fontSize: 18.0, color: Colors.blue)),
          TextSpan(
              text: '白色',
              style: TextStyle(fontSize: 18.0, color: Colors.white)),
          TextSpan(
              text: '\n紫色',
              style: TextStyle(fontSize: 18.0, color: Colors.purple)),
          TextSpan(
              text: '黑色',
              style: TextStyle(fontSize: 18.0, color: Colors.black))
        ]),
    textDirection: TextDirection.ltr,
    textAlign: TextAlign.center)
  ..layout(maxWidth: Screen.width, minWidth: Screen.width)
  ..paint(canvas, Offset(0.0, 148.0));
  1. 小菜一直有问题的就是设置点击事件,小菜以为与 RichText 一样直接传递 recognizer 即可,但始终无法调起,希望有解决过这个问题的朋友多多指导,如下是小菜的测试代码;

TextPainter(
    text: TextSpan(
        text: 'recognizer 为手势识别者,可设置点击事件,',
        style: TextStyle(fontSize: 17.0, color: Colors.black),
        children: <TextSpan>[
          TextSpan(
              text: '测试暂时有误,待研究',
              style: TextStyle(fontSize: 17.0, color: Colors.blue))
        ],
        recognizer: TapGestureRecognizer()
          ..onTap = () {            print('===测试暂时有误,待研究==');
          }),
    textDirection: TextDirection.ltr,
    textAlign: TextAlign.center)
  ..layout(maxWidth: Screen.width - 40.0, minWidth: Screen.width - 40.0)
  ..paint(canvas, Offset(20.0, 200.0));
  1. 小菜认为最有意思的就是 TextSpanstyleheight 属性,在 TextSpan 中此值设置行高,是以文字基准线为最小距离;

TextPainter(
    text: TextSpan(
        text: 'TextPainter 小尝试',
        style: TextStyle(fontSize: 20.0, color: Colors.black54),
        children: <TextSpan>[
          TextSpan(
              text: '\n作者:和尚(height:1.6)',
              style: TextStyle(
                  fontSize: 14.0, color: Colors.black54, height: 1.6))
        ]),
    textDirection: TextDirection.ltr,
    textAlign: TextAlign.center)
  ..layout(maxWidth: Screen.width, minWidth: Screen.width)
  ..paint(canvas, Offset(0.0, 20.0));
TextPainter(
    text: TextSpan(
        text: 'TextPainter 小尝试',
        style: TextStyle(fontSize: 20.0, color: Colors.black54),
        children: <TextSpan>[
          TextSpan(
              text: '\n作者:和尚(height:3.0)',
              style: TextStyle(
                  fontSize: 14.0, color: Colors.black54, height: 3.0))
        ]),
    textDirection: TextDirection.ltr,
    textAlign: TextAlign.center)
  ..layout(maxWidth: Screen.width, minWidth: Screen.width)
  ..paint(canvas, Offset(0.0, 90.0));
TextPainter(
    text: TextSpan(
        text: 'TextPainter 小尝试(height:0.1)',
        style:
            TextStyle(fontSize: 20.0, color: Colors.black54, height: 0.1),
        children: <TextSpan>[
          TextSpan(
              text: '\nTextPainter 小尝试(height:0.1)',
              style: TextStyle(
                  fontSize: 20.0, color: Colors.black54, height: 0.1)),
          TextSpan(
              text: '\nTextPainter 小尝试(height:0.1)',
              style: TextStyle(
                  fontSize: 20.0, color: Colors.black54, height: 0.1))
        ]),
    textDirection: TextDirection.ltr,
    textAlign: TextAlign.center)
  ..layout(maxWidth: Screen.width, minWidth: Screen.width)
  ..paint(canvas, Offset(0.0, 220.0));

webp



作者:阿策神奇


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
165
获赞与收藏
165

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消