- Assigning data to your table
- Row properties
- Custom rows
- Grouped rows
- Headers and footers
- Table sections
- Searching within a table
- Events and event object properties
- Pull to refresh
- Hands-on practice
- References and further reading
In this chapter, we’ll examine TableView basics then dig a bit deeper into what you can do with tables. We’ll look at headers, footers, and sections. Finally, we'll wrap up with a look at handling events associated with tables and rows.
Let's start by creating a table. You do so with the
Ti.UI.createTableView() method, like this:
Some key table properties include:
width– controls the dimensions of the table; it doesn't have to fill its parent container
left– controls placement of the table, useful if you want to add buttons or labels above or below it
backgroundImage– controls the background of the table;
transparentis a valid
maxRowHeight– controls table-wide dimensions of rows
headerView– controls the table's header
footerView– controls the table's footer
scrollable(boolean) – controls whether the table is scrollable (vertically)
Assigning data to your table
Next we'll move on to adding rows, which are the core of any table component. Let's take a look at the options that Titanium makes available to you for creating and adding rows.
Table rows are represented by the
Ti.UI.TableViewRow object. This object contains various properties and methods that you can use to style and manage your table rows. You can create TableViewRow objects explicitly using the
Creating object literals in this way is very handy when pulling data out of a database or across the network. By explicitly creating TableViewRow objects, you gain access to a few handy methods such as
Emptying a table
You can empty a table by setting its
data property to an empty array.
For best performance, create an array of row objects (object literals or explicitly typed) and then assign them to the table using either
setData() or by setting the
data property. In community tests,
appendRow() performs significantly slower than
setData() when adding thousands of rows to a test table. Though this is an uncommon scenario, it is still best to manage your tables, and all UI components, in the most performant manner possible.
If your app does however currently require a table with thousands of rows, you should may want to reconsider your UI. Users won't want to scroll through that many rows to find the one that interests them. Even on the fastest device, such a table will be slow. Consider some sort of drill-down interface, filtering mechanism, or alternate UI/UX paradigm to reduce the size of the available table.
Now that we've seen how to create tables and rows, let's learn a bit more about the built-in row properties. TableViewRow objects have various useful properties that you can use to add style and functionality to your tables.
className– set this property equal to an arbitrary string to optimize rendering performance. On both iOS and Android, setting this property enables the operating system to reuse table rows that are scrolled out of view to speed up the rendering of newly-visible rows. On iOS, the string you supply is used to specify the reuse-identifier string (setdequeueReusableCellWithIdentifier); on Android, it is used within a custom object reuse method within Titanium.
leftImage– set this property equal to an image URL (local or remote) to display that image to the left of the row's title
rightImage– set this property equal to an image URL (local or remote) to display that image to the right of the row's title
backgroundImage– set this property equal to an image URL (local or remote) to display that image in the background of the row
backgroundColor– set this property to a color string to set the row's background color
So let's augment the properties of the prior TableView example rows. In the following code we'll utilize the above properties on a per row basis, making for a highly styled set of table rows.
Row indicators are icons that provide visual cues to your users related to your table rows. As shown in the following graphic, Android supports two built-in icons while iOS supports three. Each is a boolean value set with the property listed following the graphic.
hasChild– indicates sub-table or additional rows (most commonly used on iOS with the navigation controller)
hasDetail– indicates a detail view or alert will appear when row is tapped (not supported on Android)
hasCheck– an on/off or yes/no indicator
If the stock properties don't suit your needs, you can add Views, ImageViews, Labels, Buttons, and so forth as children of your rows. This gives you enormous flexibility when laying out the content of your table.
Let's once again visit the example TableView code. This time we'll modify it to use explicit TableViewRows. To these TableViewRows we will add a label, button, and image in a custom format.
This is just one very simple example of how you can create custom rows for your tables. You can literally embed almost any Titanium UI component in any visual configuration to create your rows.
On iOS, you can set the
style property of the table to display table sections as separate components, as shown in the following graphic.
Headers and footers
You can use the built-in
footerTitle to add header and footer titles to your tables. This convenience property allows you to enter arbitrary text for these titles, but will only use the default font formatting.
A more flexible technique is to use Views for your headers and footers. You can create a view and set it in the table's
footerView properties. Here's an example of setting both the header and footer using these properties.
Table sections enable you to create groupings of related rows within your table. Table sections can have headers and footers. You create sections with the
Ti.UI.createTableViewSection() method. Then, you add rows to that section and set the table's
sections property equal to an array of sections. Like this:
Prior to Release 3.0, you need to use the TableView property
data to set the sections.
Adding or removing sections from a table
The information in this section only applies to Release 3.0 and later.
After creating and rendering a TableView, you can add sections using the TableView methods:
insertSectionAfter, you can insert individual sections anywhere in the TableView, but using
appendSection, you can only insert individual or multiple sections to the end of the TableView. For the iOS platform, you can optionally choose to render the addition with an animation.
You can remove individual sections from a TableView using the TableView method,
Prior to Release 3.0, you need to use the TableView property,
data, to refresh the entire table.
Iterating over the rows in a table using Section
Titanium always creates at least one table section for you even if you don't explicitly create a TableViewSection. This is handy because sections have a
rows property, which tables do not. This property stores an array of all the rows in that section. In other words, you can use sections to loop programmatically through the rows in a table.
Searching within a table
You can enable searching by adding a search bar to your tables. As users enter text, rows are filtered such that only those containing the text in their
title property remain visible. The search is not a leading character search. In other words, searching for the letter "b" would display all rows containing that letter anywhere within their
As you can see in the image below, there are platform differences in the way the search bar is rendered. A couple of the properties used bear further explanation:
truethen show a cancel ("clear") button always. If
falseon Android never show that button; on iOS don't show Cancel until the user begins typing.
hideSearchOnSelectionis an iOS only property. With it set to
trueas soon as the user taps a row in the search results, the search box is cleared and the Cancel button is hidden. Set it to
falseand the search text and button would remain. The default value is
trueAndroid operates as if this value were set to
Events and event object properties
As shown in the preceding code, you can add event listeners to tables. From there, you can access sections, rows, and nested child elements. While you could add event listeners directly to the rows, we don't recommend it. Doing so adds unnecessary memory and processing overhead to your app. Also, it can be difficult and repetitive adding listeners to all the various rows.
As shown in the code above, you have access to an event object,
e, that holds important table information within the listener. For example, some of the key properties of that event object include:
index– the ordinal index number of the row that received the event
row– the object representing the row that received the event
rowData– the properties of the row that received the event
source– the object that received the original event
section– the table section that received the event
For the iOS and Mobile Web platforms, the scroll events,
scrollend, have different key properties than the other table view events:
contentOffset– point that indicates how far the content in the table has moved. Since a table view can only scroll vertically, only the y-coordinate changes. A positive value indicates scrolling upward and a negative value indicates scrolling downward. See image below for a diagram of the content offset property.
contentSize– dimensions of the content, for example, the content height of a table with a few rows is smaller than the visible height. As illustrated in the image below, the pink box indicates the contentSize property and the blue box indicates the size property.
size– visible dimensions of the table view.
Pull to refresh
Since Release 2.1 on iOS, you can create a custom view that is only revealed when the user pulls the table down. This custom view is commonly used to implement a pull-to-refresh feature that updates the table data when the user drags the table downward. Create a custom View object containing other view objects, then set the TableView's
headerPullView to this object. Monitor the
scrolling event to see when the user pulls the table downward to initiate an update to the table.
While this example provides all the logic necessary to create the control, it is recommended to refactor it as a CommonJS module before using it for a production application.
In this activity, you will create a custom table that doesn’t fill the entire viewport. The table will contain customized rows with background images that differ based on the row’s location within the table. Each row will contain two images and two labels. When you tap a row, an event listener will determine if either of the images was the object that received the tap. If so, that image will be swapped with an alternate graphic.
When completed, your app should match what is shown in this movie: http://assets.appcelerator.com.s3.amazonaws.com/video/320.mov
Download the starting point code from http://assets.appcelerator.com.s3.amazonaws.com/app_u/code/320.zip. This archive includes the necessary graphics.
- Download, extract, and then import the TableView project into Studio. Confirm that the tiapp.xml file has appropriate values then close that file. Open app.js in Studio.
- Following the comments included in the starting app.js file, add these elements to the app:
- Set the window background to images/gradientBackground.png
- Add a page heading of “Custom Table” with a dark blue, 18 px, bold font that is positioned at the top-left of the window
- Define a table that is positioned below the label and which is 90% of the width of the screen and 85% of its height. Set the table’s background color to transparent and for iOS, set the separator style to NONE.
- Write a function named makeRow() following the specifications included in the comments within the code.
- Your makeRow() function should accept an object that will be used to pass in four values: the row number, primary label text, secondary label text, and a custom “which image” indicator string. Possible values for the row number parameter will be an integer or the string ‘last’. Possible values for the myImage string property will be a, b, c, blue, and red. You’ll use this myImage property to determine which element in the row is tapped and to swap images accordingly.
- Each row should contain two images:
- Left image: if an even-numbered row = ‘images/imageA.png’ otherwise ‘images/imageB.png’. It should have a custom property named myImage that is set equal to the myImage parameter passed to your makeRow() function at run time.
- Right image: use ‘images/notificationBadge.png’ and set its custom myImage property equal to ‘blue’
- Each row should contain two labels:
- Primary label: use a bold, 16 px font positioned to the right of the left image with its text set equal to the primary label parameter passed to makeRow() at run time. Be sure to set a height for the label.
- Secondary label: use a bold, 13 px font positioned below the primary label with its text set equal to the secondary label parameter passed to makeRow() at run time. Be sure to set a height for the label.
- Each row’s background image should be set to ‘images/middleRow.png’ and the selected background image to ‘images/middleRowSelected.png’. If the row number parameter is 0, set the row’s background image to ‘images/topRow.png’ and selected background image to ‘images/topRowSelected.png’. If the row number parameter equals ‘last’ then use ‘images/bottomRow.png’ and ‘images/bottomRowSelected.png’ for the background images and use ‘images/imageC.png’ for the left image.
- The makeRow() function should return a Titanium.UI.TableViewRow object.
- Use a for-loop to create an array of 8 rows for your table, calling makeRow() in each iteration of the loop. Set the primary label to “This is row” plus the row-number indicator. Set the secondary label to “Subtitle” and the row-number indicator. Set the myImage value to either ‘a’ or ‘b’ depending on whether your loop counter is odd or even. (Hint: use the modulus operator, %, to calculate this odd/even value.)
Push one additional row into the rows array. This row should pass these values:
- row number: ‘last’
- primary label: ‘This is the last row’
- secondary label: ‘The last subtitle’
- myImage: ‘c’
- Add a click event listener to your table. Check the myImage property of the event source. Using a switch or if-else test, determine if myImage is set equal to a, b, c, blue, or red. If a, b, or c, swap the left image such that imageA becomes imageB, imageB becomes imageC, and imageC becomes imageA again. If the myImage property equals blue, the right image should change to images/notificationUnreadBadge.png otherwise it should swap back to images/notificationBadge.png.
- Save and run the project in the simulator. Confirm that the user interface matches the screenshots shown below. Confirm that your event listener functions properly:
- When the row is tapped, the background image should swap to the green “selected” version.
- If the left image is tapped, the letter should advance to the next letter, looping back to the beginning properly.
- If the right image is tapped, it should swap between the red and blue versions properly.
References and further reading
- Finished code: http://assets.appcelerator.com.s3.amazonaws.com/app_u/code/345-finished.zip
- TableView API docs: http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.UI.TableView-object
- TableViewRow API docs: http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.UI.TableViewRow-object
- Kitchen Sink code: https://github.com/appcelerator/KitchenSink
In this section, you reviewed TableView basics, then explored some of its more powerful functionality. We looked at headers, footers, and sections. Finally, we wrapped up with a look at handling events associated with tables and rows. In the next section, we'll see how we can add custom icons and splash screens to our Titanium apps.