【问题标题】:Finding the exact bounds of text查找文本的确切边界
【发布时间】:2018-08-16 17:40:59
【问题描述】:

我需要知道一段文本的确切范围——相当于 Android 的getTextBounds。我意识到这与 Flutter 的设计有些背道而驰,但我以非传统方式使用文本(就好像文本以精确的位置和大小嵌入到艺术图片中一样)。

我尝试了三种方法:

  • TextPainterminIntrinsicWidthheight。来源如下。这会产生一个四面都有空间的边界框:

    我需要该矩形的边正对着“8”的黑色像素。 (在这种特殊情况下,widthmaxIntrinsicWidth 给出与 minIntrinsicWidth 相同的值;另外,preferredLineHeight 给出与height 相同的值。)

  • Paragraph's getBoxesForRange -- 与 TextPainter 的结果基本相同。

  • FittedBox -- 也留下空格,无论BoxFit 枚举值如何。我以this answer 为起点。

TextPainter 代码如下。 (我意识到这是低效的;在获得所需的边界框之前我不在乎。我使用场景/图片/等希望在较低级别找到合适的功能。)

import 'dart:ui';
import 'dart:typed_data';
import 'package:flutter/painting.dart';

const black = Color(0xff000000);
const white = Color(0xffffffff);

final identityTransform = new Float64List(16)
  ..[0] = 1.0
  ..[5] = 1.0
  ..[10] = 1.0
  ..[15] = 1.0;

TextPainter createTextPainter() {
  return new TextPainter(
    text: new TextSpan(
      text: '8',
      style: new TextStyle(
        color: black,
        fontSize: 200.0,
        fontFamily: "Roboto",
      ),
    ),
    textDirection: TextDirection.ltr,
  )..layout();
}

void drawBackground(Canvas canvas, Rect screen) {
  canvas.drawRect(
      screen,
      new Paint()
        ..color = white
        ..style = PaintingStyle.fill);
}

Picture createPicture() {
  final recorder = new PictureRecorder();
  final screen = Offset.zero & window.physicalSize;
  final canvas = new Canvas(recorder, screen);
  final offset = screen.center;
  final painter = createTextPainter();
  final bounds = offset & Size(painter.minIntrinsicWidth, painter.height);
  drawBackground(canvas, screen);
  painter.paint(canvas, offset);
  canvas.drawRect(
      bounds,
      new Paint()
        ..color = black
        ..style = PaintingStyle.stroke
        ..strokeWidth = 3.0);
  return recorder.endRecording();
}

Scene createScene() {
  final builder = new SceneBuilder()
    ..pushTransform(identityTransform)
    ..addPicture(Offset.zero, createPicture())
    ..pop();
  return builder.build();
}

void beginFrame(Duration timeStamp) {
  window.render(createScene());
}

void main() {
  window.onBeginFrame = beginFrame;
  window.scheduleFrame();
}

【问题讨论】:

  • 您使用的是哪种字体?您确定字体本身在字形周围没有空格吗?
  • 我无法想象默认错误会像这样被窃听(这甚至可能用 ttf 吗?),但无论如何我添加了fontFamily: "Roboto",结果是一样的。
  • 为了清楚起见,Android 的 getTextBounds 给出了矩形,使得每一边大麦都接触到链接到图像中“8”的黑色像素,我用 Roboto 进行了测试。
  • 好吧。我只是用衬线字体试了一下,有些字符确实碰到了盒子的水平边缘——但这对你没有多大帮助。似乎颤动在内部做了一些事情来分隔字符......
  • 你可能是SOL直接使用flutter支持的东西。我可以给你一个替代方案,但这将是相当多的工作......这取决于你想投入多少时间以及你实际需要做多少文字(如果它是artistic,正如你所说,我假设没有那么多)。

标签: text flutter


【解决方案1】:

要忽略该填充,您必须进行 真正 低级调用,因为 Text 和空间实际上被绘制在一起。我尝试在互联网上搜索是否有其他方法,但找不到任何可以真正解决您的问题的方法。

