Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Numerous updates for TIDOC-3072

...

This tutorial walks you through the process of building a simple Titanium application for iPhone and Android that lets users enter and view their favorite books. It  It touches on the following topics:

  • Alloy framework
  • Working with Backbone models and collections
  • Displaying data in a TableView
  • Using platform-specific resources and styles
  • Handling events events 

You can download a completed version of the project.

...

To create a new Alloy project:

  1. In Studio, select select File > New > New > Mobile  Mobile App Project.
  2. Select the the Default Alloy Project type  type and click Next.
  3. On the the Project Location screen screen, enter enter FaveBooks for the Project name and and com.your- domain.favebooks. Replace your-domain with  with your personal or organization's domain. Leave the other fields at their default values.
  4. Click Click OK.

The new project opens in Studio.

...

The heart of the FaveBooks app is the data containing the user’s books, so we’ll start by defining our data model. Alloy uses Backbone to provide its its data modeling and collection features

...

  1. Right-click or Control-click on the top-level project folder and select New select New > Alloy  Alloy Model, or press Cmd+Shift+M (Mac) or Ctrl+Shift+M (Windows).
  2. In the New Model dialog, make the following entries:
    • For For Model name, enter enter books.
    • Select Select sql from the  from the Adapter menu menu.
    • In the the Schema table table, add two fields named named title and  and author and  and set their types to to text. 
  3. Click OK.   

This creates a new file in the the app/models directory called  directory called books.js. This   This file describes the data model you just created. The The configsection section, in particular, defines the data columns, adapter type, and collection name.

