Перейти к содержанию
Fire Monkey от А до Я
  • 0

ListView фильтрация здорового человека (не курильщика)


Евгений Корепов

Вопрос

Возникла задача поиска (фильтрации) в ListView по нескольким критериям. К примеру нужно искать по Item.Text, Item.Detail и Item.Date['Category']. Пошел путем, ранее подсказанным на этом форуме - правкой "function TListViewItems.ApplyFilter: Boolean" в FMX.ListView.pas. Передавать строку поиска хотел в строке типа "Text=Пеньки&Detail=по три рубля&Category=Осиновые", но быстро понял что код превращается в одноразовую кашу, а хотелось сделать красиво и на будущее.

Сделал так - добавил еще один тип "TFilterPredicateEx = TPredicate<TListViewItem>;", к существующему "TFilterPredicate = TPredicate<string>;" в TListViewItems. Ну и далее по списку добавил аналоги к переменным, функциям и property.

Смысл в том чтобы вместо обычного бутылочного горлышка в фильтации:

  ListView.Items.Filter:=
    function(X: string): Boolean
    begin
      Result := AFilterText.IsEmpty or X.ToLower.Contains(AFilterText.ToLower);
    end;

иметь мощный инструмент с доступом ко всем плюшкам TListViewItem. Теперь это выглядит вот так:

type
  TListViewFilterEx = record
    Category : String;
    Name : String;
  end;
...
Var AFilter : TListViewFilterEx; // это для удобства хранения фильтров в одном месте
...
 AFilter.Category:='Береза';
 AFilter.Name:='Пеньки';
AListView.Items.FilterEx:=
    function(X: TListViewItem): Boolean
    begin
      Result:=
        (AFilter.Category.IsEmpty or X.Data['Category'].AsString.ToLower.Contains(AFilter.Category.ToLower)) And
         ((AFilter.Name.IsEmpty or X.Text.ToLower.Contains(AFilter.Name.ToLower)) or
         (AFilter.Name.IsEmpty or X.Detail.ToLower.Contains(AFilter.Name.ToLower)));
    end;

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

Внимание! FMX.ListView.pas от Delphi XE8, к другим версиям думаю не подойдет.

Прилагаю дополненный FMX.ListView.pas к сообщению.

FMX.ListViewWithFilterEx.zip

Ссылка на комментарий

Рекомендуемые сообщения

  • 0

а я вот все-таки подхожу к фильтрациям с т.з. не listItems, а данных, из которых они создаются

получается все просто и без вывертов

Ссылка на комментарий
  • 0
7 часов назад, krapotkin сказал:

а я вот все-таки подхожу к фильтрациям с т.з. не listItems, а данных, из которых они создаются

получается все просто и без вывертов

Я руководствуюсь принципом бритвы Оккама - не использую дополнительные сущности (базы данных к примеру), без крайней на то необходимости ;-)

Ссылка на комментарий
  • 0
3 часа назад, krapotkin сказал:

это не дополнительные. откуда данные появляются в листе?

вот там и место для фильтраций

К примеру данные получены от удаленного сервера в виде json. Для чего в таком случае плодить прокладки в виде баз данных, увеличивая объем и сложность приложения в два раза? 

Ссылка на комментарий
  • 0
  • Модераторы
1 час назад, Евгений Корепов сказал:

К примеру данные получены от удаленного сервера в виде json. Для чего в таком случае плодить прокладки в виде баз данных, увеличивая объем и сложность приложения в два раза? 

эм, как-бы вот (без БД, регистрации и смс)

  FilterJSON := aData;
  if (not menu_ids.IsEmpty) and (menu_ids <> ',,') then
  begin
    xFilter := SO(FilterJSON);
    aStatus := '{"status":"' + xFilter.S['status'] + '","struct":';

    FilterJSON := xFilter.A['struct'].Where(
      function(Arg: IMember): Boolean
      begin
        with Arg.AsObject do
          Result := Pos(S['id_menu'], menu_ids) > 0;
      end).AsJSON;
    FilterJSON := aStatus + FilterJSON + '}';
  end;

на выходе у нас FilterJSON только с теми данными, которые нужны (фильтруется по id_menu)

Ссылка на комментарий
  • 0
