以前介紹過了Extended text的圖片功能 ,今天要講的仍是跟產品設計有關係,老規矩上圖 git
產品說,那個文本溢出的點點點後面給我加個雞腿,想什麼啊,是加個 「全文」字樣,點擊以後跳轉到全文去。github
首先,我看了下Text的源碼,發現這個...是被寫死了的,傳遞給了TextPaintermarkdown
const String _kEllipsis = '\u2026';
複製代碼
而後再向裏面看,就是引擎繪畫的代碼了。。看不到了。。是我太弱了。 在google上搜索了下,發現也有問這個問題26748,上面也是說了。。須要把源碼複製出來,把_kEllipsis改爲你想要的,可是。。這個是個字符串啊。。那個好比說藍色怎麼弄?好比說點擊怎麼弄?ide
想來想去,一個字就是畫,在Canvas上面盡情畫。svg
首先,我定義了一個TextSpan 用於用戶自定義文本溢出效果post
overFlowTextSpan: OverFlowTextSpan(children: <TextSpan>[
TextSpan(text: ' \u2026 '),
TextSpan(
text: "more detail",
style: TextStyle(
color: Colors.blue,
),
recognizer: TapGestureRecognizer()
..onTap = () {
launch(
"https://github.com/fluttercandies/extended_text");
})
], background: Theme.of(context).canvasColor),
複製代碼
而後咱們直接來到ExtendedRenderParagraph的paint方法ui
@override
void paint(PaintingContext context, Offset offset) {
_paintSpecialText(context, offset);
_paint(context, offset);
_paintTextOverflow(context, offset);
}
複製代碼
這個效果確定須要在畫好字以後,再來魔改google
void _paintTextOverflow(PaintingContext context, Offset offset) {
if (_hasVisualOverflow && overFlowTextSpan != null) {
final Canvas canvas = context.canvas;
///we will move the canvas, so rect top left should be (0,0)
final Rect rect = Offset(0.0, 0.0) & size;
var textPainter = overFlowTextSpan.layout(_textPainter);
assert(textPainter.width <= rect.width,);
}
複製代碼
首先咱們須要layout一下咱們的overFlowTextSpan,若是你定義的太長,已經超出一行了的話,那麼抱歉 spa
老動做,把畫布移動到整個文字的左上角,根據overFlowTextSpanOffset的左上角,計算出最近的TextPosition。
canvas.save();
///move to extended text
canvas.translate(offset.dx, offset.dy);
final Offset overFlowTextSpanOffset = Offset(
rect.width - textPainter.width, rect.height - textPainter.height);
///find TextPosition near overflow
TextPosition overflowOffset =
getPositionForOffset(overFlowTextSpanOffset);
複製代碼
再經過這個找出最近文字的top-left,這樣才能保證不會剪切到半個或者不徹底的文字。
///find overflow TextPosition that not clip the original text
Offset finalOverflowOffset = _findFinalOverflowOffset(
rect, rect.width - textPainter.width, overflowOffset.offset);
Offset _findFinalOverflowOffset(Rect rect, double x, int endTextOffset) {
Offset endOffset = getOffsetForCaret(
TextPosition(offset: endTextOffset, affinity: TextAffinity.upstream),
rect,
);
//overflow
if (endOffset == null || (endTextOffset != 0 && endOffset == Offset.zero)) {
return _findFinalOverflowOffset(rect, x, endTextOffset - 1);
}
if (endOffset.dx > x) {
return _findFinalOverflowOffset(rect, x, endTextOffset - 1);
}
return endOffset;
}
複製代碼
這樣子咱們就找到咱們須要在哪一個文字的位置把OverFlowTextSpan繪畫出來,而且想辦法把OverFlowTextSpan下一層的文字給清除或者遮擋住。
首先嚐試是用BlendMode.clear來清除指定區域的文字,失敗,不知道爲何, 我看別人也是這樣子寫的,能清除掉Canvas上面的內容,若是有哪一個兄弟知道,請必定要告訴我,感謝萬分。
///why BlendMode.clear not clear the text
// canvas.saveLayer(overFlowTextSpanRect, Paint());
// canvas.drawRect(
// overFlowTextSpanRect,
// Paint()
// ..blendMode = BlendMode.clear);
// canvas.restore();
複製代碼
那麼只能畫一層跟Canvas同樣的顏色來遮住文字了。這裏默認使用的是
Theme.of(context).canvasColor
複製代碼
而後咱們再畫上OverFlowTextSpan
textPainter.paint(
canvas, Offset(finalOverflowOffset.dx, overFlowTextSpanOffset.dy));
複製代碼
最後咱們要處理一下點擊事件,保存textPainter繪畫的點(相對整個系統座標的)
overFlowTextSpan.textPainterHelper.saveOffset(Offset(
offset.dx + finalOverflowOffset.dx,
offset.dy + overFlowTextSpanOffset.dy));
複製代碼
在handleEvent方法中,咱們加入如下代碼,若是找到了對應註冊了recognizer的TextSpan,咱們就給它觸發,而且return(由於overFlowTextSpan在原來的字的上一層)
if (overFlowTextSpan != null) {
final TextPosition position =
overFlowTextSpan.textPainterHelper.getPositionForOffset(offset);
final TextSpan span =
overFlowTextSpan.textPainterHelper.getSpanForPosition(position);
if (span?.recognizer != null) {
span.recognizer.addPointer(event);
return;
}
}
複製代碼
_offset是咱們剛纔保持的相對整個系統座標的點,咱們須要把傳入的Offset減掉 _offset,這樣這個overFlowTextSpan的相對本身的座標系纔是以(0,0)開始的,最後用這個TextPosition找到對應的TextSpan,大功告成。
///method for [OverFlowTextSpan]
///offset int coordinate system
Offset _offset;
void saveOffset(Offset offset) {
_offset = offset;
}
///method for [OverFlowTextSpan]
TextPosition getPositionForOffset(Offset offset) {
return painter.getPositionForOffset(offset - _offset);
}
///method for [OverFlowTextSpan]
TextSpan getSpanForPosition(TextPosition position) {
return painter.text.getSpanForPosition(position);
}
複製代碼
除了清除(覆蓋)文字的那個部分,其餘應該都是比較完美的解決方案,期待你們能帶來更多點子,改進 Extended Text
最後放上 Github Extended_Text,若是你有什麼不明白的地方,請告訴我,歡迎加入Flutter Candies,一塊兒生產可愛的Flutter 小糖果(QQ羣:181398081)