Блог Александра Божко
Архивы
Рубрики
Поделись с другими!
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

TListView является одним из ключевых компонентов для построения интерфейса мобильного приложения в FireMonkey. Компонент этот не самый простой в использовании, зачастую предполагает значительный объем кода, зато предоставляет разработчику значительную свободу действий. Конечно, в приложениях можно использовать и TListBox, где все намного проще. Но TListBox, возможно, хорош для отображения фиксированного количества записей,  для вывода данных из источников данных, однозначно нужно использовать  TListView.

Цитирую Ярослава Бровина:

Главные отличия TListView от TListBox в:

  1. TListBoxItem - контрол, TListViewItem - нет
  2. В TListBoxItem можно добавлять любые контролы, используя Parent. В TListVIewItem - нет.
  3. TListVIewItem хранит только данные для отображения
  4. TListVIewItem сам выполняет отрисовку хранимых данных через метод Render
  5. За счет собственно ручной отрисовки в TListVIewItem достигается прирост скорости и малое потребление памяти (хранение только актуальных данных)
  6. Чтобы создать свой вариант TListViewItem, нужно создать свой класс итема, в нем реализовать требуемые данные (например время) и создать in-place редактор для редактирования времени, зарегистрировать его и т. д.

 

Сам по себе факт повышения производительности и уменьшения потребления памяти – веский аргумент в пользу использования TListView. Но есть и еще кое что.

Во многих Android приложениях мне приходилось наблюдать следующую реализацию списков. При нажатии на элемент списка (Item, если придерживаться выбранной терминологии), производится определенное действие. Обычно вызывается новая форма для редактирования данных. Но при нажатии с удерживанием (Long Tap) производится совершенно другое действие. И эти события не пересекаются. Иными словами Android приложения умеют четко различать “длинное нажатие” от “обычного”. Более того, ни одно из этих событий не срабатывает при скроллинге списка. Наглядный пример – список писем в Яндекс Почта.

При попытке реализовать подобный функционал с помощью TListBox, я получил чехарду из накладывающихся друг на друга событий. Главной проблемой оказалось отделение скроллинга от обычного нажатия. В TListView, как оказалось, с этим проблем нет. Но правильно определить события – тоже задача довольно интересная. Дабы сэкономить время своим читателям, я поделюсь здесь результатами своих экспериментов.

Итак, я заполняю список данными из набора данных следующим образом:

procedure TForm1.InitTiming;
var
  LItem, iHeader: TListViewItem;
  I: Integer;
  curDate: TDate;
  im: TListItemImage;

  procedure AddHeader(aDate: TDate);
  begin
    iHeader := ListView1.Items.Add;
    iHeader.Text := DateToStr(aDate);
    iHeader.Purpose := TListItemPurpose.Header;
  end;

begin
  ListView1.BeginUpdate;
  ListView1.Items.Clear;

  try
    while not UniQTimeSheets.Eof do
    begin

      if curDate <> DateOf(UniQTimeSheetsTimeFrom.AsDateTime) then
        AddHeader(UniQTimeSheetsTimeFrom.AsDateTime);

      LItem := ListView1.Items.Add;
      LItem.Text := 'From: ' + FormatDateTime('t',
        UniQTimeSheetsTimeFrom.AsDateTime) + '          Hours: ' +
        HoursMinutesFromMin(UniQTimeSheetsHourse.AsInteger, ':');

      LItem.Tag := UniQTimeSheetsID.AsInteger;
      LItem.Data[TMultiDetailAppearanceNames.Detail] :=
        UniQTimeSheetsCompanyName.AsString;

      curDate := DateOf(UniQTimeSheetsTimeFrom.AsDateTime);
      UniQTimeSheets.Next;

    end;
  finally
    ListView1.EndUpdate;
  end;
end;

 

По “обычному” нажатию я хочу отобразить Id записи (Tag элемента списка), по длинному нажатию я хочу перевести режим редактирования (слева будут отображаться CheckBox’ы). Очевидно, что для того, что бы “отловить” длинное нажатие следует обработать событие OnGuesture.