Code Block
linenumberstrue
languagejs
exports.definition = {
    config: {
        columns: {
            "title": "text",
            "author": "text"
        },
        adapter: {
            type: "sql",
            collection_name: "books"
        }
    ...
}

...

Next you'll write code that creates a Collection from the model definition, creates a single book, and saves it to device storage. Eventually you will add features to let users add books, but for now this will give us some data to work with.

To initialize the book collection: 

  1. Open Open controllers/index.js and  and delete the the doClick() function  function definition added by the project wizard.

  2. Get a reference to our collection: 

    Code Block
    languagejs
    var myBooks = Alloy.Collections.books;
  3. Create a new model object (book) and assign it a title and author: 

    Code Block
    languagejs
    var book = Alloy.createModel('books', { 
       title : 'Great Expectations', 
       author: 'Charles Dickens' 
    });
  4. Add the book to the collection and persist it to the database. 

    Code Block
    languagejs
    myBooks.add(book); 
    book.save();
  5. Save your changes to to index.js.

Now that our application has some data, let’s display it in a table.

...

In Alloy, collection data can be be synchronized to  to a view object, or a single model can be bound to a view component. At runtime, Alloy monitors your application's collections for changes and updates any synchronized view objects with the new data. Specifically, the Backbone add, change, destroy, fetch, remove, and reset events are monitored for changes. In this case, each time we call the add() method  method on the myBooks collection the view is synchronized with the new data.

  1. Open Open views/index.xml.

  2. Delete the the <Label> element that was generated by the project wizard and add a <TableView> element in its place.

  3. Insert a <TableViewRow> element inside the the <TableView> element element, as shown below: 

    Code Block
    languagehtml/xml
    <Alloy>
        <Window class="container">
            <!-- Add TableView and  TableViewRow -->
            <TableView>
                <TableViewRow></TableViewRow>
            </TableView>
        </Window>
    </Alloy>

    The The <TableViewRow> element  element will be repeated for each element in our data set.

  4. Add a <Collection> element to the top-level level <Alloy> element  element and set its its src property to to books. This corresponds to the name of the collection we created before. 

    Code Block
    languagehtml/xml
    <Alloy> 
          <Collection src="books"/> 
          <Window class="container"> 
          ... 
          </Window> 
    </Alloy>
  5. To synchronize the collection with the view, assign assign books to the the TableView element element's  dataCollection attribute.  

    Code Block
    languagehtml/xml
    <Alloy>
        <Collection src="books"/>
        <Window class="container">
            <!-- Asssign the collection to the TableView -->
            <TableView dataCollection="books">
                <TableViewRow></TableViewRow>
            </TableView>
        </Window>
    </Alloy>
  6. Using curly-bracket syntax, assign the title from each book (that is, each model in the collection) to the the title attribute  attribute of the the <TableViewRow>. The final contents of of index.xml should  should look like the following: 

    Code Block
    languagehtml/xml
    <Alloy>
        <Collection src="books"/>
        <Window class="container">
            <TableView dataCollection="books">
                <TableViewRow title="{title}" author="{author}"></TableViewRow>
            </TableView>
        </Window>
    </Alloy>  
  7. Open Open styles/index.tss.
  8. By default, Android applications built with Titanium use the AppCompat theme, which uses a dark background with light text, while iOS defaults to black text and a black background.  In order to see text, change the background to white for iOS only by adding the platform attribute to the container rule.

    Code Block
    ".container[platform=ios]": {
    	backgroundColor: 'white'
    }

...

  • From the Target menu in the top-right corner of Studio, select select iOS Simulator > iPhone. By default, Studio is configured to automatically launch when you select a target platform. Wait for the app to complete launching in the simulator.
     Image RemovedImage Added

 

To launch the app in an Android emulator :

  • When the application has finished launching in the iPhone simulator, select select Android Emulator > titanium_1_WVGA800 to  to launch the app. 
Box Note

The titanium_1_WVGA800 string corresponds to the name of the Android virtual device that Studio creates by default. If you have installed other AVDs, you can select one of those.

The screenshots below show the app running on an iPhone simulator and an Android emulator. As you can see, the table is populated with the book from the collection.

Image RemovedImage Removed

Image Added Image Added

Note the following:

  • On iPhone the top of the the TableView is  is partly obscured by the iOS status bar. We’ll fix this later in this tutorial.
  • On the Android, the book is hidden under the app label. We'll fix this later in this tutorial.
  • The table length increases by a row each time you launch the app, since we are saving the (same) book to each time.

...

The initial launch of the Android emulator can be quite slow and Studio may timeout before it can install and launch the application. If  If this happens just click click Runin  in Studio again. Subsequent launches should not time out on the same virtual device.

Create a book detail view

...

To create the book detail view :

  1. Right-click or Control-click on the project folder and select select New > Alloy Controller, or press Cmd+Shift+C (Mac) or Ctrl+Shift+C (Windows).

  2. In the New Controller dialog, enter enter bookdetails in  in the Controller name text field, and click OK. This creates the following new files:

    • controllers/bookdetails.js  – The controller’s JavaScript controller.
    • views/bookdetails.xml  – The controller’s view
    • styles/bookdetails.tss  – Styles for the controller
  3. Open Open views/bookdetails.xml in  in the editor.

  4. Replace the the <View> element  element with a <Window> element element

    Code Block
    languagehtml/xml
    <Alloy>
       <Window class="container">
       </Window> 
    </Alloy>
  5. Add two two <Label> elements  elements to the the <Window> element element. Give the first one an ID of of titleLabel and  and the other an ID of of authorLabel, as shown below. 

    Code Block
    <Alloy> 
       <Window class="container"> 
          <Label id="titleLabel"></Label>
          <Label id="authorLabel"></Label> 
       </Window> 
    </Alloy>
  6. Open Open styles/bookdetails.tss and  and a background color for iOS to the the .container rule rule

    Code Block
    ".container[platform=ios]" : { 
       backgroundColor: 'white' 
    }

Next we’ll add an event handler to the the <TableView> that  that opens this new window and displays the book's information.

...

To respond to user selection, you add an an onClick attribute  attribute to the the <TableView> element element, and assign it the name of the function to invoke. The function creates a new instance of the the bookdetails Alloy  Alloy controller, passing it the title and author of the selected book, so it can display those values in the the <Label> elements  elements you defined there.

To display the book details screen on selection:

  1. In the Project Navigator, open open views/index.xml.

  2. Add an an onClick attribute to the the <TableView> element  element and set its value to to showBook 

    Code Block
    languagehtml/xml
    <TableViewRow title="{title}" author="{author}" onClick="showBook"></TableViewRow>
  3. Open Open controllers/index.js and  and add a function named named showBook() that takes a single argument called called event . This is the event object passed to the function that contains information about the selected item. 

    Code Block
    function showBook (event) { 
    }
  4. First, let’s add code to extract the title and author of the selected book from the the event.source object object

    Code Block
    function showBook(event) {
        var selectedBook = event.source;
        var args = {
            title: selectedBook.title,
            author: selectedBook.author
        };
    }
  5. Lastly, we create a new instance of the Alloy bookdetails controller, passing it the the args object  object containing the title and author data, and open the view. 

    Code Block
    function showBook(event) {
        var selectedBook = event.source;
        var args = {
            title: selectedBook.title,
            author: selectedBook.author
        };
        var bookview = Alloy.createController("bookdetails", args).getView();
        bookview.open();
    }      
  6. Lastly, we need to update bookdetails.js to display the title and author values passed to the the createController() method, or display a default title and author if none were provided: 

    Code Block
    var args = arguments[0] || {};
    $.titleLabel.text = args.title || 'Default Title';
    $.authorLabel.text = args.author || 'Default author'; 
  7. Save your changes.

...

Modify the book detail layout

Titanium supports three three layout modes: composite (or absolute), vertical, and horizontal. In composite mode, you specify a child view's coordinates on a grid relative to its parent container's top/left or bottom/right corner. If not specified, a child view is centered on the display. In this section you'll apply a vertical layout to the book details view, and then tweak the style.

  1. Open Open views/bookdetails.xml.

  2. Wrap the two two <Label> elements  elements within a <View> element element, as shown below. 

    Code Block
    languagehtml/xml
    <View> 
       <Label id="titleLabel"></Label> 
       <Label id="authorLabel"></Label> 
    </View>   
  3. Add a layout attribute to the the <View> element  element and set its value to to vertical 

    Code Block
    languagehtml/xml
    <View layout='vertical'> 
      <Label id="titleLabel"></Label>
      <Label id="authorLabel"></Label> 
    </View>
  4. Open Open styles/bookdetails.tss and add the following style rules to align the labels along the left side of the screen, and to set their font sizes. We also add some padding between the two labels by 10.

    Code Block
    "#titleLabel": {
        font: {
            fontSize: '30'
        },
        left: '10'
    },
    "#authorLabel": {
        font: {
            fontSize: '20'
        },
        left: '10'
    }

Save your changes and build again for iPhone or Android. The title and author should now be positioned vertically within the view, and aligned to the left side of the screen. To get back to the original table, you can click the Back button  button on Android.  On iOS, the application is stuck here.  Instructions on adding an iOS navigation controller is detailed in a later section of  of this tutorial.

Add platform-specific styles to the TableView

...

To fix this we will apply a platform-specific style to the  to the <TableViewRow> element element. During the build process, platform-specific styles are applied only to builds on the target platform. The following is an example of a platform-specific style that makes all Labels blue in iOS builds.

...

In this case we’ll increase the font size used by each each <TableViewRow> element element, and set the height of each row to provide some space around the text.

To increase the table row height on Android:

  1. Open Open views/index.tss and  and add the following style rule to the end of the document: 

    Code Block
    "TableViewRow[platform=android]": { 
        font: { 
           fontSize: '24' 
        }, 
        height: '40' 
    }
  2. Save your changes and build the build the application for Android. The TableView should look like the following screenshot:

Add a NavigationWindow for iOS

Unlike Android, iOS devices do not provide a physical back button for navigation. The The NavigationWindow is an iOS-only UI control that provides an easy way to display and navigate hierarchical content. (In native iOS terms, the control is an implementation of the iOS iOS navigation controller interface.) On Android, you get this type of navigation for "free". The  The NavigationWindow will wrap the original <Window> element and provide a title bar above the <TableView> control, solving this layout issue.

To do this we'll take advantage of an Alloy feature called called  platform-specific resources that lets you  that lets you define platform-specific view, controller, and style files. At build time, only those resources specific to the target platform are included. There   There are two parts to this:

  • Creating a new iOS-specific entry point (a new version of of index.xml) that includes the the NavigationWindow element
  • Updating the controller code to handle the difference between the iOS and Android implementations. In this case we'll use use platform-conditional code to handle the cross-platform code differences.

To create the iOS-specific view with a NavigationController:

  1. In Project Explorer, create a new folder named named ios in the  in the app/views folder folder.

  2. Create a new file named named index.xml in  in the new folder. 

  3. Copy the contents of of views/index.xml to  to views/ios/index.xml .

  4. Wrap the existing existing <Window> element  element with a <NavigationWindow> element: 

    Code Block
    <Alloy>
        <Collection src="books"/>
        <NavigationWindow>
            <Window class="container" title="My Books">
                <TableView dataCollection="books" id="bookTable">
                    <TableViewRow title="{title}" author="{author}" onClick="showBook"></TableViewRow>
                </TableView>
            </Window>
        </NavigationWindow>
    </Alloy>
  5. Save your changes.

The NavigationWindow control has a different API than Window, so our code now needs to account for those differences. We  We could create a platform specific controller folder for iOS (controllers/ios/index.js),  but the code differences are small enough that we’ll use use platform-conditional code to  to include the proper code for each platform.

To update the controller code for platform differences:

  1. Open Open controllers/index.js.

  2. Modify the the showBook() method to account for the platform differences when opening the details view. On iOS, you now need to open the book details view using the the NavigationWindow object's openWindow() method: 

    Code Block
    function showBook(event) {
        var selectedBook = event.source;var args = {
            title: selectedBook.title,
            author: selectedBook.author
        };
        var bookview = Alloy.createController("bookdetails", args).getView();
        if (OS_IOS) {
            $.index.openWindow(bookview);
        }
        if (OS_ANDROID) {
            bookview.open();
        }    
    } 

    As you can see, in the iOS case the handler now opens the window inside the NavigationGroup; the Android code branch uses the same code to open the window (addview.open()).

  3. Save your changes and test again on an iPhone device or simulator.

The first screen should now display the the TableView within the the NavigationWindow.

 

Allow user to add books

Of course, we need a way for users to add their own books. Like we did for the book details view, we will create a new Alloy controller composed of three files: the XML view, the JavaScript controller, and the TSS styles file.

The UI will contain two input text fields where the user enters the book title and author, and an "Add" button to update the books collection with the new data. To  To take advantage of native UI components familiar to iOS and Android users, we'll use a Toolbar component on iOS and a Menu component on Android to contain the "Add" button.

To create the add book feature:

  1. In the Project Explorer, right-click on the project folder and select select New > Alloy  > Alloy Controller.

  2. In the New Controller dialog, give the controller the name name addbook.Click  Click OK.

  3. Open Open views/addbook.xml and  and add the following markup: 

    Code Block
    linenumberstrue
    languagehtml/xml
    <Alloy>
        <Window class="container">
            <View layout="vertical">
                <TextField id="titleInput" hintText="Title..."></TextField>
                <TextField id="authorInput" hintText="Author..."></TextField>
                <Button id="insertBookButton" onClick="addBook">Add</Button>
            </View>
        </Window>
    </Alloy>

    This creates input text fields where the user will enter the book title and author, and a button to add the new book to the collection. The button’s button’s onClick handler  handler will call the the insertBook() function function, which you’ll create next.

  4. Open Open styles/addbook.tss and  and add the following styling: 

    Code Block
    linenumberstrue
    languagecss
    ".container[platform=ios]" : {
      backgroundColor: 'white'
    }
    "TextField" : {
    	borderStyle: Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
    	top: 10,
    	left: 20,
    	right: 20,
    	height: 40
    }
  5. Open Open controllers/addbook.js, remove the existing code in that file and add the following: 

    Code Block
    languagejs
    var myBooks = Alloy.Collections.books;
  6. Define a new function named named addBook() with  with the following code: 

    Code Block
    linenumberstrue
    languagejs
    function addBook() {
        var book = Alloy.createModel('books', {
            title : $.titleInput.value,
            author : $.authorInput.value
        });
        myBooks.add(book);
        book.save();
        // Close the window.
        $.addbook.close();
    }
  7. For iOS, open open views/ios/index.xml and  and insert a <Toolbar>  inside the the <Window> element below the the <TableView> element: 

    Code Block
    languagelinenumbershtml/xmltrue
    <Toolbar bottom="0">
        <Items>
            <Button id="add" title="Add book" onClick="addBook"/>
        </Items>
    </Toolbar>
  8. For Android, open open views/index.xml and  and add the following  <Menu> and  and <MenuItem> elements below the the <TableView> element: 

    Code Block
    linenumberstrue
    languagexml
    <Menu id="menu" platform="android">
        <!-- Cannot specify node text.  Use attributes only. -->
        <MenuItem id="addBook"
            title="Add book"
            onClick="addBook"
            showAsAction="Ti.Android.SHOW_AS_ACTION_IF_ROOM" />
    </Menu>
  9. Open controllers/index.js and add the following code to open the addbook controller when the appropriate native control is used.  For Android, depending on the device, the Add button can either be accessed from the Action Bar at the top of the display or by using the Menu button.  For iOS, use the toolbar at the bottom of the screen.

    Code Block
    linenumberstrue
    languagejs
    function addBook(){
        var myAddBook = Alloy.createController("addbook",{}).getView();
        if (OS_IOS) {
            $.index.openWindow(myAddBook);
        }
        if (OS_ANDROID) {
            myAddBook.open();
        }
    }
  10. Save your changes and test on a device or emulator.

...