[FMX] 计算文本所占用的显示区域大小

我们有时需要手动计算文本内容显示所需要占用的区域大小,在 VCL 中相信大家都很熟悉怎么做了。但在 FMX 中如何去跨平台的实现它,这就需要研究研究了,毕竟连 FMX 自带的 TLabel 的 AutoSize 设置为 true 都没产生任何效果。

好吧,首先看看我们要包含的单元:

  • FMX.TextLayout :我们需要 TTextLayout 实例来计算文本内容的显示区域
  • FMX.Graphics :我们需要它提供 TCanvasManager.MeasureCanvas 用于计算文本内容显示区域大小的画布
  • FMX.Objects :我们需要 TText 来取得样式中的字体设置(当然,如果你不使用样式来控制控件的字体大小,则请忽略它)

接下来,我们来看看如何实现这个计算的过程:

  • 首先,我们要确保我们使用的是正确的字体。如果一个控件,如 TLabel 使用了样式的字体,我们计算时就必需使用样式的字体,否则应该使用控件本身的字体设置。
  • 其次,我们计算时要设置一堆属性,最后直接取 TTextLayout 实例的 TextRect 就是内容占据的区域。由于面要更改 TTextLayout 实例的多个属性,我们最好调用 BeginUpdate/EndUpdate 来提高效率。

好了,我们来看实现:

function CalcTextRect(S: String; ASettings: TTextSettings;
  const AMaxWidth: Single): TRectF;
var
  ATextLayout: TTextLayout;
begin
  Result := TRectF.Create(0, 0, AMaxWidth, MaxInt);
  ATextLayout := TTextLayoutManager.TextLayoutByCanvas
    (TCanvasManager.MeasureCanvas.ClassType)
    .Create(TCanvasManager.MeasureCanvas);
  try
    ATextLayout.BeginUpdate;
    ATextLayout.Font.Assign(ASettings.Font);
    ATextLayout.HorizontalAlign := ASettings.HorzAlign;
    ATextLayout.WordWrap:=ASettings.WordWrap;
    ATextLayout.Text := S;
    ATextLayout.EndUpdate;
    Result:=ATextLayout.TextRect;
  finally
    ATextLayout.DisposeOf;
  end;
end;

我们测试一下效果:

procedure TForm2.Button1Click(Sender: TObject);
var
  ARect: TRectF;
begin
  ARect:=CalcTextRect(Memo1.Text,Label1.ResultingTextSettings,ClientWidth);
  Label1.Height := ARect.Height;
  Label1.Width := ARect.Width;
  Label1.Text := Memo1.Text;
end;

其中,我们在 Label1 中设置了一个 TRectangle,对齐方式为 Content,填充样式设置为 None,实际运行效果如下图:

TextLayout

等等,你说你前面要引用 TText 对象,这里也没见到呀!甭着急,上面的代码根本没考虑千变万化的变态样式。如果我们要考虑样式,则需要查找 Label1 所有的子控件中,类型为 TText,StyleName 为 text 的对象,然后计算它相对于 Label1 边缘的位置,然后再计算额外要添加的上、下、左、右空间,才能真正得出最终需要的矩形区域,不过这里就不啰嗦了,自己去玩吧。

如果考虑到多次重复执行测试,最好单独定义一个类,将 TTextLayout 实例缓存来下,避免不必要的重复创建。

分享到: