Go Premium for a chance to win a PS4. Enter to Win


Appcelerator: Making an expandable (accordian) menu

Published on
4,904 Points
1 Endorsement
Last Modified:
Preface: This article is part of a series focused on cross platform mobile app development (specifically Android and iOS) using the Alloy framework and Titanium Studio made by Appcelerator. This article presumes a working knowledge of Titanium Studio and the Alloy framework.

The Alloy framework has great built in elements for making a simple menu, mainly using the TableView (and TableViewRow) system. Unfortunately this does not include any built in functionality for expanding and collapsing rows. At first glance, using the "insertRowAfter" and "deleteRow" methods of the TableView would work for this. Unfortunately there seems to be a bug with the iOS implementation which leads to a "no row found for index" error when deleting rows. To avoid this error, the menu can be implemented as verticaly aligned View elements.

In the menu View elements represent each row. The rows for the submenus start out "hidden" by a wrapper View with a height of 0. Each row of them menu that does not expand will have a "menuAction" property which will be harvested by the "menuClick" onClick listener. The parent row of each submenu will have extra properties which define how big the submenu is and the name of the submenu's wrapper View.

Example Alloy markup would look like this:

file: /views/index.xml
  <Window class="container">
    <ScrollView id="menu" layout="vertical">
      <View class="menuDivider"/>
      <View class="menuRow" onClick="menuClick" menuAction="home">
        <Label class="rowName">Home</Label>
      <View class="menuDivider"/>
      <View class="menuRow" onClick="menuClick" menuAction="away">
        <Label class="rowName">Away</Label>
      <View class="menuDivider"/>		
      <View class="menuRow" onClick="menuExpand" menuItems="3" submenuId="submenuBrowsers" expanded="false">
        <Label class="rowName">Browsers</Label>
        <Label class="expandArrow" id="expandIndicator" text=">"/>
      <View class="submenuWrap" id="submenuBrowsers" layout="vertical">
        <View class="menuDivider"/>
        <View class="submenuRow" onClick="menuClick" menuAction="chrome">
          <Label class="rowName">Chrome</Label>
        <View class="menuDivider"/>
        <View class="submenuRow" onClick="menuClick" menuAction="firefox">
          <Label class="rowName">FireFox</Label>
        <View class="menuDivider"/>
        <View class="submenuRow" onClick="menuClick" menuAction="ie">
          <Label class="rowName">Internet Explorer</Label>
      <View class="menuDivider"/>
      <View class="menuRow"  onClick="menuExpand" menuItems="4" submenuId="submenuPhones" id="network" expanded="false">
        <Label class="rowName">Smart Phones</Label>
        <Label class="expandArrow" id="expandIndicator" text=">"/>
      <View class="submenuWrap" id="submenuPhones" layout="vertical">
        <View class="menuDivider"/>
        <View class="submenuRow" onClick="menuClick" menuAction="android">
          <Label class="rowName">Android</Label>
        <View class="menuDivider"/>
        <View class="submenuRow" onClick="menuClick" menuAction="blackberry">
          <Label class="rowName">Blackberry</Label>	
        <View class="menuDivider"/>
        <View class="submenuRow" onClick="menuClick" menuAction="iphone">
          <Label class="rowName">iPhone</Label>
        <View class="menuDivider"/>
        <View class="submenuRow" onClick="menuClick" menuAction="windows">
          <Label class="rowName">Windows</Label>
      <View class="menuDivider"/>
      <View class="menuRow" onClick="menuClick" menuAction="settings" id="bottom">
        <Label class="rowName">Settings</Label>

Open in new window

The "tss" (Titanium Style Sheet) file specifies the initial styles of the menu. Note the "submenuWrap" has a height of "0" and visible set to "false". Each submenu row is configured to have a height of "36".
file: /styles/index.tss
".container" : {
   backgroundColor: "#63666a"
"#menu" : {
   backgroundColor: "#63666a",
"Label" : {
   color: "#fff",
   height: Ti.UI.FILL,
   touchEnabled: "false"
".menuDivider" : {
   width: Ti.UI.FILL,
   height: '1dp',
   backgroundColor: '#999'
".menuRow" : {
   height: '40dp',
   backgroundColor: '#777',
   width: Ti.UI.FILL
".rowName" : {
   left: '20dp'
".expandArrow" : {
   right: '20dp'
".submenuWrap" : {
   height: '0dp',
   visible: 'false'
".submenuRow" : {
   height: '36dp',
   backgroundColor: '#888'

Open in new window

The controller is where the expanding and collapsing is processed. When a submenu's title row is clicked, the "expandMenu" function will calculate how tall to make the submenuWrap View so that the submenu will be visible. To collapse the menu the submenuWrap is set back to a height of "0". Note: Each row in the menu includes a "divider" which is a height:1 View, hence why the value used to calculate the new height is 37 for each row instead of 36.
file: /controllers/index.js
function menuClick(e) {
   //  Perform processing based on the menuAction of the clicked menu item

var submenuRowHeight = 37;

function menuExpand(e) {
   var menuRow = e.source;
   var submenuWrap = $[menuRow.submenuId];
   if (menuRow.expanded == 'false') {
      var newHeight = (submenuRowHeight * menuRow.menuItems);
      menuRow.expanded = 'true';
   } else if (menuRow.expanded == 'true') {
      menuRow.expanded = 'false';


Open in new window

This is a basic example of a menu that includes optional expanded submenus created in the Alloy framework that will work for both Android and iPhone devices. The nested submenu items could also be have their own submenus allowing for deeper navigation. By incorporating this menu into an app it can be utilzied as a "side swipe" drawer, options list, or any number of uses.
Author:Mark Olsen

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Join & Write a Comment

This video shows how to quickly and easily deploy an email signature for all users in Office 365 and prevent it from being added to replies and forwards. (the resulting signature is applied on the server level in Exchange Online) The email signat…
We’ve all felt that sense of false security before—locking down external access to a database or component and feeling like we’ve done all we need to do to secure company data. But that feeling is fleeting. Attacks these days can happen in many w…
Other articles by this author
Suggested Courses

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month