Flutter 137: 圖解自定義 ACEFoldTextView 折疊文本

????小菜在學(xué)習(xí) Flutter 過(guò)程中,有特別需求是對(duì)于文本過(guò)長(zhǎng)的內(nèi)容需要展示固定行數(shù)歼跟,而在文本右下角有提示用戶點(diǎn)擊展開(kāi)和收起和媳;小菜嘗試自定義一個(gè)可折疊收縮的 ACEFoldTextView

ACEFoldTextView

????小菜首先簡(jiǎn)單梳理了一下設(shè)計(jì)流程哈街,如下圖所示留瞳;

  • 當(dāng)文本內(nèi)容所占據(jù)行數(shù)小于等于限制的最大行數(shù)時(shí),默認(rèn)展示整個(gè)文本內(nèi)容叹卷,不會(huì)有【展開(kāi)/收起】撼港;
  • 當(dāng)文本內(nèi)容所占據(jù)行數(shù)大于限制的最大行數(shù)時(shí),默認(rèn)展示最大行數(shù)內(nèi)容骤竹,并在右下角顯示【展開(kāi)】提示;
  • 點(diǎn)擊【展開(kāi)】區(qū)域時(shí)往毡,當(dāng)文本內(nèi)容最后一行內(nèi)容與【展開(kāi)】區(qū)域占據(jù)內(nèi)容寬度之和小于最大寬度時(shí)蒙揣,默認(rèn)展示【收起】;
  • 點(diǎn)擊【展開(kāi)】區(qū)域時(shí)开瞭,當(dāng)文本內(nèi)容最后一行內(nèi)容與【展開(kāi)】區(qū)域占據(jù)內(nèi)容寬度之和大于等于最大寬度時(shí)懒震,【收起】區(qū)域換行展示;


1. 透明漸變【展開(kāi)/收起】

????小菜整體通過(guò) Stack 層級(jí)嵌套方式在右下角顯示可點(diǎn)擊的【展開(kāi)/收起】文本區(qū)嗤详,為了提高顯示效果个扰,并防止完全遮擋內(nèi)容文本,小菜嘗試了兩種方式來(lái)實(shí)現(xiàn)顏色透明度漸變葱色;

1.1 ShaderMask 著色器

????小菜之前有重點(diǎn)介紹過(guò) ShaderMask 著色器递宅,可以對(duì)子 Widget 進(jìn)行顏色處理,包括遮罩層特效展示苍狰;小菜設(shè)置了一個(gè) LinearGradient 線性漸變办龄,但 ShaderMask 是對(duì)整個(gè)子 Widget 遮罩層生效,可能會(huì)影響 Text 文本顯示效果淋昭,需要 Stack 層級(jí)使用俐填;

_transparentWid02() => ShaderMask(
    shaderCallback: (bounds) => LinearGradient(
          colors: [_bgColor.withOpacity(0.0), _bgColor],
        ).createShader(bounds),
    child: Container(
        alignment: Alignment.centerRight,
        color: Colors.white,
        width: _kMoreWidth,
        child: Text((_temLines > _maxLines) ? '展開(kāi)' : '收起',
            style: TextStyle(color: Theme.of(context).accentColor, fontSize: widget.textStyle?.fontSize ?? 14.0))));

1.2 Container BoxDecoration

????第二種就是常用的 Container 配合設(shè)置 BoxDecoration 設(shè)置線性漸變色;該方式使用更為便捷翔忽;

_transparentWid01() => Container(
    alignment: Alignment.centerRight,
    decoration: BoxDecoration(
        gradient: LinearGradient(
            colors: [_bgColor.withOpacity(0.0), _bgColor],
            end: FractionalOffset(0.5, 0.5))),
    width: _kMoreWidth,
    child: Text((_temLines > _maxLines) ? '展開(kāi)' : '收起',
        style: TextStyle(color: Theme.of(context).accentColor, fontSize: widget.textStyle?.fontSize ?? 14.0)));

2. Text 文本內(nèi)容折疊

????小菜想實(shí)現(xiàn)文本折疊英融,首先需要預(yù)先得知 Text 文本在范圍內(nèi)占據(jù)的行數(shù),一般都需要通過(guò) TextPainter 等方式獲刃健驶悟;小菜嘗試了兩種方式進(jìn)行判斷;

2.1 TextPainter.didExceedMaxLines

????小菜之前也有簡(jiǎn)單了解過(guò) TextPainterTextSpan 的應(yīng)用贬丛,主要用于文本的繪制撩银,當(dāng)設(shè)置 maxLines 之后,可以通過(guò) didExceedMaxLines 判斷文本內(nèi)容是否已經(jīng)超行豺憔;小菜之后會(huì)對(duì) TextPainter 再深入研究一下额获;

