Firemonkey TListView Удалить последний элемент в элементе списка - PullRequest
1 голос
/ 11 апреля 2020

Я использую dephi firemonkey при тестировании Rad Studio 10.3.2 на android 9

Я хочу удалить последний элемент из TListView. Но перед тем, как удалить его, я хочу запросить подтверждение, а затем удалить.

, для этого я построил приведенный ниже пример кода, он имеет 1 TListview, 2 кнопки быстрого запуска, 1 прямоугольник и 1 метку.

прямоугольник виден как ложный, поэтому, когда пользователь проведет пальцем по элементу списка, появится кнопка удаления. в кнопке удаления я отменю удаление и поставлю прямоугольник, видимый с вопросом, если нажать «да», то удалите элемент. проблема в том, что кнопка удаления списка просмотра никогда не исчезает go, и когда пользователь снова нажимает на экран, приложение делает sh.

следующие изображения, иллюстрирующие операцию

Перед проведите пальцем

После проведите пальцем

после клика удалить

после клика да

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.

1 Ответ

1 голос
/ 12 апреля 2020

Вы делаете это неправильно.

Цель события ListView OnDeletingItem состоит в том, чтобы управлять возможностью удаления элемента ListView. Это должно быть сделано во время выполнения этого события, предпочтительно путем показа модального диалогового окна для подтверждения в этом событии.

Но в вашем коде вы возвращаете ACanDelete как ложное и, следовательно, уведомляете ListView, что он не может удалить этот предмет. Позже вы попытаетесь удалить указанный элемент c ListView из вашего собственного кода с помощью кнопки быстрого запуска в этом вашем прямоугольнике. Проблема заключается в том, что к тому времени, когда пользователи нажимают на любую из этих кнопок быстрого доступа, содержимое ListView, возможно, уже изменилось, поскольку после выхода из события OnDeletinItem ваше приложение возобновило нормальное выполнение.

Поэтому я настоятельно рекомендую вместо этого используя прямоугольник с кнопками, вы используете модальное диалоговое окно для получения подтверждения от вашего пользователя, так как с помощью модального диалогового окна вы гарантируете, что до закрытия модального диалогового окна остальная часть прогрессирования кода будет остановлена, и, таким образом, содержимое ListView не может измениться до тех пор.

Документация по событию ListView OnDeletingItem также предоставляет небольшой пример кода для показа такого модального диалога в таком сценарии, поэтому вы обязательно должны его проверить.


РЕДАКТИРОВАТЬ: Потратив много часов на изучение этой проблемы, мне удалось отследить ее причину, но, к сожалению, я не знаю, как ее легко решить.

Первая часть проблемы заключается в том, что Android не поддерживает истинные модальные диалоги. Таким образом, вам необходимо реализовать дополнительное подтверждение для вашего пользователя синхронно, либо с помощью синхронного диалога, либо с помощью какого-либо пользовательского способа, как вы пытались.

Но теперь мы подошли ко второй части проблемы, которая заключается в плохом дизайне TListView. компонент в части Embarcadero.

Вы видите, когда при помощи жеста смахивания показывает кнопку удаления, кнопка удаления создается не как часть указанного элемента c, а как часть класса класса TListViewBase, из которого в конечном итоге создается TListView. полученный из. Также поле с именем FDeleteButtonIndex устанавливается с порядковым номером элемента, для которого был выполнен жест смахивания. Поскольку эта кнопка Delete объявлена ​​и создана так глубоко внутри класса TListViewBase, получить прямой доступ к ней невозможно, поскольку она помечена как частная.

Теперь, когда вы нажимаете на эту кнопку Delete, специальный метод события DeleteButtonClicked выполняется, и в этом методе кнопка «Удалить» уничтожается, и FDeleteButtonIndex устанавливается на -1.

Но при удалении элемента ListView из кода кнопка «Удалить» не уничтожается и поле FDeleteButtonIndex не устанавливается до -1. Это означает, что если вы нажмете эту кнопку Удалить во второй раз, TListView будет go и удалит элемент с тем же индексом, что и предыдущий. И если вы ранее удалили последний элемент, то теперь вы получите нарушение прав доступа при попытке получить доступ к элементу, который находится за пределами вашего ListView.

Поэтому я боюсь, что у меня нет простого решения для вас , Вы можете попробовать:

  • Создайте свой собственный пользовательский класс для TListView как способ сделать необходимые методы доступными c, но вам придется сделать 5 пользовательских, поскольку TListVievBase имеет пять уровней сравнение с TListView.
    • Вы можете попробовать использовать некоторые хаки TRL для доступа к закрытым методам TListViewBase class
    • Вы можете отключить функциональность SwipeToDelete по умолчанию и реализовать свою собственную, с помощью которой вы создадите собственную кнопку Delete во время выполнения таким образом, что он является частью ListItem и поэтому уничтожается с помощью ListItem
    • Или вы можете запросить Embarcadero исправить TListView, чтобы он всегда проверял, присутствует ли кнопка Удалить для этот указанный c элемент, который вы удаляете программно или добавляете в TListView другой метод, который позволил бы нам удалять эту кнопку «Удалить» в любое время.

Извините, я могу лучше помочь

...