【问题标题】:Custom drawing of TCustomListbox itemsTCustomListbox项的自定义绘制
【发布时间】:2018-10-11 14:27:19
【问题描述】:

我正在为 Delphi 10.2 中的 Firemonkey 重写一个显示自定义 TCustomListbox 的 VCL 组件。自定义使用了覆盖的DrawItem,基本上添加了一些缩进并根据项目文本和索引设置文本颜色。

DrawItem 让它变得相当容易,但在 FMX 中似乎没有这样的东西。我可以覆盖PaintChildren 并自己绘制每个项目,但它看起来会有所不同,我必须自己处理滚动和所有事情。我刚开始使用 FMX 和 don't have the sources yet

  • 在 FMX 中是否有 DrawItem 替换?我可能错过了。

  • 如果没有,它如何获得所需的信息?基本上,要绘制的矩形和理想的样式。

问题

Hans 的解决方案可行,但存在一些主要问题:

颜色

设置颜色不起作用,文本总是黑色的。我尝试了各种可能性,包括这个:

PROCEDURE TMyItem.Paint;
BEGIN
  TextSettings.FontColor := TAlphaColorRec.Red;
  INHERITED;
END;

速度

打开一个装有 180 件物品的盒子可能需要两秒钟。我们需要这么多项目,它们的数量实际上是我们需要自定义框的原因(我们使用组件的TEdit 部分提供过滤)。使用不带TMyItem 的字符串的版本更快(虽然可能比VCL 版本慢),但使用这些项目似乎更慢(它比填充类似样式的HTML 列表慢)。

还是别的什么?没有来源,几乎没有我无法分辨的文档。

我试图缓存这些项目以供重复使用,但这没有帮助。

看起来使用自定义项实际上比使用字符串更快,(以毫秒为单位):

nItems String TMyItem
   200    672      12
  2000   5604     267
 20000  97322   18700

当内容多次更改时,速度问题似乎会累积。我用的是FListBox.Items.Clear;,然后我试了

n := FListBox.Items.Count;
FOR i := 0 TO n-1 DO FListBox.ListItems[n-1-i].Free;

最后是FListBox.Clear;,这是最有意义的(也是我最后发现的)。尽管如此,最后似乎每个项目都需要 2 毫秒。

【问题讨论】:

  • 在 FMX 上,我以不同的方式完成了它:覆盖 TListBoxItem,我通过覆盖 Paint 方法添加自定义绘图并在 Create 方法中添加其他控件(例如文本/标签)。跨度>
  • @J... 我的意思是“Embarcadero® Delphi 10.2 版本 25.0.29899.2631”,因为它在“关于”中写道,已修复。无论如何,我对他们的编号感到很困惑(但这不是唯一的事情)。
  • @Hans 这会解决它,但我怎样才能让TCustomListbox 使用TMyListBoxItem
  • FMX 中总有一种风格。该样式绘制控件的可视部分。如果不指定样式,则使用默认样式。您可以轻松添加自己的样式。如果您的所有列表框项目都相同,那么您可以为ListBoxItem 定义自己的样式。我已经为我的一些项目做到了这一点,但我更喜欢我的答案中的解决方案,因为它非常灵活。
  • 没有firemonkey的源代码,你几乎没有机会做出好的东西,我什至不知道没有源代码的firemonkey怎么可能做任何事情。我最好的选择是简单地放置一个自定义 TvertScrollBox 并在其中放置许多 TLayout 并在 tlayout.paint 内完成您需要的一切。如果您担心速度,请使用 Alcinoe 控件 (github.com/Zeus64/alcinoe)

标签: delphi firemonkey vcl delphi-10.2-tokyo


【解决方案1】:

这是一个如何完成的示例。关键是将(自定义)ListBoxItemParent设置为ListBox。这会将其附加到其项目列表中。我在构造函数中设置了父级,因此每次向列表框添加内容时不必这样做(并记住它)。

type
  tMyListBoxItem = class(TListBoxItem)
  strict private
    fTextLabel: TLabel;
  public
    constructor Create(aOwner: TComponent);
    property TextLabel: TLabel read fTextLabel;
  end;

implementation

constructor tMyListBoxItem.Create(aOwner: TComponent);
begin
  inherited;
  fTextLabel := TLabel.Create(self);
  fTextLabel.Parent := self;
  Assert(aOwner is TFMXObject, 'tMyListBoxItem.Create');
  Parent := TFMXObject(aOwner);
end;

procedure tMyForm.FillListBox(aListBox: TListBox; aStringList: TStringList);
var
  lItem: tMyListBoxItem;
  i: integer;
begin
  aListBox.BeginUpdate; //to avoid repainting for every item added
  aListBox.Clear;
  for i := 0 to aStringList.Count-1 do
  begin
    lItem := tMyListBoxItem.Create(aListBox);
    lItem.TextLabel.Text := aStringList[i];
    lItem.Margins.Left := 20;
  end;
  aListBox.EndUpdate;
end;

我现在在很多地方都使用自定义 ListBoxItem,因为您可以在 ListboxItem 中拥有 ComboBox、EditBox 和所有其他控件。这将打开一个非常动态(基于列表)的屏幕布局,可以轻松适应所有平台和屏幕尺寸。

【讨论】:

  • 这看起来不错,但这意味着我不能使用标准的TListBox 方法来添加项目,不是吗?
  • 标准函数使用内部 TStrings 实例,您可以通过 ListBox.Items 属性对其进行修改。使用我制作的解决方案,您只需修改自己的 TStrings 实例,然后在完成后将其应用到 ListBox。它没有那么干净,但至少它可以让你实现你想要的。
  • 如果需要在列表中的特定位置插入项目,可以使用 TListBox 的InsertObject 方法(最终也可以通过AddObject 追加)。我的答案使用 TStringList ,因为它接近原始行为,但我自己不使用它。相反,我直接更新 ListBox。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-09
相关资源
最近更新 更多