【问题标题】:Drawing unicode text on listbox canvas is too slow在列表框画布上绘制 unicode 文本太慢
【发布时间】:2016-01-13 23:22:07
【问题描述】:

我正在尝试使用以下格式在列表框中显示来自 RSS 的新闻,如下图所示。屏幕截图上的应用程序是通过设置列表框样式在 firemonkey 中开发的。我需要在我的 VCL 应用程序中显示相同的内容。

这种布局的要求是:

  • 新闻标题应为粗体
  • 简短的描述应该位于底部,并且应该是 如果它不适合单行,则包裹(如图所示); font-style 应该是正常的
  • 每条新闻都应该有一张图片

到目前为止我的代码:

procedure TfrmDatePicker.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
  R: TRect;
begin
  ListBox1.Canvas.Font.Color := clBlack;
  ListBox1.Canvas.Font.Style := [fsBold];

  ListBox1.Canvas.Font.Size := 9;

  if Odd(Index) then ListBox1.Canvas.Brush.Color := clWhite
  else ListBox1.Canvas.Brush.Color := clBtnFace;

  ListBox1.Canvas.FillRect (Rect);
  ListBox1.Canvas.Pen.Color := clHighlight;

  if(odSelected in State) then
  begin
      ListBox1.Canvas.Font.Color := clHighlightText;
      ListBox1.Canvas.Brush.Color := clHighlight;
      ListBox1.Canvas.Rectangle(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
      if(odFocused in State) then DrawFocusRect(ListBox1.Canvas.Handle, Rect);
  end;

  ImageList1.Draw(ListBox1.Canvas, Rect.Left + 2,
          Rect.top + (ListBox1.ItemHeight - ImageList1.Height) div 2, Index, true);


  ListBox1.Canvas.TextOut(Rect.Left + 70, Rect.Top + 4, 'कान्तिपुर समाचारआजकोपत्रिकामाकेहिछैन');

  ListBox1.Canvas.Font.Style := ListBox1.Canvas.Font.Style - [fsBold];

  R := Rect;
  R.Left := R.Left + 70;
  R.Top := R.Top + 32;
  R.Height := 30;

  DrawText(ListBox1.Canvas.Handle, PChar(ss), Length(ss), R, DT_LEFT or DT_WORDBREAK or DT_NOPREFIX);
  ListBox1.Canvas.TextOut(Rect.Right - 80, Rect.top + 4, '5 mins ago');
end;

这是我得到的输出:

问题

Unicode 文本绘制速度太慢,并且在滚动列表框或调整表单大小时闪烁过多。

注意

  • 字体已设置为 @Microsoft NeoGothic
  • 项目高度=70; style = ownerdrawfixed
  • 在中绘制相同的unicode文本没有问题 firemonkey 应用程序发布在第一个屏幕截图中。
  • 上面发布的代码对于普通的英文文本和 根本没有闪烁。问题只存在于 Unicode 文本。

更新: 似乎问题出在 DrawText 方法的 DT_WORDBREAK 标志中。每当我删除此标志时,虽然闪烁可见,但在绘制文本时会有显着改进。

示例 Unicode 文本

तिम्रोतिम्रोत्योबोलिबोलिलेमलाईमिठोतिम्रोत्योमुस्कानमुस्कानमाझुलायोझुलाओह्स्द्जिःसह्स्ध्फद्जह्स्ध्फद्जद्श्जड्सफगस्द्फ़गस्द्फ्गफसफसग्स्द्फ़ग्स्द्फ़ग्दस्फ्गत्योबोलिलेमलाईमलाईबोलायोबोलायोमिठोमुस्कानमामलाईसह्स्ध्फद्जह्स्ध्फद्जह्स्ध्फद्जह्स्ध्फद्जह्स्ध्फद्जह्स्ध्फद्जह्स्ध्फद्जह्स्ध्फद्जसगग फस ग्स्द्फ़ ग्दस्फ्ग द्स्फग्द

【问题讨论】:

  • 我很困惑为什么您要构建一个与 FMX 应用程序分开的 VCL 应用程序。我很好奇你为什么需要这样做?这只是为了测试吗?
  • @JerryDodge 实际上,我正在编写一个具有多种功能的 VCL 应用程序。新闻阅读器是其中之一,仅待实施。我试图搜索可以显示所有重要部分的自定义绘图。第一次尝试是stackoverflow.com/questions/33057500/…,但我无法得到正确的答案。我猜,为这个单一模块混合 firemonkey 不是一个好主意。
  • 好吧,我的意思是,如果您首先在 Firemonkey 中编写此代码,我假设您这样做是为了实现多平台。好吧,Windows 是受支持的平台之一。为什么不只保留一个代码库?
  • 我正在为使用 VCL 的 Windows 编写此应用程序。您看到的 firemonkey 屏幕截图实际上是一个测试应用程序。我在玩列表框样式,没有别的。
  • 好吧,那是有道理的。我假设您正在构建同一事物的 FMX 和 VCL 版本。

标签: delphi winapi delphi-xe8 drawtext tlistbox


【解决方案1】:

如果你真的真的很想使用标准的列表框来显示你的 RSS 提要,我建议你使用双缓冲。这意味着您将您的东西绘制在内存中的位图上,然后将其绘制给您的 listView。从您的源代码中,我制作了一个小型演示,向您展示如何做。我没有解决所有问题,但我相信这是您使用标准 VCL 组件可以获得的最佳效果。

unit Unit12;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ImgList;

type
  TForm12 = class(TForm)
    ListBox1: TListBox;
    ImageList1: TImageList;
    procedure ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
    procedure FormCreate(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    MemBitmap: TBitmap;
    OldListBoxWP: TWndMethod;
    procedure NewListBoxWP(var Message: TMessage);
  public
    { Public declarations }
  end;

var
  Form12: TForm12;

implementation

{$R *.dfm}

const
  NewsStr = 'तिम्रो त्यो बोलि ले मलाई बोलायो मिठो तिम्रो त्यो मुस्कान मा मलाई झुलायो झुल' +
    'ाओ ह्स्द्जिः स ह्स्ध्फद्ज द्श्जड्स हस फग स्द्फ़ ग स्द्फ्ग फस ग्स्द्फ़ ग्दस्फ्ग द्स्फग्द तिम्रो त्यो बोलि ले मलाई बोलायो मिठो तिम्रो त्यो मुस्कान मा मलाई स ह्स्ध्फद्ज द्श्जड्स हस फग स्द्फ़ ग स्द्फ्ग फस ग्स्द्फ़ ग्दस्फ्ग द्स्फग्द';

procedure TForm12.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ListBox1.WindowProc := OldListBoxWP;
  MemBitmap.Free;
end;

procedure TForm12.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  OldListBoxWP := ListBox1.WindowProc;
  ListBox1.WindowProc := NewListBoxWP;
  MemBitmap := TBitmap.Create;
  MemBitmap.SetSize(Width, Height);

  ListBox1.Items.BeginUpdate;
  for i := 0 to 10 do
    ListBox1.Items.Add(NewsStr);
  ListBox1.Items.EndUpdate;
end;

procedure TForm12.FormResize(Sender: TObject);
begin
  MemBitmap.SetSize(Width, Height);
end;

procedure TForm12.ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
  R: TRect;
begin
  MemBitmap.Canvas.Font.Color := clBlack;
  MemBitmap.Canvas.Font.Style := [fsBold];

  MemBitmap.Canvas.Font.Size := 9;

  if Odd(Index) then
    MemBitmap.Canvas.Brush.Color := clWhite
  else
    MemBitmap.Canvas.Brush.Color := clBtnFace;

  MemBitmap.Canvas.FillRect(Rect);
  MemBitmap.Canvas.Pen.Color := clHighlight;

  if (odSelected in State) then
  begin
    MemBitmap.Canvas.Font.Color := clHighlightText;
    MemBitmap.Canvas.Brush.Color := clHighlight;
    MemBitmap.Canvas.Rectangle(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
    if (odFocused in State) then
      DrawFocusRect(MemBitmap.Canvas.Handle, Rect);
  end;

  ImageList1.Draw(MemBitmap.Canvas, Rect.Left + 2, Rect.Top + (ListBox1.ItemHeight - ImageList1.Height) div 2, Index, true);
  MemBitmap.Canvas.TextOut(Rect.Left + 70, Rect.Top + 4, 'कान्तिपुर समाचारआजकोपत्रिकामाकेहिछैन');

  MemBitmap.Canvas.Font.Style := MemBitmap.Canvas.Font.Style - [fsBold];

  R := Rect;
  R.Left := R.Left + 70;
  R.Top := R.Top + 32;
  R.Height := 30;

  DrawText(MemBitmap.Canvas.Handle, PChar(NewsStr), Length(NewsStr), R, DT_LEFT or DT_WORDBREAK or DT_NOPREFIX);
  MemBitmap.Canvas.TextOut(Rect.Right - 80, Rect.Top + 4, '5 mins ago');

  BitBlt(ListBox1.Canvas.Handle, Rect.Left - 1, Rect.Top - 1, Rect.Right - Rect.Left + 2, Rect.Bottom - Rect.Top + 2, MemBitmap.Canvas.Handle, Rect.Left - 1, Rect.Top - 1, SRCCOPY);
end;

procedure TForm12.NewListBoxWP(var Message: TMessage);
begin
  if Message.Msg = WM_ERASEBKGND then
    Message.Result := 0
  else
    OldListBoxWP(Message);
end;

end.

【讨论】:

  • 不幸的是,这并没有解决问题。和以前一样的问题。除了使用标准列表框之外,还有其他可用的选项吗?顺便说一句,我尝试为表单和列表框设置 doublebuffered := true ,也只为列表框设置。
  • 我知道它没有解决问题。我只是说这是使用标准列表框可以获得的最佳效果。我看到很多人在谈论 VirtualTreeView (soft-gems.net/index.php/controls/virtual-treeview) 我没有自己测试它,因为我拥有 Developer Express 组件的许可证。但 VirtualTreeView 或 TcxGrid 是有效的替代方案。
  • 是时候结束这个问题了?
  • @JensBorrisholt 如果要谈论 DevEx,那么 cxTreeList 是更接近的选择但是 DevEx 组件本身非常复杂。而且突然不太灵活,一年前我不得不修补源来实现简单的排序......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-23
  • 2021-10-27
  • 1970-01-01
  • 2017-07-27
  • 2012-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多