(原)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;
浙公网安备 33010602011771号