Mastering Titanium SDK ListView: Virtualized Scrolling, Sections, and Performance Tweaks
Titanium SDK’s ListView can render thousands of items smoothly by virtualizing rows, supporting sections, and using lightweight templates. Learn how to set it up, handle events, and avoid common pitfalls.
16 Dec 2025, 09:56 UTC

Problem: Displaying Thousands of Items on Mobile
When building a feed‑style app, developers often hit a wall: a UI that freezes, memory spikes, or a sluggish scroll. The root cause is usually the way the framework renders every item in the data array. Titanium’s ListView solves this by rendering only what the user sees, but many teams miss out on its full potential.
Thesis: Use ListView’s Virtualization, Sections, and Custom Templates to Keep the UI Responsive
By combining declarative data binding, section support, and lightweight templates, you can display tens of thousands of rows on both iOS and Android with minimal memory usage and smooth scrolling.
1. Declarative Setup and Virtualization
ListView is a virtualized component: it creates UI objects only for the visible rows. This means that a list of 10,000 items will not consume 10,000 views at once. The framework recycles row objects as you scroll.
// tiapp.xml – enable debug to see ListView performance warnings
<property name="debug" type="bool">true</property>
// main.js – create a 10k item list
var win = Ti.UI.createWindow({
backgroundColor: '#fff',
layout: 'vertical'
});
var listView = Ti.UI.createListView({
separatorColor: '#ccc',
separatorStyle: Ti.UI.iPhone.ListViewSeparatorStyle.SINGLE_LINE
});
// generate data
var data = [];
for (var i = 0; i < 10000; i++) {
data.push({
properties: {
title: 'Item ' + i,
subtitle: 'Subtitle ' + i,
itemId: i
}
});
}
listView.setItems(data);
win.add(listView);
win.open();
Run this on a device or simulator. Open the Xcode/Android Studio profiler, and you’ll see that memory usage spikes only during the initial load and drops back as you scroll.
2. Sectioned Data for Grouped Content
Sections allow you to group items under headers or footers, which is useful for categories, dates, or any logical grouping.
var sections = [];
for (var month = 1; month <= 12; month++) {
var section = Ti.UI.createListSection({
headerTitle: 'Month ' + month
});
for (var day = 1; day <= 28; day++) {
section.addItem({
properties: {
title: 'Day ' + day,
subtitle: 'Event details',
itemId: month + '-' + day
}
});
}
sections.push(section);
}
listView.setSections(sections);
Each section is rendered only when it becomes visible. This keeps memory usage low even when you have a complex hierarchy.
3. Custom Row Templates for Multiple View Types
Instead of manually creating views for each row, define a template once. Titanium supports multiple templates in a single ListView, enabling mixed row types (image + text, toggle + label, etc.).
// Define templates
var templates = {
default: {
childTemplates: [
{ type: 'Ti.UI.Label', bindId: 'title', properties: { left: 10, width: Ti.UI.FILL } },
{ type: 'Ti.UI.Switch', bindId: 'switch', properties: { right: 10 } }
]
},
image: {
childTemplates: [
{ type: 'Ti.UI.ImageView', bindId: 'image', properties: { left: 10, width: 50, height: 50 } },
{ type: 'Ti.UI.Label', bindId: 'title', properties: { left: 70, width: Ti.UI.FILL } }
]
}
};
listView.templates = templates;
// Sample data using templates
var mixedData = [
{ properties: { title: 'Switch row', template: 'default' } },
{ properties: { title: 'Image row', template: 'image', image: 'https://picsum.photos/50' } }
];
listView.setItems(mixedData);
Only the needed subviews are instantiated, so even a heavily decorated list stays snappy.
4. Event Handling and Interaction
ListView supports click, longpress, and swipe events on both rows and sections. Attach listeners at the ListView level to avoid per‑row callbacks, which can accumulate.
listView.addEventListener('click', function(e) {
if (e.sectionHeader) {
Ti.API.info('Header clicked: ' + e.sectionHeader.title);
} else {
Ti.API.info('Row clicked: ' + e.itemId);
}
});
5. Pull‑to‑Refresh and Infinite Scrolling
Use the refreshControl property for pull‑to‑refresh. Combine it with the onScroll event to detect when the user reaches the bottom and load more items.
listView.refreshControl = Ti.UI.createRefreshControl({
tintColor: '#0066cc'
});
listView.addEventListener('refresh', function() {
// fetch new data, then call listView.endRefresh();
});
listView.addEventListener('scroll', function(e) {
if (e.contentOffset.y + e.size.height >= e.contentSize.height - 100) {
// load next page
}
});
Trade‑Offs and Limitations
- Pagination Still Needed: Virtualization saves memory, but loading 10k items at once can still block the main thread. Use server‑side pagination or incremental fetching.
- Platform Differences: iOS and Android handle
separatorColorandseparatorStyledifferently. Test on both to ensure consistent look. - Heavy Subviews: Rows with many
ImageViewor custom controls can drop frames on older devices. Keep templates lean. - Event Listener Management: Attaching listeners to every row can leak memory. Prefer ListView‑level listeners and remove them when the window closes.
Actionable Checklist
- Define data as an array of objects or a
ListSectionarray. - Create reusable templates for each row type.
- Attach event listeners at the ListView level.
- Implement pagination or server‑side filtering for large data sets.
- Test memory usage on target devices using the profiler.
By following these steps, you’ll build a ListView that scales gracefully, stays responsive, and delivers a polished user experience.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.