写点什么

Flutter RichText 支持图片显示和自定义图片效果,经典 Android 开发教程

用户头像
Android架构
关注
发布于: 刚刚

///so that we can define letterSpacing as Image Span widthconst String imageSpanTransparentPlaceholder = "\u200B";


///transparentPlaceholder is transparent text//fontsize id define image height//size = 30.0/26.0 * fontSize///final double size = 30.0;///fontSize 26 and text height =30.0//final double fontSize = 26.0;


double dpToFontSize(double dp) {return dp / 30.0 * 26.0;}


图片文字那么必然要有图片了,那么我们就提供个 ImageProvider 来装载图片,因为做过extended image,这部分不要太熟悉了,对 image 不了解的同学可以去看看 这个 全能的Image


当然我没有忘记给大家准备网络图片缓存的 ImageProvider,以及清除它们的方法 clearExtendedTextDiskCachedImages


CachedNetworkImage(this.url,{this.scale = 1.0,this.headers,this.cache: false,this.retries = 3,this.timeLimit,this.timeRetry = const Duration(milliseconds: 100)}): assert(url != null),


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


assert(scale != null);


/// Clear the disk cache directory then return if it succeed./// <param name="duration">timespan to compute whether file has expired or not</param>Future<bool> clearExtendedTextDiskCachedImages({Duration duration}) async


需要注意的是,因为 ImageSpan 没法获取到 BuildContext,所以我们需要在 Extended text build 的时候,把 ImageProvider 所需要的 ImageConfiguration 准备好


void _createImageConfiguration(List<TextSpan> textSpan, BuildContext context) {textSpan.forEach((ts) {if (ts is ImageSpan) {ts.createImageConfiguration(context);} else if (ts.children != null) {_createImageConfiguration(ts.children, context);}});}


接下来就要到核心绘画文字的类里面去了 ExtendedRenderParagraph 在 Paint 方法中,在画字之前我们来处理这个图片(反正文字是透明的,而且 0 的 width,只是有个与前后文字的距离(图片的宽)),在绘画图片的时候,我把画布移动到 offset 的地方,就是整个文字开始绘画的点,方便后面计算的绘画


void paint(PaintingContext context, Offset offset) {_paintSpecialText(context, offset);_paint(context, offset);}


void _paintSpecialText(PaintingContext context, Offset offset) {final Canvas canvas = context.canvas;


canvas.save();///move to extended textcanvas.translate(offset.dx, offset.dy);


///we have move the canvas, so rect top left should be (0,0)final Rect rect = Offset(0.0, 0.0) & size;_paintSpecialTextChildren(<TextSpan>[text], canvas, rect);canvas.restore();}


在_paintSpecialTextChildren 中,循环找寻 ImageSpan. 注意使用 getOffsetForCaret 方法,我们来判断这个 TextSpan 是否已经是文本溢出了。


Offset topLeftOffset = getOffsetForCaret(TextPosition(offset: textOffset),rect,);//skip invalid or overflowif (topLeftOffset == null ||(textOffset != 0 && topLeftOffset == Offset.zero)) {return;}


textOffset 起始为 0,当跳过一个 TextSpan,我们加上该 TextSpan 的 offset,然后继续查找


textOffset += ts.toPlainText().length;


如果是一个 ImageSpan,首先因为这个\u200B 没有宽度,而宽度是我们设置的 letterSpacing,所以这个图片绘画的地方应该要向前移动 width / 2.0


if (ts is ImageSpan) {///imageSpanTransparentPlaceholder \u200B has no width, and we define image width by///use letterSpacing,so the actual top-left offset of image should be subtract letterSpacing(width)/2.0Offset imageSpanOffset = topLeftOffset - Offset(ts.width / 2.0, 0.0);


if (!ts.paint(canvas, imageSpanOffset)) {//image not readyts.resolveImage(listener: (ImageInfo imageInfo, bool synchronousCall) {if (synchronousCall)ts.paint(canvas, imageSpanOffset);else {if (owner == null || !owner.debugDoingPaint) {markNeedsPaint();}}});}}


ImageSpan 的 paint 方法,如果图片还没加载,那么我们需要 resolveImage 并且监听回调,在回调的时候,如果是一个同步的回调,那么这个时候 Canvas 应该不没有被 dispose 掉,那么我们就直接画上。否则判断 owner,并且设置 markNeedsPaint,让整个 Text 再次绘画。


上面就是怎么在文本中加入一个图片,然而产品可不是那么好对付的,产品说,那个图片给我加个圆角,加个 Border,加个加载效果,给弄成圆形的,巴拉巴拉...说累了,你就直接按照下面的图来做吧。



看到这样的需求,我的表情为



不过其实掌握了 Canvas 的一些技巧之后,这点事情难不倒我,加上 2 个回调,在绘画图片之前和之后,做你想要做的任何事情。


///you can paint your placeholder or clip///any thing you wantfinal BeforePaintImage beforePaintImage;


///you can paint border,shadow etcfinal AfterPaintImage afterPaintImage;


比如说在图片加载之后来个 loading 占位,你可以这样做

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Flutter RichText支持图片显示和自定义图片效果,经典Android开发教程