Translate

суббота, 3 декабря 2016 г.

#3 ModernListView - динамическая подгрузка контента

Доброго времени суток!

Сегодня хотел бы рассмотреть динамическую подгрузку контента.
Мы не так давно в нашем ламповом чатике обсуждали два варианта подгрузки данных:
  • Бесконечный скроллинг (например как лента в ВК)
  • Паджинация/Пагинация (постраничная загрузка)
В современных приложениях почти всегда используется бесконечный скроллинг, это удобно по нескольким причинам:
  1. Пользователю не нужно дотягиваться до кнопок переключения страниц или использовать жесты влево/вправо
  2. Разработчику не нужно искать место для размещения контролов переключения страниц или реализовывать доп. функционал по управлению паджинацией с помощью жестов
Было свободное время и я реализовал два этих варианта с применением ModernListView, т.к. в нём есть специальные методы и события для простой реализации.

Опишу немного как всё это работает:
На сервер отправляется запрос с указанием страницы, PHP скрипт сформирует массив из имён статичных файлов (картинок) и упакует это в JSON формат. Наш клиент принимает JSON и отображает информацию в списке. При прокрутке контента происходит динамическая подгрузка картинок с сервера.

Для удобного использования я создал помощника (комментарии присутствуют)
type
  TmyLVLoadMode = (Scrolling, Pages);

  TmyLVDynamicLoad = record
    // настраиваем LV
    class procedure configList(const aLV: TListView); static;

    // режим подгрузки данных
    class var LoadMode: TmyLVLoadMode;

    // кол-во страниц
    class function PageCount: integer; static;
    // текущая страница
    class var Page: integer;

    // загрузка нужной страницы
    class procedure LoadPage(const aLV: TListView; const aPage: integer; 
      const aLabel: TLabel); static;
  end;
  
Вот весь листинг основного модуля, вся работа происходит в недрах помощника
procedure TFormMain.btnPrevClick(Sender: TObject);
begin
  // загружаем предыдущую страницу
  TmyLVDynamicLoad.LoadPage(ListView1, TmyLVDynamicLoad.Page - 1, lbText);
end;

procedure TFormMain.btnNextClick(Sender: TObject);
begin
  // загружаем следующую страницу
  TmyLVDynamicLoad.LoadPage(ListView1, TmyLVDynamicLoad.Page + 1, lbText);
end;

procedure TFormMain.FormShow(Sender: TObject);
begin
  // настраиваем отображение ListView
  TmyLVDynamicLoad.configList(ListView1);
  // выбираем режим подгрузки Scrolling
  SetLVMode(false);
end;

procedure TFormMain.ListView1Paint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
var
  I: integer;
  aLV: TListView;
  aFirst, aLast: integer;
begin
  aLV := (Sender as TListView);
  if (aLV.Items.Count <= 0) then
    exit;

  // получаем первый видимый элемент
  aFirst := Max(0, aLV.getFirstVisibleItemIndex);
  // получаем кол-во видимых элементов
  aLast := aFirst + aLV.getVisibleCount;
  for I := aFirst to aLast do
  begin
    // находится ли наш индекс в нужном диапозоне
    if InRange(I, 0, aLV.Items.Count - 1) then
    begin
      // если картинка еще не была загружена, то начинаем её грузить
      if aLV.Items[I].Data[sign_Loaded].AsInteger = 0 then
      begin
        // начали загружать!
        aLV.Items[I].Data[sign_Loaded] := 1;
        // собственно загрузка картинки, в доп. потоке
        LoadBitmapFromURL(aLV.Items[I].Data[sign_URL].AsString, aLV.Items[I].Bitmap);
      end;
    end;
  end;
end;

procedure TFormMain.OnLVScrollEnd(Sender: TObject);
begin
  if TmyLVDynamicLoad.Page < TmyLVDynamicLoad.PageCount then
  begin
    // в режиме скроллинга всегда грузим следующую страницу
    TmyLVDynamicLoad.LoadPage(ListView1, TmyLVDynamicLoad.Page + 1, lbText);
    sleep(1500); // делаем паузу, имитируем диалог ожидания
  end;
end;

procedure TFormMain.SetLVMode(const aValue: Boolean);
begin
  ListView1.ItemsClearTrue;

  if aValue then
  begin
    // меняем режим подгрузки на паджинацию
    TmyLVDynamicLoad.LoadMode := TmyLVLoadMode.Pages;
    lbText.Text := 'Pages / page = 1';
    // в режиме паджинации обнуляем событие
    ListView1.OnScrollEnd := nil;
    // показываем контролы управления паджинацией
    btnNext.Visible := true;
    btnPrev.Visible := true;
  end
  else
  begin
    // меняем режим подгрузки на бесконечный скроллинг
    TmyLVDynamicLoad.LoadMode := TmyLVLoadMode.Scrolling;
    lbText.Text := 'Scrolling / page = 1';
    // назначаем событие при котором будет вызываться подгрузка следующей порции
    ListView1.OnScrollEnd := OnLVScrollEnd;
    // скрываем контролы управления паджинацией
    btnPrev.Visible := false;
    btnNext.Visible := false;
  end;

  // после смены режима подгрузки, загружаем первую страницу
  TmyLVDynamicLoad.LoadPage(ListView1, 1, lbText);
end;

procedure TFormMain.Switch1Switch(Sender: TObject);
begin
  SetLVMode(Switch1.IsChecked);
end;

Остальной код который выполняет запросы и заполнение списка, также имеет комментарии. Его можно посмотреть на GitHub'e

Используемые модули