_checkOverMaxLines01(maxLines, maxWidth) {
  final textSpan = TextSpan(text: _textStr, style: widget.textStyle);
  final textPainter = TextPainter(text: textSpan, textDirection: TextDirection.ltr, maxLines: maxLines);
  textPainter.layout(maxWidth: widget.maxWidth ?? MediaQuery.of(context).size.width);
  return textPainter.didExceedMaxLines;
}

2.2 LineMetrics

????didExceedMaxLines 可以直接獲取文本內(nèi)容是否超行够庙,但無(wú)法獲取每行文本信息等;于是小菜嘗試了 computeLineMetrics() 方式獲取 LineMetrics 基線度量抄邀;可以獲取每行內(nèi)容所占據(jù)的寬高等耘眨;

????當(dāng)然 LineMetrics 也無(wú)法獲取每行文本內(nèi)容,以及在兩種文本對(duì)齊方式共用時(shí)有注意事項(xiàng)境肾,小菜之后會(huì)進(jìn)一步研究剔难;

????Tips: 在使用 computeLineMetrics() 獲取 LineMetrics 信息時(shí),需要注意 TextPainter 必須設(shè)置好 textDirection 文本對(duì)齊方式奥喻,以及在 layout 布局之后才可以獲扰脊;

_checkOverMaxLines02(maxWidth) {
  final textSpan = TextSpan(text: _textStr, style: widget.textStyle);
  final textPainter = TextPainter(text: textSpan, textDirection: TextDirection.ltr);
  textPainter.layout(maxWidth: widget.maxWidth ?? MediaQuery.of(context).size.width);
  _lines = textPainter.computeLineMetrics();
  return _lines;
}

3. ACEFoldTextView

????有了前面兩步的基礎(chǔ)环鲤,小菜將其結(jié)合起來(lái)纯趋,生成自定義 ACEFoldTextView;通過(guò) LinearBuilder 約束子 Text 延遲加載冷离;通過(guò) LineMetrics 獲取最后一行文本長(zhǎng)度吵冒,與默認(rèn)【展開(kāi)】所在 Widget 計(jì)算總和,之后判斷是否占據(jù)超過(guò)限制最大寬度西剥;當(dāng)超過(guò)最大寬度時(shí)痹栖,小菜將文本添加一個(gè) \n 強(qiáng)制換行;

return LayoutBuilder(builder: (context, size) {
  _isOverFlow = _checkOverMaxLines01(_maxLines, widget.maxWidth);
  _temLines = _checkOverMaxLines02(widget.maxWidth)?.length;
  return (_temLines <= _maxLines)
      ? _itemText() : Stack(children: <Widget>[_itemText(), _moreText()]);
});

_moreText() => Positioned(
      bottom: 0, right: 0,
      child: GestureDetector(
          child: _transparentWid02(),
          onTap: () => setState(() {
                if (_temLines > _maxLines) {
                  if (_lines.last.width + _kMoreWidth >= widget.maxWidth) {
                    _maxLines = _temLines + 1;
                    _textStr = '${widget.text}\n';
                  } else {
                    _maxLines = _temLines;
                  }
                } else if (_temLines == _maxLines) {
                  _maxLines = widget.maxLines;
                }
              })));

????ACEFoldTextView 案例源碼


????小菜對(duì) ACEFoldTextView 的繪制到此為止瞭空,其中涉及到 TextPainter 內(nèi)容較淺顯揪阿,小菜之后會(huì)進(jìn)一步學(xué)習(xí)研究;如有錯(cuò)誤匙铡,請(qǐng)多多指導(dǎo)图甜!

來(lái)源: 阿策小和尚

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鳖眼,隨后出現(xiàn)的幾起案子黑毅,更是在濱河造成了極大的恐慌,老刑警劉巖钦讳,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矿瘦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡愿卒,警方通過(guò)查閱死者的電腦和手機(jī)缚去,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)琼开,“玉大人易结,你說(shuō)我怎么就攤上這事。” “怎么了搞动?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵樊零,是天一觀的道長(zhǎng)府怯。 經(jīng)常有香客問(wèn)我苗桂,道長(zhǎng)玫镐,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任箩溃,我火速辦了婚禮瞭吃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涣旨。我一直安慰自己歪架,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布开泽。 她就那樣靜靜地躺著牡拇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪穆律。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天导俘,我揣著相機(jī)與錄音峦耘,去河邊找鬼。 笑死旅薄,一個(gè)胖子當(dāng)著我的面吹牛辅髓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播少梁,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼洛口,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了凯沪?” 一聲冷哼從身側(cè)響起第焰,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妨马,沒(méi)想到半個(gè)月后挺举,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡烘跺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年湘纵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滤淳。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梧喷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铺敌,我是刑警寧澤汇歹,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站适刀,受9級(jí)特大地震影響秤朗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜笔喉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一取视、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧常挚,春花似錦作谭、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吼过,卻和暖如春锐秦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盗忱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工酱床, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人趟佃。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓扇谣,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親闲昭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子罐寨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容