In this post I will try to explain how to create custom rows for TableViews using Titanium Mobile.
If you don’t know what Titanium is about, take a look here. I suppose you already know how to setup and start a Titanium project so I’ll show only how to create a custom row.
By default a TableView will let you create a row that can have: the left image, the title and the row type decorator set. The next code is an example of the standard data that needs to be passed to a TableView.
var RegData = [ { leftImage:'es.png', title:"Spain", hasChild:true }, { leftImage:'gb.png', title:"United Kingdom", hasChild:true }, { leftImage:'us.png', title:"United States", hasChild:true }, { leftImage:'fr.png', title:"France", hasChild:true } ]; var TheTable = Titanium.UI.createTableView({ data:RegData });
And the result is this:
Ok, we would like to add more data to a row, maybe something like this:
This will require to build the row “by hand”. So we need to add a 2 imageViews, the flag and the trend, and labelViews for the country and the percent . We also need to change the data array of course.
var CustomData = [ { flag:'es.png', country:"Spain", trend:'up.png', percent:'28%' ,hasChild:true }, { flag:'gb.png', country:"United Kingdom", trend:'down.png', percent:'-3%', hasChild:true }, { flag:'us.png', country:"United States", trend:'up.png', percent:'8%', hasChild:true }, { flag:'fr.png', country:"France", trend:'down.png', percent:'-40%', hasChild:true } ];
We create a data variable as an array that will hold the row objects generated.
var data=[];
then we walk through the CustomData array, create a new row , the imageViews and labels and add them to the row.
for (var i = CustomData.length - 1; i <= 0; i--) { var row = Titanium.UI.createTableViewRow(); var flag = Titanium.UI.createImageView({ url:CustomData[i].flag, width:32, height:32, left:4, top:2 }); var country = Titanium.UI.createLabel({ text:CustomData[i].country, font:{fontSize:16,fontWeight:'bold'}, width:'auto', textAlign:'left', top:2, left:40, height:16 }); var percent = Titanium.UI.createLabel({ text:CustomData[i].percent, font:{fontSize:12,fontWeight:'bold'}, width:'auto', textAlign:'left', bottom:0, left:60, height:12 }); var trend = Titanium.UI.createImageView({ url:CustomData[i].trend, width:16, height:16, right:10 }); row.add(flag); row.add(country); row.add(percent); row.add(trend); row.hasChild=CustomData[i].hasChild; row.className = 'coutry_row'; data.push(row); };
As you see we also add a className to the row to improve the rendering performance, as the iPhone will reuse the row template with every new data when rendering the table.
The obtained result is this:
You can download the complete Titanium project from here. You will have to create a new project in Titanium and replace the resource folder with the one in the archive.
Let me know your thoughts.
Check the Spanish version of this post:
Como crear filas personalizadas para TableViews usando Titanium Móvil



