【问题标题】:Firemonkey TListView Delete Last Item on List Item Swipe CrashFiremonkey TListView 删除列表项上的最后一项滑动崩溃
【发布时间】:2020-04-11 17:48:34
【问题描述】:

我在 Rad Studio 10.3.2 上使用 dephi firemonkey 在 android 9 上进行测试

我想从 TListView 中删除最后一个项目。但在我删除它之前,我想要求确认,然后删除。

为此,我构建了下面的示例代码,它有 1 个 TListview、2 个 Speedbuttons、1 个矩形和 1 个标签。

矩形是可见的,所以当用户滑动 listviewitem 时,它会显示删除按钮。在删除按钮中,我将取消删除并将矩形显示在问题所在的位置,如果单击是则删除该项目。问题是 listviewitem 删除按钮永远不会消失,当用户再次单击屏幕时应用程序崩溃。

以下图片说明操作

Before swipe

After swipe

after click delete

after click yes

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.ListView.Types, FMX.ListView.Appearances, FMX.ListView.Adapters.Base,
  FMX.ListView, FMX.StdCtrls, FMX.Controls.Presentation, FMX.Objects;

type
  TForm1 = class(TForm)
    ListView1: TListView;
    Rectangle1: TRectangle;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure ListView1DeletingItem(Sender: TObject; AIndex: Integer;
      var ACanDelete: Boolean);
    procedure SpeedButton1Click(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  ItemDelete  : Integer;

implementation

{$R *.fmx}

procedure TForm1.FormCreate(Sender: TObject);
var
  Item: TListViewItem;
begin
  Item := ListView1.Items.Add();
  Item.Text := 'Item 1';
  Item := ListView1.Items.Add();
  Item.Text := 'Item 2';
  Item := ListView1.Items.Add();
  Item.Text := 'Item 3';
  Item := ListView1.Items.Add();
  Item.Text := 'Item 4';
end;

procedure TForm1.ListView1DeletingItem(Sender: TObject; AIndex: Integer; var ACanDelete: Boolean);
begin
  ACanDelete := false;
  ItemDelete := AIndex;
  Rectangle1.Visible := true;
end;

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  ItemDelete := -1;
  Rectangle1.Visible := false;
end;

procedure TForm1.SpeedButton2Click(Sender: TObject);
begin
  ListView1.Items.Delete(ItemDelete);
  ItemDelete := -1;
  Rectangle1.Visible := false;
end;

end.

【问题讨论】:

  • 如果我忽略滑动并单击 SpeedButton2,ItemDelete 已被编译器初始化为零,因此列表中的第一项(第 1 项)将在没有调用 ListView1DeletingItem 的情况下被删除,并且我没有 AV。
  • 不管问题出在哪里,你真的需要3个步骤(滑动、点击删除、点击是)来完成破坏性动作吗?另一点是在这样的对话框中是/否aren't proper option names
  • 上面的代码只是一个示例。在我的实际应用程序中,代码更加谨慎。是的,我需要在删除前确认。我的问题是当我删除该项目时。我认为问题是单击删除后,该项目应该回到原始表单,在滑动之前,但它没有。
  • @AndreNogueira :有点晚了,只是部分与您关于带有 Delete Button 的 AV 的问题有关。在 Android 上使用 Delphi 10.4.2。我有一个保存数据的数组。我有一个过程,然后根据这个数组填充 Listview。然后将 Listview.ItemIndex 设置为 ItemCount。如果添加项目,这很好用。但是当我删除最后一个项目时,我得到一个访问冲突,在调试后导致删除滑动按钮本身。我所做的是在 onDeleteChangeVisible 内删除时设置索引,否则在 onItemChange 内删除。没有 AV。
  • 感谢您的帮助,但现在我工作的公司停止使用 delphi for mobile。我们搬到了颤振。我不得不说,这是一次更好的体验。现在移动设备上的 delphi 只能在遗留应用程序上使用,直到我在 Flutter 上编码

标签: android delphi firemonkey tlistview


【解决方案1】:

你做错了。

ListView OnDeletingItem event的目的是控制是否可以删除ListView项。这应该在该事件的执行时间内完成,最好通过在该事件中显示一个用于确认的模式对话框来完成。

但在您的代码中,您将 ACanDelete 返回为 false,因此通知 ListView 它无法删除该项目。稍后,您尝试使用该矩形中的速度按钮从您自己的代码中删除特定的 ListView 项目。问题是,当用户单击任何这些速度按钮时,ListView 的内容可能已经发生了变化,因为您退出 OnDeletinItem 事件后,您的应用程序恢复正常执行。

所以我强烈建议不要使用带有按钮的矩形,而是使用模态对话框来获得用户的确认,因为通过使用模态对话框可以确保在模态对话框没有关闭之前,其余的代码进程会停止,因此内容ListView 在那之前不能改变。

ListView OnDeletingItem event 上的文档还提供了一个小代码示例,用于在这种情况下显示这种模式对话框,所以你一定要检查一下。


编辑:在花了很多时间研究这个问题之后,我设法找到了它的原因,但不幸的是我不知道如何轻松解决它。

问题的第一部分是 Android 不支持真正的模式对话框。因此,您需要通过使用同步对话框或尝试使用一些自定义方式来同步向您的用户实现额外的确认。

但是现在我们来到问题的第二部分,即 Embarcadero 部分的 TListView 组件设计不佳。

当您使用滑动手势显示删除按钮时,您会看到删除按钮不是作为特定项目的一部分创建的,而是作为 TListView 最终派生自的 TListViewBase 类的一部分。此外,一个名为FDeleteButtonIndex 的文件被设置为执行滑动手势的项目的索引号。因为这个 Delete 按钮是在 TListViewBase 类中声明和创建的,所以无法直接访问它,因为它被标记为私有。

现在,当您单击删除按钮时,将执行一个特殊的事件方法 DeleteButtonClicked,在此方法中删除按钮被销毁,FDeleteButtonIndex 设置为 -1。

但是,当您从代码中删除 ListView 项目时,“删除”按钮不会被破坏,FDeleteButtonIndex 字段也不会被设置为 -1。这意味着如果您第二次单击该删除按钮,TListView 将删除与之前具有相同索引的项目。如果您之前删除了最后一个项目,您现在将因尝试访问超出 ListView 的项目而受到访问冲突。

所以恐怕我没有任何简单的解决方案适合您。你可以试试:

  • TListView 创建您自己的自定义类,作为公开所需方法的一种方式,但您必须自定义5 个,因为TListVievBaseTListView 相比有五个级别。
    • 您可以尝试使用一些 TRL hack 来访问 TListViewBase 类的私有方法
    • 您可以禁用默认的SwipeToDelete 功能并实现您自己的功能,您将使用该功能在运行时创建您自己的删除按钮,使其成为 ListItem 的一部分,因此会被 ListItem 销毁
    • 或者您可以请求 Embarcadero 修复 TListView,以便它始终检查您正在以编程方式删除的特定项目是否存在删除按钮,或者向 TListView 添加另一个方法,以便我们删除该删除随时按按钮。

对不起,我帮不上忙

【讨论】:

  • 是的,在 rad studio 10.3 之前,我在删除按钮中有一个对话框。但是从 10.3 开始,来自 embarcadero 的对话示例停止工作,这就是我的解决方案。你还有其他想法吗?还是适用于 10.3.2 的问题对话框示例?
  • 我已经用一些额外的信息编辑了我的原始答案,但恐怕我没有一个简单的解决方案给你。
  • 感谢您的努力,您给了我足够多的选择,我想我会尝试联系 embarcadero。现在我有一个解决方法,它并不漂亮,但它有效并且给了我更多的时间。在我的列表中,我在底部添加了一个空白项目,并禁用了该项目的 onclick / ondelete 代码。结果是底部有一个空白项,用户无法对其进行任何操作,因此他们永远无法删除最后一项。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-14
  • 2015-07-23
  • 1970-01-01
  • 2018-09-03
  • 1970-01-01
  • 2022-01-19
  • 1970-01-01
相关资源
最近更新 更多