• Constantiner
  • Junik
  • Maar
  • Agahov
  • Graann
  • SlonVsapogah
  • Vertex

Fiat lux!

  • В начало
  • Контакты
  • Войти

Пейджинг на лету

На одном из проектов возникла производственная необходимость отобразить данные в виде таблицы. Казалось бы проще репы, но избыточность данных бесстыдно ставила контрол на локти. Стоит отметить, что он имел 46 колонок и 100 строк, количество же данных могло исчисляться десятками тысяч. Упомяну, что интеграция происходила на базе веб-сервисов, так что LCDS нам только снился.

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

Прольем же немного света на базовые механизмы паджинации поддерживаемые стандартными компонентами, являющими собой производные List-а, а именно HorizontalList, TileList, DataGrid, Menu, Tree, ну и List конечно. Техника базируется на использовании класса mx.collections.errors.ItemPendingError. Выбросив такую ошибку из метода getItemAt() коллекции, которая является дата-провайдером для List-based копонент, можно заставить контрол впасть в ожидание pendent данных. Абстрактный например:

  1. public class PagingCollection implements IList
  2. {
  3.     override public function getItemAt(index:int, prefetch:int=0):Object
  4.     {
  5.         if (index < 0 || (_length != -1 && index >= _length))
  6.           throw new RangeError();
  7.         if(!cache.hasItem(index))
  8.           throw new ItemPendingError("");
  9.         return cache.getItemAt(index);
  10.   }
  11. }

При этом контрол поймав эту ошибку подписывается к ней с помощью респондеров и ждет их срабатывания. Код в листинге ниже взят из метода scrollHandler() класса mx.controls.List (line 1451).

  1. try
  2. {
  3.     if (!iteratorValid)
  4.         iterator.seek(CursorBookmark.FIRST, pos);
  5.     else
  6.         iterator.seek(CursorBookmark.CURRENT, delta);
  7.     if (!iteratorValid)
  8.     {
  9.         iteratorValid = true;
  10.         lastSeekPending = null;
  11.     }
  12. }
  13. catch(e:ItemPendingError)
  14. {
  15.     lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, pos);
  16.     e.addResponder(new ItemResponder( seekPendingResultHandler, seekPendingFailureHandler, lastSeekPending));
  17.     iteratorValid = false;
  18. }

Когда данные готовы, дергаем за респондеры из нутра кастомной коллекции и контрол, в свою очередь, начинает дергать getItemAt() используя индексы paged данных. Абстрактный код ниже (в контексте кастомной коллекции) иллюстрирует обход всех респондеров, что фактически приводит к вызову обработчиков, добавленных контролом в момента отлова ItemPendingError (e.g. seekPendingResultHandler из предыдущего листинга).

  1.  
  2. protected function onResult(val:PagingResult):void
  3. {
  4.     cache.addItems(val.result);
  5.     itemPendingError.responders.forEach(
  6.       function(responder:IResponder, index:int, arr:Array):void
  7.       {
  8.           responder.result(val.result);     
  9.       });
  10. }
  11.  

Интересно упомянуть, что начинается всё с обращения к геттеру length() коллекции. То есть при присвоения нового провайдера Листу, чтобы корректно отрисовать скролл, первым делом он пытается узнать его размер. Вначале, при отсутствии данных, возвращаем -1. Это важно чтобы отличить начальное состояние от возможно пустого массива в полученных данных в дальнейшем и делаем начальный запрос.

  1. override public function get length():int
  2. {
  3.     if (_length == -1 && !lengthPending)
  4.     {
  5.         lengthPending = true;
  6.         requestData();
  7.     }
  8.     return _length;
  9. }

Узнав общее колличество выставляем длину и пинаем контрол с помощью события CollectionEvent.COLLECTION_CHANGE.

  1. protected function onResult(val:PagingResult):void
  2. {
  3.     cache.addItems(val.result);
  4.     if(_length < 0)
  5.     {
  6.         _length = val.totalSize;
  7.         lengthPending = false;
  8.         var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
  9.         event.kind = CollectionEventKind.RESET;
  10.         dispatchEvent(event);
  11.   }
  12. }

Весьма отмечу также, что интенсивное таскание тумбы в режиме liveScrolling представляло из себя совершенно удручающее зрелище, даже с задержкой данных в 1 секунду, контрол выбрасывал ItemPendingError. Отнаследовавшись и перекрыв scrollHandler() c hot-plug мы вернули его к жизни:

  1. public class ListExtended extends List
  2. {
  3.     public function ListExtended()
  4.     {
  5.         super();
  6.     }
  7.     override protected function scrollHandler(event:Event):void
  8.     {
  9.         /*
  10.         value can be null and it
  11.         results in throwing our custom
  12.         PagingCollectionItemPendingError.
  13.         In overridden method it is not
  14.         wrapped in try/catch block.
  15.         */
  16.         if (iterator.bookmark.value)
  17.             super.scrollHandler(event);
  18.     }
  19. }

Творение сие не есть алмаз, а лишь фантазия воображения, но мы счастливы и живы, и солнце, по обыкновению, снова греет наши затылки.

Довольно живописную картину можно получить ознакомившись с приведенными выше ссылками и исходниками ниже.
Качать пример отсюда (некоторые комментарии в коде на англицком). view source.

Bookmark this article at …

  • От Pavel Kozhin
  • Июнь 23rd, 2008
  • Написан в ActionScript 3
  • 598 просмотров
  • 7 отзывов »
  Russian (RU)  
 
  • Author

    View Pavel Kozhin's profile on LinkedIn
  • Fiat lux!

    • Последние
    • Архивы
    • Рубрики
    • Последние комментарии
  • Поиск




  • Рубрики

    • Все
    • ActionScript 3
    • Uncategorized
  • XML ленты

    • RSS 2.0: Записи, Комментарии
    • Atom: Записи, Комментарии
    What is RSS?

powered by b2evolution free blog software


Контакты | Powered by b2evolution
Credits: Foppe Hemminga | blog software | web hosting | monetize