Optimizing Lists
List performance depends on how much work occurs at initial render, on each focus/scroll frame, and when rows or cells are reused. Preserve correctness first: a fast list with unstable identity, stale pooled state, or broken focus is not an optimization.
Choose the Right Container
- Use
Repeaterfor a small, bounded set of similar views. - Use
ScrollViewfor one heterogeneous page whose complete child tree is affordable. - Use
CollectionViewfor data-driven rails, grids, EPGs, remote catalogs, and other large or incremental collections.
ScrollView renders its complete tree. A very long ScrollView or Repeater does not gain CollectionView virtualization by being clipped.
Stabilize Data and Identity
- Give rows, cells, and important controls stable semantic IDs.
- Keep item identity separate from array position when items can be inserted or reordered.
- Apply incremental data changes when the API supports them instead of replacing an entire collection for one changed item.
- Avoid allocating formatted strings, callback closures, large copied objects, or derived arrays on every scroll tick.
- Keep focus callbacks small. Per-frame item-focus behavior is opt-in because it adds work during motion.
Stable identity improves reconciliation, test locators, focus restoration, and useful diagnostics at the same time.
Make Layout Predictable
- Provide known row/cell dimensions when the design allows them.
- Keep cell hierarchies shallow and move reusable presentation into AppConfig fragments.
- Avoid nested
ScrollView,ScrollContainer, orRepeatertrees inside collection cells and supplementary views. - Do not trigger network requests, parsing, or large state transformations from layout callbacks.
- Verify the same content at the logical sizes and font metrics used by each target.
Variable-size content is valid, but measure it with realistic extremes. One unexpectedly tall label or late image dimension can invalidate more layout than the common case suggests.
Control Image Cost
- Request images near the rendered size instead of decoding originals much larger than the cell.
- Use deterministic placeholders and explicit dimensions so layout does not wait for the bitmap.
- Limit simultaneous image work to what the visible and near-visible window needs.
- Cancel or ignore late results when a pooled cell now represents another item.
- Measure decoded memory, not only compressed transfer size.
Visual regression tests can assert load status and decoded dimensions, but sustained device scrolling is still required to expose decoder and GPU pressure.
Tune Pool Preloading Last
Hosanna reuses cells, rows, view instances, and SceneGraph/native nodes. The AppConfig performance tier can preload fragments and instances:
{
"instancePoolSettings": [
{
"styleKey": "cells.poster",
"className": "DynamicCell",
"count": 2
}
]
}
The styleKey and class name must exist in the application’s generated/reflection registry. Keep counts small and tier-specific. Preloading can reduce a measured first-use allocation spike, but it increases launch work and baseline memory and can conceal a lifecycle bug.
Inspect the Instance Pool view in Hosanna DevTools while repeating the same scroll path. Counts that grow on every pass, or in-use objects that never return, indicate ownership/reuse problems rather than a need for a larger pool.
Benchmark Checklist
Use a deterministic data set and record:
- time to first visible/focusable row;
- the first navigation into an unvisited row;
- sustained forward and reverse scrolling;
- image-ready time and visible placeholder behavior;
- maximum created/in-use pool counts;
- focus continuity at row/cell boundaries;
- return navigation after several screens;
- low-tier device memory and recovery.
Change only one of hierarchy, data update strategy, image size, preload count, animation, or buffer behavior per comparison.
For CollectionView API and lifecycle details, use the CollectionView overview rather than duplicating its contract here.