Here’s a quick example of one way to dynamically populate a ScrollableView using Titanium SDK 2.x. I started with an example from this pastebin sample.
The below code displays a horizontally ScrollableView with default iOS page controls. Each page displays 6 photo thumbnails, while the last page displays the remainder (less than 6). This should work on Android, but I haven’t had time to test it yet (famous last words, I know).
SideScrollPagedView() is a CommonJS module (one of the recommended ways to organize code in Titanium).
module.exports = function SideScrollPagedView() {
var OS = Ti.Platform.osname;
// in real life this might come from a database or web service or be dynamically populated from a local directory
var data = [ { PhotoID: 1, Photo_Title: 'my first photo', Thumb_Path: 'photothumb_1.jpg'},
{ PhotoID: 2, Photo_Title: 'my second photo', Thumb_Path: 'photothumb_2.jpg'},
{ PhotoID: 3, Photo_Title: 'my third photo', Thumb_Path: 'photothumb_3.jpg'},
{ PhotoID: 4, Photo_Title: 'my fourth photo', Thumb_Path: 'photothumb_4.jpg' },
{ PhotoID: 5, Photo_Title: 'my fifth photo', Thumb_Path: 'photothumb_5.jpg'},
{ PhotoID: 6, Photo_Title: 'my sixth photo', Thumb_Path: 'photothumb_6.jpg'},
{ PhotoID: 7, Photo_Title: 'yeah, 7th photo', Thumb_Path: 'photothumb_7.jpg'},
{ PhotoID: 8, Photo_Title: 'ok, 8th photo', Thumb_Path: 'photothumb_8.jpg'},
{ PhotoID: 9, Photo_Title: 'this is the 9th photo', Thumb_Path: 'photothumb_9.jpg'},
{ PhotoID: 10, Photo_Title: 'photo number 10', Thumb_Path: 'photothumb_10.jpg'},
{ PhotoID: 11, Photo_Title: 'photo number 11', Thumb_Path: 'photothumb_11.jpg'},
{ PhotoID: 12, Photo_Title: 'photo number 12', Thumb_Path: 'photothumb_12.jpg'},
{ PhotoID: 13, Photo_Title: 'photo number 13', Thumb_Path: 'photothumb_13.jpg'},
{ PhotoID: 14, Photo_Title: 'photo number 14', Thumb_Path: 'photothumb_14.jpg'},
{ PhotoID: 15, Photo_Title: 'photo number 15', Thumb_Path: 'photothumb_15.jpg'},
{ PhotoID: 16, Photo_Title: 'photo number 16', Thumb_Path: 'photothumb_16.jpg'} ];
//main view
var self = Ti.UI.createView({
layout: 'vertical',
width: '100%',
height: Ti.UI.SIZE
});
//you can stack another one of these above or below this one
var mainContentBar = Ti.UI.createView({
layout: 'horizontal',
height: Ti.UI.SIZE,
//borderColor: '#CCCCCC',
//borderRadius: 2
});
self.add(mainContentBar);
var views = []; //array that holds a collection of "pages" (views) to be assigned to ScrollableView's .view property
var gameData = data; //copy of 'data' array, lets us slice it up per page, while keeping original list in tact
var itemsPerPage = (OS==='ipad') ? 6 : 4; //number of photos to display per page inside ScrollableView
var gutterSpace = (OS==='ipad') ? 16 : 9;
var halfOfItems = itemsPerPage * 0.5;
var buttonWidth = (OS==='ipad') ? 204 : 132;
var buttonHeight = (OS==='ipad') ? 152 : 99;
var scrollablePgW = (OS==='ipad') ? '644dip' : '273dip';
var scrollablePgH = (OS==='ipad') ? '360dip' : '230dip';
var numPages = Math.ceil(data.length/itemsPerPage); //numPages determined by number of times TotalFavorites / itemsPerPage, rounded up for whole pages
var itemsOnLastPage = data.length % itemsPerPage;
var lastInRow1 = (OS==='ipad') ? 2 : 1;
var lastInRow2 = (OS==='ipad') ? 5 : 3;
var sidePadding = true;
for ( var i = 0 ; i < numPages; i++ )
{
var view = createView(views.length);
//modify itemsPerPage for last page
if(i == (numPages-1)) { itemsPerPage = itemsOnLastPage; }
var perPageData = gameData.splice(0, itemsPerPage);
//for each set of itemsPerPage (6 or less), add an icon subview to view
for(var h = 0 ; h < itemsPerPage; h++)
{
//remove gutter space from last icon in each row to prevent the parent view (w/ layout:'horizontal') from forcing the next row too early
( h === lastInRow1 || h === lastInRow2 ) ? sidePadding = false : sidePadding = true;
var subview = createSubView( sidePadding );
var item = perPageData[h];
var iconItem = Ti.UI.createView({
backgroundImage: '/images/photos/thumbs/' + item['Thumb_Path'],
layout : 'composite',
width : buttonWidth,
height : buttonHeight,
left : 0,
top : 0,
visible : true,
photoData : item //can be used to later grab property values like photoData.Photo_Title
});
iconItem.addEventListener('singletap', function(evt) {
this.fireEvent('scrollItem_Clicked', {data : this.photoData });
});
//add additional subviews to iconItem here as needed
subview.add(iconItem);
view.add(subview);
}
views.push(view);
}
function createSubView(xval, yval)
{
var rightPadding = (OS==='ipad') ? '16dip' : '8dip';
var btmPadding = (OS==='ipad') ? '17dip' : '8dip';
if(!sidePadNum) {
rightPadding = 0;
}
var view = Ti.UI.createView({
width: buttonWidth,
height: buttonHeight,
bottom: btmPadding,
right: rightPadding,
//borderColor: '#FF00FF'
});
return view;
}
function createView(i)
{
var view = Ti.UI.createView({
backgroundColor: '#FFFFFF',
text: i,
layout: 'horizontal'
});
return view;
}
var scrollView = Titanium.UI.createScrollableView({
top: (OS==='ipad') ? '25dip' : '15dip',
left: (OS==='ipad') ? '50dip' : '20dip',
width: scrollablePgW,
height: scrollablePgH,
//borderColor: '#AACC00', //for debugging
views:views,
showPagingControl:true,
pagingControlColor: '#666666',
currentPage:0
});
mainContentBar.add(scrollView);
return self;
}
This assumes that the actual thumbnail images are stored under /Resources/images/photos/thumbs/.
ScrollableView and clickable items – avoiding click/swipe conflicts
Notice how the “iconItem.addEventListener(‘singletap’, function(evt){})” is listening for a ‘singletap’ event and NOT a ‘click’ event. I tried using ‘click’ first and ran into the problem of the photo icons being accidentally clicked while I’m trying to swipe (scroll) the ScrollableView.
Usage
To use the above module inside another file, you can do something like this, depending on what you’re doing:
//Application Window Component Constructor
module.exports = function ApplicationWindow() {
...
//create component instance
var self = Ti.UI.createWindow({
layout: 'composite',
width: Ti.UI.FILL,
height: Ti.UI.FILL,
backgroundColor:'#ffffff',
navBarHidden:true,
exitOnClose:true
})
...
var SideScrollPagedView = require('/ui/SideScrollPagedView');
var sspv = new SideScrollPagedView();
self.add(sspv);
return self;
}
The above code assumes that the scrollable photos component .js file is located in this location: /Resources/ui/SideScrollPagedView.js.
The above ApplicationWindow.js component would be called by app.js in the same way it is in the SingleWindow application template that comes with the Titanium 2.x IDE, like so:
//bootstrap and check dependencies
if (Ti.version < 1.8 ) {
alert('Sorry - this application template requires Titanium Mobile SDK 1.8 or later');
}
else if (Ti.Platform.osname === 'mobileweb') {
alert('Mobile web is not yet supported by this template');
}
else {
...
//require and open top level UI component
var AppWin = require('/ui/ApplicationWindow');
var appWin = new AppWin();
appWin.open();
}