1 час назад, ZuBy сказал:

эм, как-бы вот (без БД, регистрации и смс)


  FilterJSON := aData;
  if (not menu_ids.IsEmpty) and (menu_ids <> ',,') then
  begin
    xFilter := SO(FilterJSON);
    aStatus := '{"status":"' + xFilter.S['status'] + '","struct":';

    FilterJSON := xFilter.A['struct'].Where(
      function(Arg: IMember): Boolean
      begin
        with Arg.AsObject do
          Result := Pos(S['id_menu'], menu_ids) > 0;
      end).AsJSON;
    FilterJSON := aStatus + FilterJSON + '}';
  end;

на выходе у нас FilterJSON только с теми данными, которые нужны (фильтруется по id_menu)

Повторю вопрос - для чего? ListView мощный инструмент способный превосходно хранить данные и манипулировать ими. В определенных задачах естественно. Искусственное раздувание требуемых объемов оперативной памяти путем хранения трех-четырех дубликатов необходимых данных может и оправдано на настольных платформах, но в мобильных приложениях резко ограничит количество потенциальных пользователей.

Напомню - тема всего лишь о простом решении продвинутой фильтрации в ListView. Давайте не будем раздувать флейм о моделях хранения.

Ссылка на комментарий
  • 0
  • Модераторы
53 минуты назад, Евгений Корепов сказал:

Напомню - тема всего лишь о простом решении продвинутой фильтрации в ListView. Давайте не будем раздувать флейм о моделях хранения.

хорошо, хотел прикрутить ваше решение в ModernLV но к сожалению моему, принцип поиска в новых версиях отличается.

как только разберусь, предоставлю решение для новой студии

Ссылка на комментарий
  • 0
4 часа назад, Евгений Корепов сказал:

ListView мощный инструмент способный превосходно хранить данные

не согласен. совсем. в родной разработке для андроидов у листа ВСЕГДА присутствует дата адаптер. и это жжж неспроста.

было несколько специальных лекций от разработчиков андроид, почему и как этим пользоваться...

данные на экране хранить нельзя.

Ссылка на комментарий
  • 0
  • Модераторы
10 часов назад, krapotkin сказал:

не согласен. совсем. в родной разработке для андроидов у листа ВСЕГДА присутствует дата адаптер. и это жжж неспроста.

было несколько специальных лекций от разработчиков андроид, почему и как этим пользоваться...

данные на экране хранить нельзя.

ну как бы и в реализации FMX так сделано, данных не визуализируются до того пока разработчик сам этого не пожелает. 

Ссылка на комментарий
  • 0

о каких данных мы говорим?

о тех что хранятся в объекте Data у listItem?  Так это и в VCL тоже было. Цепляйте объекты на указатель и вперед...

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

Ссылка на комментарий
  • 0
  • Модераторы
1 минуту назад, krapotkin сказал:

о каких данных мы говорим?

о тех что хранятся в объекте Data у listItem?  Так это и в VCL тоже было. Цепляйте объекты на указатель и вперед...

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

вы путаете статические данные и динамические. Тут вариант представлен со статикой, ни от каких внешних факторов не зависит фильтрация.

это тоже самое что искать песню в плейлисте, нашли поставили, нет - окей гугл

Ссылка на комментарий
  • 0
11 часов назад, krapotkin сказал:

не согласен. совсем. в родной разработке для андроидов у листа ВСЕГДА присутствует дата адаптер. и это жжж неспроста.

было несколько специальных лекций от разработчиков андроид, почему и как этим пользоваться...

данные на экране хранить нельзя.

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

В качестве примера приведу одно из своих приложений - Инвентаризация для узкого круга заказчиков. В Т.З. были довольно жесткие ограничения по цене устройств, обязательная оффлайн работа, с последующей синхронизацией, слабые, нестабильные каналы этой самой синхронизации и довольно большой объем данных (от 60 до 100 тысяч наименований) с которым нужно работать. Естественно заказчик хотел мгновенного отклика от приложения. Мои конкуренты пошли по пути завещанному вебинарами и правильными теоретиками - база данных на устройстве, дата-адаптер, визуализация. На тестовом пакете данных в 50к наименований это было жалкое и печальное зрелище, минимум трехсекундные интервалы после любого телодвижения. Импорт данных (из csv файла) занимал несколько минут, тесовые устройства грелись и сажали батарею. Естественно они сдулись, заявив что выполнить задание не возможно.

