Titanium ListView
Khamis, 20 Jun 2013, 1:40 pm0
Concept behind new Titanium ListView is similar to ModelViewViewModel (MVVM) pattern, where you have a set of data and you will map them to a view template
Let’s say you have this kind of data structure (model)
Person - id {Number} - name {String} - photo {String} - URL - message {String}
And you have an array of Person objects
var dataModels = [ { id: 1, name: 'John', photo: 'http://example.com/img/john.jpg', message: 'Thank you' }, { id: 2, name: 'Mary', photo: 'http://example.com/img/mary.jpg', message: 'Goodbye' }, { id: 3, name: 'Chris', photo: 'http://example.com/img/chris.jpg', message: 'Hey there!' }, ];
Now we want to map this array to a ListView, each Person object will use an ItemTemplate to produce a ListItem. First, define an ItemTemplate
var personTemplate = { childTemplates: [ { type: 'Ti.UI.ImageView', bindId: 'photo', properties: { left: 5, top: 5, width: 50, height: Ti.UI.SIZE } }, { type: 'Ti.UI.Label', bindId: 'name', properties: { left: 60, top: 5, height: 40, font: { fontSize: '18dp' }, color: '#fff', wordWrap: false, ellipsize: true } }, { type: 'Ti.UI.Label', bindId: 'message', properties: { left: 60, top: 45, font: { fontSize: '14dp' }, color: '#eee' } } ] };
A few things to note here. ItemTemplate defines views inside a ListItem, different from usual way we use add()
method to add a view to another view. To compare how to define it with TableViewRow
var person = dataModels[i]; var row = Ti.UI.createTableViewRow({ // to determine which row is being clicked itemId: person.id }); row.add(Ti.UI.createImageView({ image: person.photo, left: 5, top: 5, width: 50, height: Ti.UI.SIZE })); row.add(Ti.UI.createLabel({ text: person.name, left: 60, top: 5, height: 40, font: { fontSize: '18dp' }, color: '#fff', wordWrap: false, ellipsize: true })); row.add(Ti.UI.createLabel({ text: person.message, left: 60, top: 45, font: { fontSize: '14dp' }, color: '#eee' }));
In childTemplates
, is list of UI elements contain inside a ListItem. The way you define the element is by type
& properties
. bindId
is the Person property (data model), which you want the UI to bind to – getting the data out and put it into the view.
Note that in Titanium documentation, there are ItemTemplate and ViewTemplate. ItemTemplate is used to generate a ListItem, while ViewTemplate is to generate other views, which specifically defined by the type
property. See how to generate views in custom module, other than those defined in Ti.UI.* namespace.
However we cannot use dataModels
array above directly onto ListView, we need to restructure it so it can be compatible with the UI element it is bound to. This process is called binding, which produce an array of view model (of type ListDataItem) that will be assigned to ListViewSection items
property:
var viewModels = [ { properties: { itemId: 1 }, name: { text: 'John' }, photo: { image: 'http://example.com/img/john.jpg' }, message: { text: 'Thank you' } }, { properties: { itemId: 2 }, name: { text: 'Mary' }, photo: { image: 'http://example.com/img/mary.jpg' }, message: { text: 'Goodbye' } }, { properties: { itemId: 3 }, name: { text: 'Chris' }, photo: { image: 'http://example.com/img/chris.jpg' }, message: { text: 'Hey there!' } }, ];
Notice that each Person properties have been changed, and by now it’s not considered data model any, but rather a view model. text
and image
are properties of Ti.UI.Label and Ti.UI.ImageView respectively, which you can use to bind the value to UI in the template you define earlier.
A ListView need to explicitly declare at least one ListSection, which acts as a place where you can set & append more ListItem.
var listView = Ti.UI.createListView({ templates: { personTemplate: personTemplate }, defaultItemTemplate: 'personTemplate' }); var sections = []; var section = Ti.UI.createListSection(); sections.push(section); listView.setSections(sections); win.add(listView);
You can add more template if you have more layout being used in a single ListView. Now load the dataSet
into the ListView.
section.setItems(viewModels);
Load more items?
section.appendItems(viewModels);
Deeper visual tree of ItemTemplate
Use childTemplates
on each UI element inside the template to customize it even more.
var personTemplate = { childTemplates: [ { type: 'Ti.UI.ImageView', bindId: 'photo', properties: { left: 5, top: 5, width: 50, height: Ti.UI.SIZE } }, { type: 'Ti.UI.View', properties: { layout: 'vertical', left: 60, top: 5 }, childTemplates: [ { type: 'Ti.UI.Label', bindId: 'name', properties: { left: 0, top: 0, height: 40, font: { fontSize: '18dp' }, color: '#fff', wordWrap: false, ellipsize: true } }, { type: 'Ti.UI.Label', bindId: 'message', properties: { left: 0, top: 5, font: { fontSize: '14dp' }, color: '#eee' } } ] } ] };
Referring to template above, now you have this visual tree:
ListItem |__ ImageView |__ View |__ Label |__ Label
Events
For example, to add click event on image, add events property, specify an object with event name as key & callback as value
var template = { childTemplates: [ { type: 'Ti.UI.ImageView', bindId: 'photo', properties: { left: 5, top: 5, width: 50, height: Ti.UI.SIZE }, events: { click: function(e) { Ti.API.info('photo image clicked!'); } } },{ ... } ] };
You can also use itemclick event of ListView to detect if a ListItem was clicked
listView.addEventListener('itemclick', function(e) { Ti.API.info('a ListItem was clicked!'); // get which row is being clicked. See itemId property in viewModels above var person = dataModels.reduce(function(p, v) { if (v.id == e.itemId) { return v; } return p; }); });
23 Jun 2013
Delete specific item from squid cache
16 Jun 2013