日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何低成本实现Flutter富文本,看这一篇就够了!

發(fā)布時(shí)間:2024/8/23 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何低成本实现Flutter富文本,看这一篇就够了! 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

作者:閑魚技術(shù)-玄川

背景

閑魚是國(guó)內(nèi)最早使用Flutter 的團(tuán)隊(duì),作為一個(gè)電商App商品詳情頁是非常重要場(chǎng)景,其中最主要的技術(shù)能力是文字混排。

我們面對(duì)文本類的需求是復(fù)雜而且多變,然而Flutter歷史的幾個(gè)版本,Text只能顯示簡(jiǎn)單樣式文本,它只有包含一些控制文本樣式顯示的屬性,而通過TextSpan連接實(shí)現(xiàn)的RichText也只能顯示多種文本樣式(例如:一個(gè)基礎(chǔ)文本片段和一個(gè)鏈接片段),這些遠(yuǎn)遠(yuǎn)達(dá)不到設(shè)計(jì)需要的能力。被產(chǎn)品和設(shè)計(jì)慫為啥別人別的平臺(tái)能做,Flutter為何做不了,不管,必須支持。

因此,需要開發(fā)一個(gè)能力更強(qiáng)的文字混排組件就變得迫在眉睫。

富文本的原理

再講文字混批組件設(shè)計(jì)實(shí)現(xiàn)前,先來講講系統(tǒng)RichText的富文本的原理。

  • 創(chuàng)建過程

創(chuàng)建RichText節(jié)點(diǎn)的時(shí)候其實(shí)會(huì)創(chuàng)建以下幾個(gè)對(duì)象:

RenderParagraph實(shí)例最后會(huì)將自身登記到渲染模塊的Dirty Nodes當(dāng)中去,渲染模塊會(huì)遍歷Dirty Nodes 將進(jìn)入RenderParagraph 渲染環(huán)節(jié)。

  • 先創(chuàng)建LeafRenderObjectElement實(shí)例。
  • ComponentElement方法當(dāng)中會(huì)調(diào)用RichText實(shí)例的CreateRenderObject方法,生成RenderParagraph 實(shí)例。
  • RenderParagraph 會(huì)創(chuàng)建TextPainter 負(fù)責(zé)其就計(jì)算寬高和繪制文本到Canvas 的代理類,同時(shí)TextPainter 持有TextSpan 文本結(jié)構(gòu)。
    • 渲染過程

      RenderParagraph 方法當(dāng)中封裝的是將文本繪制到 canvas 上面的邏輯,主要是用了一個(gè)叫做 TextPainter 的模塊,其調(diào)用過程遵循RenderObject 調(diào)用。

    • PerfromLayout 過程通過調(diào)用TextPaint的Layout,在期過程中通過TextSpan 結(jié)構(gòu)樹,依次通過AddText 添加各個(gè)階段的文本,最后通過Paragraph的Layout 計(jì)算文本高度。
    • Paint 過程,先繪制clipRect,接著通過TextPaint的Paint函數(shù)調(diào)用,Paragraph的Paint繪制文本,最后繪制drawRect。

    設(shè)計(jì)思路

    通過RichText的文本繪制原理,我們不難發(fā)現(xiàn)TextSpan記錄了各段文本信息,TextPaint通過記錄的信息調(diào)用Native接口計(jì)算寬高,以及將文本繪制到canvas上面。傳統(tǒng)的方案實(shí)現(xiàn)復(fù)雜的混排,會(huì)通過HTML去做一個(gè)WebView的富文本,使用WebView在性能上自然不及原生實(shí)現(xiàn),出于性能的考慮,我們?cè)O(shè)想通過通過原生的方式去實(shí)現(xiàn)圖文混排。一開始的方案是設(shè)計(jì)幾種特殊的Span(例如:ImageSpan,EmojiSpan等),通過Span記錄的信息,在TextPaint的Layout 重新根據(jù)各種類型重新計(jì)算布局,在Paint過程再分別繪制特殊的Widget,然而這種方案對(duì)上面幾個(gè)涉及的類封裝破壞的特別大,需要將RichText、RenderParagraph 源碼Copy 出來重新修改。最后設(shè)想是后可以通過特殊的文字先占位置,(例如:空字符串),然后在這個(gè)文字的位置上面把特殊的Span分別獨(dú)立移動(dòng)到上面。

    然而上面這種方案會(huì)帶來兩個(gè)難點(diǎn):

    • 難點(diǎn)一:如何在文本中先占位,并且能制定任意想要的寬高。

    通過Google 發(fā)現(xiàn)u200B字符代表ZERO WIDTH SPACE(寬帶為0的空白),結(jié)合對(duì)TextPainter測(cè)試,我們發(fā)現(xiàn)layout出來的Width總是0,fontSize只決定了高度,結(jié)合TextStyle里面的letterSpacing

    /// The amount of space (in logical pixels) to add between each letter /// A negative value can be used to bring the letters closer. final double letterSpacing;

    這樣我們就能任意的控制這個(gè)特殊文字的寬高度。

    • 難點(diǎn)二:如何將特殊的Span移動(dòng)到位置上面。

    通過上面的測(cè)試不難發(fā)現(xiàn),特殊的Span其實(shí)還是獨(dú)立Widget和RichText并不融合。所以我們需要知道當(dāng)前widget相對(duì)RichText空間的相對(duì)位置,并且結(jié)合Stack將其融合。結(jié)合TextPaint里面的getOffsetForCaret方法

    /// Returns the offset at which to paint the caret.////// Valid only after [layout] has been called.Offset getOffsetForCaret(TextPosition position, Rect caretPrototype)

    可以天然的獲取到當(dāng)前占位符相對(duì)位置。

    實(shí)現(xiàn)方案

    關(guān)鍵部分代碼實(shí)現(xiàn)如下:

    • 統(tǒng)一的占位SpaceSpan

      SpaceSpan({this.contentWidth,this.contentHeight,this.widgetChild,GestureRecognizer recognizer, }) : super(style: TextStyle(color: Colors.transparent,letterSpacing: contentWidth,height: 1.0,fontSize:contentHeight),text: '\u200B',recognizer: recognizer);
    • SpaceSpan 相對(duì)位置獲取

      for (TextSpan textSpan in widget.text.children) {if (textSpan is SpaceSpan) {final SpaceSpan targetSpan = textSpan;Offset offsetForCaret = painter.getOffsetForCaret(TextPosition(offset: textIndex),Rect.fromLTRB(0.0, targetSpan.contentHeight, targetSpan.contentWidth, 0.0),);........}textIndex += textSpan.toPlainText().length;}
    • RichtText和SpaceSpan融合

      Stack(children: <Widget>[RichText(),Positioned(left: position.dx, top: position.dy, child: child),],);}

    效果

    先上圖看看效果

    這種方案的優(yōu)點(diǎn)是任意Widget可通過SpaceSpan和RichText進(jìn)行組合,無論是圖片、自定義標(biāo)簽、甚至是按鈕都可以融合進(jìn)來,同時(shí)對(duì)RichText本身封裝性破壞較小。

    未來

    上面只是富文本顯示的部分,依然存在著很多局限,還有較多需要優(yōu)化的點(diǎn),目前通過SpaceSpan 控件,必需要指定寬高,另外對(duì)于文本選擇、自定義文字背景這些都是無法支持,其次對(duì)富文本編輯器的支持,可以使其編輯文字時(shí),讓圖片、貨幣格式化等控件輸入等。


    原文鏈接
    本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。

    總結(jié)

    以上是生活随笔為你收集整理的如何低成本实现Flutter富文本,看这一篇就够了!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。