58 Responses
Thanks, that’s really great to share this.
Nice tutorial!
Well done, thanks for sharing. The classname trick is wise and save ressources.
Thanks for the great tutorial!!
Everything is working for me except I cannot get an Event Listener to link up with the data in order to open new windows when a table selection is made. I have other regular tables that open windows just fine but for some reason I cannot get the event listener to read the data. (I used an alert dialog from kitchen sink and the table does listen just fine, showing the dialog).
Thanks and I can’t wait for more of your Titanium tutorials!
Hi, can you post some code on how you create the rows and the table and how you try to get the event?
I need this to see what you are doing there
Try in this lines:
// create table view event listenertableview.addEventListener(‘click’, function(e)
{
var newwin = Titanium.UI.createWindow({
url:e.rowData.test,
title:e.rowData.date
});
Titanium.UI.currentTab.open(newwin,{animated:true});
});
to add some logging to see if you get the rowData,
Titanium.API.info(e.rowData);and try simply with
newwin.open({transition:Titanium.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT})instead of trying to get the current tab.
To be hones I’ve never been able to use right currentWindow and currentTab.
A solution would be to make your own stack of tabs and take it from there.
I added this line
Titanium.API.info(e.rowData);
and in the Titanium log screen it returns
[INFO] [object TiUITableViewRow]
Is it supposed to return more info than that if it is working?
try
Titanium.API.info(e.source.rowData);or
Titanium.API.info(e.rowData.rowData);and let me know.
If none is working I will import your code in a Titanium project and try to figure out what’s going on.
I’m waiting for your reply
Hey Dan,
Thanks for the awesome customer service
I tried both of those and the log returns
[INFO] null
Ok, I’ll take a deeper look at your code and let you know.
i just deleted the code
, can you send it over email to dan.tamas [at] gmail [dot] com ?
I found it
you forgot to add the test and date fields to your row
Add this when you build the row
row.test = customData[i].test;row.date = customData[i].date;
Another thing would be to try to make the variables more clear, you have another date variable that is a label and may confuse you ( or maybe this was the reason you lost so much time
).
let me know
OMIGOD I love you so much Dan!!!! Thank you!!! I figured it had to be something like that but couldn’t put my finger on it… awwww what a relief
Great tip – just what I was looking for.
I needed a way to add the same data to EVERY row, like background color, etc
thanks
Hi,
great tutorial, fab to get more data in the table, but am having the same problem as appceleron when I click on an item I get null for my alerts – I downloaded your project and put an alert in the ‘click’ listener and get the same – help!
not sure what you mean in your reply to appceleron about row.test and row.date as his code was not posted)
Any help much appreciated!
Can you post some code on http://pastie.org so I can take a look at your code?
Thanks, but I think I’ve fixed it – got sidetracked by the alerts suggested above – they both return null, but if I alert just on e.index I get the row ID
Add to that the fact I had changed the name of one of the columns probably accounts for it!
So my bad and your cool code
Thanks for the uber fast response – any other crafty tutorials in the pipeline (like how to make pop up window work in iPad split view landscape mode (as per kitchen sink windows standalone (animation fun)) – works fine in portrait, but still appears as portrait when device in landscape (so come s up sideways!)
sorry off topic, but doing my head in!
I’m working on a countdown example for titanium, with a little of OOP for javascript
, but you could ask on the Appcelerator list
For the popup window I don’t know yet, I didn’t need it so far
http://developer.appcelerator.com/questions/created
Will do – thanks
looking forward to the countdown
Awesome tutorial, I was trying to figure out from the api how to get this working and your tutorial outlined it nicely! Thanks!
Ok guys, two more tutorials released:
iPhone countdown timer with Titanium
and
Combobox control for iPhone – this one has video too.
Let me know your thoughts
Awesome!
How do I set the searchBar to search by the label in the custom row?
Sorry for the late reply.
The only way to do this I think is to manage yourself the searching, based on the labels text, and show only the rows you want.
So you register all the labels in a separate array to avoid accessing the rows in the table – this can put a big load on the phone, search inside this array and display the correspondent table rows only.
I don’t have another idea.
ya men
pls given some example of blackberry for this type.
Thanks for this great tutorial! It was very helpful for me.
One tip: use the image property in createImageView instead of the deprecated property url. With url, there are some repaint problems, if you add or remove rows.
I’m sorry but BB it’s beta and I don’t have yet access to it. If you have bought the pro pack you should be able to talk to the team if you have issues.
This is awesome. Thanks so much for making this so clear. One question.
My rows have different layouts. One may be a label with a text field, another may be a Label with an > to a child page, another may be a label with a checkbox.
So would I still use the same className for each row or would each similar row get the same class name.
For example would any rows with label/checkbox get the same class names and other rows with > and children get a different name.
Thanks!
Fish
@Fish
Each different layout of the rows should have it’s own className.
So label/checkbox – className:’lblck’
some_other_layout – className:’otherlayout’
and so on.
All the rows that respects a specific layout put it with the same className.
If you use the same className on different layouts your app may crash
WOW. thanks for the fast response. REALLY appreciate the help here. Gonna try and get this to work in my tab view now. Just digging into Appcelerator. Very excited about how much more comfortable I am with it vs native coding in ObjC.
Thanks again
hey one more quick question. I want to use a Custom Data array like you have without hard coding each row. But I need to be able to access each textfield object. The problem is that if I use this inside my loop:
var tf = Titanium.UI.createTextField({…
I can’t access each of the rows because tf.value would be over written each time. Or am I just not thinking straight here. Had I hardcoded this I could access tf1 and tf2 etc…. Would I use a dynamic variable or something?
My JS is a bit rusty.
Thanks
Fish
Why don’t you add the tf variable to the row when you create the loop, like any other custom variable and fetch it with row.rowData.tf.value
if this doesn’t work, you need to get it using children
row.children[0].value
if the it’s the first element in row.
Thanks for getting back to me.. and sorry for the follow ups. I’m still trying to wrap my head around this.
I do use row.add(tf) within the loop. But I’m sorta not getting how to call a row by name vs by array index. I guess I can re-iterate through the array and get the index number to know whats what. The reason is that I am going to have long form that I’m going to want to access elements of here and there. Doing so by name would be easier for sure.
I’ve been on vacation this week so I’m not completely focused. I’m sure it will be a bit more clear next week when I get back. Thanks again
Fish
I’m afraid that without creating your own hash for the rows you cannot call the rows by name.
I see what you’re saying.
I’m having trouble getting the values in those fields by any methods. If you have a quick second, can you possibly give me a quick example of how to call the value using the row.rowData.tf.value method you mentioned. Googled and looked in the Ti community but havent found my answer yet.
I have 2 rows right now… one is username and one is password.
I posted my code on the forum as well:
http://developer.appcelerator.com/question/60231/get-values-from-input-textfields-in-custom-table-view
Thanks Dan!
Fish
Is there a feasible way to set the background color of each row to a specific value based on one of the labels or data elements contained in the custom row?
When you build the row, put everything in a big view and you can set the backgroundColor property of it based on your needs. After this add this view to the row.
Looks like there is some minor mistake in example code:
for (var i = CustomData.length – 1; i >= 0; i–) {
Should be “>” instead of “>”.
In the custom row how can you add a button with it’s own unique colour to it?
Thanks
Shaun
I think yes. Just use a backgroundImage property for that button.
Be aware that this does not work on Android, clicking the left image will trigger some bug in Titanium that will change the image, remove it but even worse – will change the index value from now on other objects like the title/text….
https://developer.appcelerator.com/question/60861/two-listeners-triggered-at-the-same-time-removeeventlistener-doesnt-work
Thanks, should be ok now
//Create a message Window
var msgwin = Titanium.UI.createWindow({
height:30,
width:250,
bottom:70,
borderRadius:10,
touchEnabled:false,
orientationModes : [
Titanium.UI.PORTRAIT,
Titanium.UI.UPSIDE_PORTRAIT,
Titanium.UI.LANDSCAPE_LEFT,
Titanium.UI.LANDSCAPE_RIGHT,
]
});
var msgView = Titanium.UI.createView({
height:30,
width:250,
borderRadius:10,
backgroundColor:’#000′,
opacity:0.7,
touchEnabled:false
});
var msgLabel = Titanium.UI.createLabel({
text:”,
color:’#fff’,
width:250,
height:’auto’,
font:{
fontFamily:’Helvetica Neue’,
fontSize:13
},
textAlign:’center’
});
msgwin.add(msgView);
msgwin.add(msgLabel);
var data=[{title:"New Message",hasChild:true},{title:"Inbox"},{title:"sent"}];
//var row1 = Titanium.UI.createTableViewRow({title:’Compose’});
var table = Titanium.UI.createTableView({
backgroundColor:’transparent’,
//style: Titanium.UI.iPhone.TableViewStyle.PLAIN,
data:data
});
//table.add(row1);
table.addEventListener(‘click’,function(e)
{
e.msgwin.open({transition:Titanium.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT});
});
Titanium.UI.currentTab.open(newwin,{animated:true});
win1.add(table);
the msg window is not coming after clicking on the table..
Plz suggest me
you are creating msgwin but you try to open it wrong
try
table.addEventListener(‘click’,function(e)
{
msgwin.open({transition:Titanium.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT});
});
not e.msgwin.open….
Thank you, you shared a good concept
Here’s a newbie question – where are you defining your ‘coutry_row’ class? I’m sure it’s in another file, but I can’t figure out where. Thanks.
‘country_row’ is just a string, is not a variable. You can use anything there, as long as it’s the same string for the rows that has similar layout.
Thanks for your quick reply! But isn’t ‘country_row’ defining a css class? Isn’t that how you are getting the separator lines and “>” image on each row?
The “className” is just a coincidence with the css similar property. The way titanium works is not exactly as it does a web browser. The syntax is similar to CSS and it uses Javascript only to facilitate the development of iphone apps for web developers.
The right arrow is defined by the hasChild property of the row.
Aha, thanks for the help!
Great help here. Just wondered if you could clarify were you put the code you suggested to appceleron? I have exactly the same problem as them but cannot get it to work! So close!
At the end if the article there is a link where you can download the whole project for Titanium. Create your own project and replace the resources folder with the one you find in archive.
Thanks for this great tutorial!
But Your code is something wrong. Your For() statement always returns false.
Please See below code.
————————————————
for (var i = CustomData.length – 1; i <= 0; i–) {
for (var i = CustomData.length – 1; 0 <= i; i–) {
or
var length = CustomData.length;
for(var i = 0;i<length;i++) {
—————————
Maybe you can provide me a pastie (pastie.org) with your code because for me is working ok. I suspect something is wrong with your CustomData declarations.
another great tutorial, thanks for sharing.
I’m trying to add an event listener for the links but I keep getting this error when I click on a row:
[WARN] Exception in event callback. {
expressionBeginOffset = 2110;
expressionCaretOffset = 2119;
expressionEndOffset = 2119;
line = 79;
message = “Can’t find variable: newWindow”;
name = ReferenceError;
sourceId = 204767424;
sourceURL = “file://localhost/Users/xxx/Development/xxx/Resources/view.js”;
}
Could you please have a quick look? http://pastie.org/1543977 . Thank you!
you are trying to open newWindow even if the if above is false – and so there is no newWindow. Put the open statement inside the if structure and it will work.
appreciate your quick reply.
When I move the newWindow into the ‘if’ it doesn’t do anything, nothing is shown in the debug window either. I’ve noticed if I comment out the line about the listener, “tableview.setData(data);”, the links work fine, however the rows are not populated with any of images / text / etc… Any idea where the conflict is? I have events firing on other windows fine but I cant figure this one out..
You are not adding anyway the link to the row.
Try something like this:
var row = Titanium.UI.createTableViewRow({
hasChild: true,
height: ‘auto’,
link:customData[i].link
});
perfect! thanks a lot for taking the time!