(原)FMX 设置ListBox的字体及自动计算ListItem高度

在用FMX的ListBox和ComboBox等涉及TListBox的时候,都要人工设置ItemHeight。

如果ItemHeight是固定不变的,并且是定制使用ListItem的Style的话,可以直接在Style中设置ItemHeight。

如果要动态改变ItemHeight,或者是ItemHeight的大小未知,自动设置ItemHeight, 就要根据Item的文本大小来调整,因此要测量ListItem的文本高度TextHeight(一般不用考虑宽度),如果Scale不是1,还得乘以Scale。所以就涉及到测量文本的高度问题。

原来在VCL下,直接是TCanvas.MeasureText(),就可以得到实际的文本高度TextHight。但是在FMX下,最好不用TCanvas来测量,虽然TCanvas提供了MeasureText()和TextHeight()方法,其中TextHeight()调用MeasureText(),MeasureText()调用了TTextLayout的属性TextRect。

但是要注意,我们改变ListItem的Font的时候,ListItem.Canvas的Font是不变的,还是默认大小。一般情况下,默认Font的大小(在win10下,如果不改系统默认字体大小,这个值是12)太小了,我们都会设置的大一些。所以要用TCanvas的MeasureText()或TextHeight()方法测量的TextHeight大小,测量前我们得先改变ListItem.Canvas的Font大小,但是TCanvas.Font属性是只读的,不能改变整个Font,只能是单独改变TFont的各个属性,比如Font.Family,Font.Size,Font.Color等,比较麻烦。看看TCanvas的代码:

.........

property Stroke: TStrokeBrush read FStroke;
property Fill: TBrush read FFill write SetFill;
property Font: TFont read FFont;
property Matrix: TMatrix read FMatrix;
property Width: Integer read FWidth;
property Height: Integer read FHeight;
property Bitmap: TBitmap read FBitmap;
property Scale: Single read FScale;

.........

Fill和Stroke倒是可读写的,Font是只读的。

再看看MeasureText()和TextHeight():

procedure TCanvas.MeasureText(var ARect: TRectF; const AText: string;
  const WordWrap: Boolean; const Flags: TFillTextFlags; const ATextAlign,
  AVTextAlign: TTextAlign);
var
  Layout: TTextLayout;
begin
  if AText.IsEmpty then
  begin
    ARect.Right := ARect.Left;
    ARect.Bottom := ARect.Top;
    Exit;
  end;

  Layout := TTextLayoutManager.TextLayoutByCanvas(Self.ClassType).Create(Self);
  try
    Layout.BeginUpdate;
    Layout.TopLeft := ARect.TopLeft;
    Layout.MaxSize := PointF(ARect.Width, ARect.Height);
    Layout.Text := AText;
    Layout.WordWrap := WordWrap;
    Layout.HorizontalAlign := ATextAlign;
    Layout.VerticalAlign := AVTextAlign;
    Layout.Font := Self.Font;
    Layout.Color := Self.Fill.Color;
    Layout.RightToLeft := TFillTextFlag.RightToLeft in Flags;
    Layout.EndUpdate;
    ARect := Layout.TextRect;
  finally
    FreeAndNil(Layout);
  end;
end;

function TCanvas.TextHeight(const AText: string): Single;
var
  R: TRectF;
begin
  R := RectF(0, 0, 10000, 10000);
  MeasureText(R, AText, False, [], TTextAlign.Leading, TTextAlign.Leading);
  Result := R.Bottom;
end;

 是通过TTextLayout来实现。

所以干脆不用这个名不副实的MeasureText(),自己用TTextLayout来实现好了。

写个通用的设置字体和自动计算Item高度的方法:

//ListBoxItem 的文本高度自动计算,以ListItem[0]为准,如果是每个ListItem不一样,建议用Style方式实现

procedure SetListBoxItemFontAndHeight(AControl: TFmxObject; AFontName: string; AFontSize: Single;
    AFontColor: TAlphaColor; AutoItemHeight: Boolean);
var
  i: Integer;
  AStyledSettings: TStyledSettings;
  aListBox: TListBox;
  LTextLayout: TTextLayout;
begin
  AStyledSettings := AllStyledSettings;
  if not AFontName.IsEmpty then
    AStyledSettings := AStyledSettings - [TStyledSetting.Family];
  if AFontSize > 8 then
    AStyledSettings := AStyledSettings - [TStyledSetting.Size];
  if AFontColor <> TAlphaColors.Black then
    AStyledSettings := AStyledSettings - [TStyledSetting.FontColor];

  aListBox := nil;

  if AControl is TListBox then
    aListBox := TListBox(AControl)
  else if AControl is TComboBox then
  begin
    TComboBox(AControl).DropDownKind := TDropDownKind.Custom;
    aListBox := TListBox(TComboBox(AControl).ListBox);
  end
  else
    Exit;

  if (aListBox = nil) or (aListBox.Count = 0) then
    Exit;

  for i := 0 to aListBox.Count-1 do
  begin
    aListBox.ListItems[i].StyledSettings := AStyledSettings;
    if not AFontName.IsEmpty then
      aListBox.ListItems[i].TextSettings.Font.Family := AFontName;
    if AFontSize > 8 then
      aListBox.ListItems[i].TextSettings.Font.Size := AFontSize;
    if AFontColor <> TAlphaColors.Black then
      aListBox.ListItems[i].TextSettings.FontColor := AFontColor;
  end;

  if not AutoItemHeight then
    Exit;

 //计算ItemHeight LTextLayout := TTextLayoutManager.DefaultTextLayout.Create(); try LTextLayout.BeginUpdate; try LTextLayout.Font := aListBox.ListItems[0].Font; LTextLayout.Text := aListBox.Items[0]; finally LTextLayout.EndUpdate; end; aListBox.ItemHeight := LTextLayout.Height; //TextHeight finally LTextLayout.Free; end; end;

  

posted @ 2021-03-08 22:52  舞天涯  阅读(575)  评论(0编辑  收藏  举报