procedure TForm1.ListView1Gesture(Sender: TObject;
  const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
  try
    if EventInfo.GestureID = System.UITypes.igiLongTap then
    begin
      if not ListView1.EditMode then
      begin
        ListView1.EditMode := True;
      end;
    end;

  except
  end;

end;

Соответственно, на OnClickEx вешаем следующий код:

procedure TForm1.ListView1ItemClickEx(const Sender: TObject; ItemIndex: Integer;
const LocalClickPos: TPointF; const ItemObject: TListItemObject);

begin
Memo1.Lines.Add('ItemClickEx Start');
 if (not ListView1.EditMode) and blnCanShowForm then
 begin
  ShowMessage(IntToStr(ItemIndex));
 end;

end;

Если запустить этот код под Windows на ПК с мультитач экраном, он прекрасно отработает. Но вот под Android. увы, при длинном нажатии сначала будет выведен Id элемента, а потом ListView благополучно перейдет в режим редактирования.

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

Memo1.Lines.Add(<Название события>);

Сразу оговорюсь, что я знаю о механизмах логирования в Android и о CodeSite Studio. Но, выбрал столь архаичный метод логирования ввиду простоты и универсальности. Впрочем, логирование для Android – совсем другая история.

Что же касается результатов. Под Windows при обычном нажатии я получил следующую последовательность событий:

  • Tap
  • Click
  • ItemClick
  • Change
  • ItemClickEx

 При Long Tap она выглядит так:

  • Gesture
  • EditModeChanging
  • EditModeChange
  • Tap

Совершенно очевидно, что предложенная выше обработка событий без проблем будет работать под Windows. При обычном нажатии срабатывает событие OnCahnge, а затем выполняется обработчик ItemClickEx.

В случае Long Tap, ни ItemClick ни ItemClickEx не выполняются, что позволяет обрабатывать длинное нажатие абсолютно независимо. Обращу внимание на то, что здесь идет речь именно о нажатии, а не о клике мышкой.

Как же обстоят дела в мобильном приложении?

При обычном нажатии все точно так же:

  • Tap
  • Click
  • ItemClick
  • Change
  • ItemClickEx

А вот при длинном нажатии совершенно другая картина:

  • ItemClick
  • ItemClickEx
  • Gesture
  • EditModeChanging
  • EditModeChange
  • Tap
  • Click
  • Change

 

Перед обработчиком жеста вызывается OnItemClickEx. таким образом два события. которые должны быть независимы, накладываются друг на друга. Конечно, вполне можно бы было обработать обычное нажатие с помощью события OnTap. Но в этом случае обработчик будет вызван до на новый Item и мы не сможем узнать интересующий нас Id .

Но, как вы понимаете, при наличии наглядной последовательности событий, разрешение данной задачи не является великой алгоритмической проблемой. Самый примитивный вариант – ввести “флажок”, и с его помощью в обработчике события OnTap разрешать выводить Id, а в OnEditModeChange и OnClickEx – сбрасывать флажок.

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

 

procedure TForm1.ListView1Gesture(Sender: TObject;
  const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
  try
    Memo1.Lines.Add('ListView1Gesture');

    if EventInfo.GestureID = System.UITypes.igiLongTap then
    begin
      if not ListView1.EditMode then
      begin
        ListView1.EditMode := True;
        Memo1.Lines.Add('igiLongTap');

      end;
    end;

  except
  end;
  
end;

procedure TForm1.ListView1Tap(Sender: TObject; const Point: TPointF);
begin
  Memo1.Lines.Add('Tap');
  blnCanShowForm := True;

end;

procedure TForm1.ListView1EditModeChange(Sender: TObject);
begin
  blnCanShowForm := False;
  Memo1.Lines.Add('EditModeChange');
end;
procedure TForm1.ListView1ItemClickEx(const Sender: TObject; ItemIndex: Integer;
  const LocalClickPos: TPointF; const ItemObject: TListItemObject);

begin
  //

  Memo1.Lines.Add('ItemClickEx Start');
  if (not ListView1.EditMode) and blnCanShowForm then
  begin
    ShowMessage(IntToStr(ItemIndex));
  end;

  blnCanShowForm := False;
  Memo1.Lines.Add('ItemClickEx Finish');
end;

Хотите сэкономить? До 30 сентября у вас есть возможность выгодно купить Windows 10.

Хотите выгодно приобрести новую ОС?  Подсказываем: в течение первого года после выхода новой ОС, до 29 июля 2016 года, все пользователи Windows 7 и Windows 8.1 могут бесплатно установить Windows 10 на свои ПК. Таким образом, вы можете выгодно приобрести Windows 8.1 и бесплатно перейти на Windows 10:

Microsoft Windows 8.1 Home за 4990 рублей

Microsoft Windows 8.1 Professional за 8990 руб.

780_198_11

 

 


Поделись с другими!
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

1 комментарий: FireMonkey на практике. TListView.

  • Спасибо! Отличная статья, выручила по времени :-) Ни как не мог разобраться как добраться до хедоров и поменять значения.

Оставить комментарий

Ваш email не будет опубликован. Обязательные поля отмечены *

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Продукты DevArt
Купить онлайн:



Читай русскоязычные Delphi блоги
Каталог блогов Blogdir.ru
Яндекс.Метрика