Было понятно, что конкурировать с бригадой "правильных" разработчиков в классической схеме мне не светит, поэтому пошел "запрещенным путём" - в приложении, помимо необходимых элементов управления был один ListView, которых адаптировался под текущую задачу и хранил все данные. Результат был потрясающий. Мгновенная реакция на поиск, фильтры. Для смеха использовал пакет данных с 150 тысячами записей. Демонстрировал на Samsung GT-S7390 https://market.yandex.ru/product/10542116/spec?hid=91491&track=char . Импорт/экспорт занимал около 30 секунд. Честно говоря я снял шляпу перед разработчиками ListView. Приложение успешно работает по сей день, конечно спустя полтора года я бы многое изменил/дополнил (особенно доступ ко всем данным несмотря на фильтрацию, тогда не нашел решения кроме ее временного отключения, выполнения действий, включения, недавно опубликовал здесь найденное решение), но ListView оставил бы в качестве хранилища.

Ссылка на комментарий
  • 0

ну да. все мы на жизненном опыте живем. и, кстати, нельзя руками проверять, есть ли ток в розетке. есть повод проверить...

не могу понять, в чем разница хранения ссылок на объекты в указателях listView и в указателях других списков? или это были не объекты, а голые текстовые строки Text и Detail?

я вот тоже могу рассказать пару историй наоборот, где до-меня-разработчики (замечу, на десктопе, где дури хватит на все) хранили данные в гриде на форме и, соответственно, в другом приложении, где понадобилась эта функциональность, им пришлось тащить форму (hidden! ) и дата-модуль, к которому она цеплялась, и еще специальными костылями ограничивать функциональность формы для второго приложения, а костыли лежали  - правильно, в глобальных переменных в первом приложении.

при этом программа работала шустро, все всех устраивало, кроме тех, кто пытался допилить дополнительный функционал

как результат, пришлось переписывать целиком.

Ссылка на комментарий
  • 0

Пытаюсь повторить этот фокус на Berlin Update1, но уже голову сломал. Накрутили там мусора - интерфейсы, типы данных из кучи файлов. Чтоб сделать подобное нужно около пяти файлов отредактировать. Родной фильтр ListViewFilter(Sender: TObject; const AFilter,  AValue: string; var Accept: Boolean) теперь что фильтрует? Понятно что когда был Text и Detal то фильтрация осуществлялась исключительно по ним. А если DynamicApperance? Полей этих может и не быть, что именно он фильтрует? 

 

Изменено пользователем Евгений Корепов
Ссылка на комментарий
  • 0
4 минуты назад, Евгений Корепов сказал:

Пытаюсь повторить этот фокус на Berlin Update1, но уже голову сломал. Накрутили там мусора - интерфейсы, типы данных из кучи файлов. Чтоб сделать подобное нужно около пяти файлов отредактировать. Родной фильтр ListViewFilter(Sender: TObject; const AFilter,  AValue: string; var Accept: Boolean) теперь что фильтрует? Понятно что когда был Text и Detal то фильтрация осуществлялась исключительно по ним. А если DynamicApperance? Полей этих может и не быть, что именно он фильтрует? 

 

Нашел ответ http://community.embarcadero.com/blogs/blog-menu/entry/filtering-support-for-custom-listview-layouts-in-update-1 : "New in Update 1 is search filtering for custom appearance, allowing you to add automatic filtering to your custom lists". Все ясно -  "автоматически"! 

Ссылка на комментарий
  • 0

Сделал тоже самое для берлина.

Изменено три файла FMX.ListView.Adapters.Base.pas FMX.ListView.Appearances.pas FMX.ListView.Types.pas, изменения предваряются комментарием "// Added Evgeniy Korepov for FilterEx". 

Единственный нюанс - параметр функции теперь 

function(X: TListItem): Boolean

а не 

function(X: TListViewItem): Boolean

 

ListViewBerlinWithFilterEx.zip

Изменено пользователем Евгений Корепов
Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...