我尝试搜索 Flutter SDK (Widget_Library) 的源代码,但我只能看到最终生成的小部件是 RichText,其源代码消失得无影无踪。 (我只是在 GitHub 上搜索它时找不到该小部件的定义......在 Android Studio 中,您可能会通过将鼠标悬停在它上面并按 Ctrl+LeftClick 来获得一些参考......但我很确定它需要你到另一个未知的世界,因为字体是根据它们的字体系列绘制的(这将是 .ttf 或 .otf 文件......无论哪种方式,它们都会与你所指的填充一起绘制)。

您现在所能做的就是创建我们自己的字符集小部件,该小部件接受原始字符串并读取每个字符并使用一些自定义绘制函数绘制您已经定义的数字/字符,

或者如果您准备放弃该填充以节省一些时间(这对您现在可能或可能不是至关重要的),您可以使用一些预定义的函数来获取空间或使用以下解决方案来获取您想要的任何小部件的尺寸。

我需要知道一段文本的确切边界...

了解任何小部件宽度的最简单方法是使用GlobalKey

声明并初始化一个变量来存储一个 GlobalKey,

GlobalKey textKey = GlobalKey();

GlobalKey 传递给Text 小部件的关键参数,

Text(
  "hello",
   key: textKey,
 )

现在我将用GestureDetector 包裹上面的小部件以打印上面的小部件的宽度。

GestureDetector(
  onTap: (){
    print(textKey.currentContext.size.width); //This is your width
    print(textKey.currentContext.size.height); //This is your height
      },
   child: Text(
     "hello",
     key: textKey,
       ),
     )

正如我之前提到的,您可以在任何小部件上使用它,那么您可以在 TextSpan 小部件上使用它:

GestureDetector(
  onTap: (){
    print(textKey.currentContext.size.width); //This is your width
      },
TextSpan(
      key: textKey,
      text: '8',
      style: new TextStyle(
        color: black,
        fontSize: 200.0,
        fontFamily: "Roboto",
      ),
    ),
  )

现在在GlobalKey() 的帮助下,您可以获得有关Text 所需的所有信息,并相应地绘制您的边界/矩形。

注意:GlobalKey() 为您的小部件提供了唯一标识,因此不要在多个小部件上重复使用它。

===================

编辑:

如果能够以某种方式获取您的应用/文本中每个像素的颜色,那么就有可能获得您想要实现的目标。

注意:您必须分别找到每一侧的偏移量/填充。

x1,y1 是容器起点的坐标,x2,y2 是终点。

minL,minR,minT,minB 是您要查找的偏移量。

基本逻辑:根据所考虑的边垂直/水平扫描每一行,直到你得到一个颜色等于文本颜色的像素,并对所有假想的线执行此操作,直到你得到最小偏移量(文本的距离从边界)所有方面。

想象你从容器的边界画一条线,直到你遇到一个文本的边界(/满足文本颜色),然后你对所有其他的(水平[左/右]或垂直[ Top/Bottom]) 线,你只需要记住最短的距离。

// Left Side
for(i=y1+1;i<y2;i++)
 for(j=x1+1;j<x2;j++)
   if(colorof(j,i)==textcolor && minL>j) {minL = j; break;}

// Right Side
for(i=y1+1;i<y2;i++)
 for(j=x2-1;j>x1;j--)
   if(colorof(j,i)==textcolor && minR>j) {minR = j; break;}

// Top Side
for(i=x1+1;i<x2;i++)
 for(j=y1;j<y2;j++)
   if(colorof(i,j)==textcolor && minT>j) {minT = j; break;}

// Bottom Side
for(i=x1+1;i<x1;i++)
 for(j=y2;j>y1;j--)
   if(colorof(i,j)==textcolor && minB>j) {minB = j; break;}

x1 = x1 + minL;
y1 = y1 + minT;
x2 = x2 - minR;
y2 = y2 - minB;

在实现上述逻辑时,确保没有其他颜色与文本颜色匹配的重叠/底层小部件(Scaffold/Stack Combo)。

【讨论】:

  • 富文本的源代码不会凭空消失(见this article)。但是,我不知道任何衡量实际文本而不是文本+填充的东西。你对8 做过测量吗?我怀疑它包括填充。
  • 正如答案中间接提到的那样,Flutter 没有明确给出这样的填充,这一切都根据文本的字体系列被绘制在一起......想象一下,如果你选择了一种时尚的字体它在其范围内尽可能对角扭曲 8 度,并要求您找到它的最小值。垂直偏移..您如何区分两者?
  • 我大致阅读了您评论的文章...我找不到任何可以知道偏移量的方法+其中没有直接提到字体系列...阅读该页面的 Way Down:Flutter 的文本引擎 部分...这可能在某种程度上对您有所帮助,但您肯定需要花费大量时间了解所有内容并重新设计 Flutter 的方式努力得到你正在寻找的东西 - 确切的界限。
  • 您可以做一件事...我已在答案中附加了解决方案。
  • 您的编辑是一个有趣的解决方案(因为目前似乎没有其他解决方案)。赏金是你的。
【解决方案2】:

您可以使用 TextPainter 类来确定某些文本的宽度。 https://api.flutter.dev/flutter/painting/TextPainter-class.html Flutter 在布局期间使用它来确定文本小部件的大小。

不确定 TextPainter 的结果是否会包含少量填充,但如果包含,那么已经提出的解决方案可能也会包含。

【讨论】:

  • 问题是专门要求文本周围没有填充。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-17
  • 2017-02-22
  • 2018-02-16
  • 2016-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多