skip to content

Custom row for TableView in Appcelerator Titanium

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

4.16.2010

Thanks, that’s really great to share this.

4.16.2010

Nice tutorial!

4.16.2010

Well done, thanks for sharing. The classname trick is wise and save ressources.

4.28.2010

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!

4.28.2010

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 :)

4.28.2010

Try in this lines:

// create table view event listener
tableview.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.

4.28.2010

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?

4.28.2010

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 :)

4.28.2010

Hey Dan,

Thanks for the awesome customer service :)

I tried both of those and the log returns

[INFO] null

4.28.2010

Ok, I’ll take a deeper look at your code and let you know.

4.28.2010

i just deleted the code :( , can you send it over email to dan.tamas [at] gmail [dot] com ?

4.28.2010

I found it :D 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

4.28.2010

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 :)

5.4.2010

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

5.19.2010

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!

5.19.2010

Can you post some code on http://pastie.org so I can take a look at your code?

5.19.2010

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!

5.19.2010

I’m working on a countdown example for titanium, with a little of OOP for javascript :)
For the popup window I don’t know yet, I didn’t need it so far :) , but you could ask on the Appcelerator list

http://developer.appcelerator.com/questions/created

5.19.2010

Will do – thanks :)

looking forward to the countdown :)

5.27.2010

Awesome tutorial, I was trying to figure out from the api how to get this working and your tutorial outlined it nicely! Thanks!

5.29.2010

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 :)

6.2.2010

Awesome! :)

How do I set the searchBar to search by the label in the custom row?

6.11.2010

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.

8.19.2010

ya men
pls given some example of blackberry for this type.

8.21.2010

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.

8.23.2010

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.

9.10.2010

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

9.10.2010

@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 :)

9.10.2010

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

9.10.2010

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

9.10.2010

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.

9.11.2010

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

9.11.2010

I’m afraid that without creating your own hash for the rows you cannot call the rows by name.

9.11.2010

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

9.22.2010

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?

9.22.2010

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.

10.12.2010

Looks like there is some minor mistake in example code:
for (var i = CustomData.length – 1; i >= 0; i–) {

Should be “>” instead of “>”.

10.13.2010

In the custom row how can you add a button with it’s own unique colour to it?

Thanks
Shaun

10.19.2010

I think yes. Just use a backgroundImage property for that button.

10.22.2010

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

10.29.2010

Thanks, should be ok now :)

11.26.2010

//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

11.26.2010

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….

12.23.2010

Thank you, you shared a good concept :)

1.3.2011

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.

1.3.2011

‘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.

1.3.2011

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?

1.3.2011

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.

1.4.2011

Aha, thanks for the help!

1.18.2011

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!

1.19.2011

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.

1.31.2011

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++) {

—————————

1.31.2011

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.

2.9.2011

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!

2.9.2011

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.

2.9.2011

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..

2.9.2011

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
});

2.9.2011

perfect! thanks a lot for taking the time!


Switch to our mobile site