Indicators

Indicators are used to show that activity is taking place, even it it’s not visible, and in some cases, to show some measure of the progress of the activity. Mojo has four indicator widgets, but they belong to two types:

  • Activity indicator, or Spinner, which spins without showing progress

  • Progress indicator, which shows both activity and progress

The spinner is the only activity indicator, but there are three progress indicator widgets:

  • Progress Pill, a wide pill that is styled to match the View menu and the palm-header scene style

  • Progress Bar, a narrow horizontal bar with a blue progress indicator

  • Progress Slider, which is intended for streaming media playback applications

The Spinner widget is most appropriate when there isn’t much space in the layout for an indicator or when the duration of the activity is hard to estimate. In other cases you should use a progress indicator; it’s preferable because it gives the user a bounded sense of duration.

Spinners

Use a spinner to show that an activity is taking place. The framework uses a spinner as part of any activity button, and you’ll see it used in the core applications. There are two sizes; the large spinner is 128 × 128 pixels, and the small spinner is 32 × 32 pixels. These sizes are optimized for the Palm Prē screen and may vary on other devices, but the spatial and visual characteristics will be maintained on other devices.

Back to the News: Adding a spinner for feed updates

There aren’t any long operations in News other than the feed updates, which are asynchronous. We’ll add a spinner to the feed list whenever an update is in progress, demonstrating a simple application of an indicator.

This will also demonstrate the technique for including widgets within a list entry, a powerful Mojo feature introduced in Chapter 3. You already know that you can use widgets to display dynamic data; by combining them into lists you can create complex UI controls with the widgets as building blocks. You may want to review Chapter 3 if you have questions after reading these next few paragraphs.

You can design list entries to include other widgets, including other lists, in almost the same way that you use widgets outside of lists. The differences are that the list’s model includes the widgets’ models, and that you declare widgets within the list’s itemTemplate, using a name attribute to identify each widget.

In this example, a Spinner widget is included in each feedListWgt entry, which will be activated when the corresponding news feed is updated through an Ajax request. Start by adding the spinner declaration into the feedListWgt’s row template, views/feedList/feedRowTemplate.html:

<div class="feedlist-info icon right" id="info"></div>
<div x-mojo-element="Spinner" class="right" name="feedSpinner"</div>
<div class="feedlist-title truncating-text">#{title}</div>
<div class="feedlist-url truncating-text">#{-url}</div>

The new line is the div with the name feedSpinner and is simply a declaration of the Spinner widget. The syntax should start to seem familiar by now.

By including it into the feedListWgt list item’s template, we have implicitly directed the List widget to insert a new spinner element in the DOM whenever it creates a new entry. It’s the same as creating a spinner outside of a list, except in one major way: the spinner’s model property must be part of the feedListWgt’s items array.

We still have to set up the Spinner widget, which we do in the setup method of feedList-assistant.js, but we don’t include a model in the call to setupWidget(), as that is assumed to be part of the feedListWgt’s items array:

    // Setup the feed list, but it's empty
this.controller.setupWidget("feedListWgt",
     {
        itemTemplate:"feedList/feedRowTemplate",
        listTemplate:"feedList/feedListTemplate",
        addItemLabel:"Add...",
        swipeToDelete:true,
        renderLimit: 40,
        reorderable:true
    },
    this.feedWgtModel = {items: this.feeds.list});

// Setup event handlers: list selection, add, delete and reorder feed entry
this.showFeedHandler = this.showFeed.bindAsEventListener(this);
this.controller.listen("feedListWgt", Mojo.Event.listTap,
    this.showFeedHandler);
this.addNewFeedHandler = this.addNewFeed.bindAsEventListener(this);
this.controller.listen("feedListWgt", Mojo.Event.listAdd,
    this.addNewFeedHandler);
this.listDeleteFeedHandler = this.listDeleteFeed.bindAsEventListener(this);
this.controller.listen("feedListWgt", Mojo.Event.listDelete,
    this.listDeleteFeedHandler);
this.listReorderFeedHandler = this.listReorderFeed.bindAsEventListener(this);
this.controller.listen("feedListWgt", Mojo.Event.listReorder,
    this.listReorderFeedHandler);

// Setup spinner for feedlist updates
this.controller.setupWidget("feedSpinner", {property: "value"});

Most of the feedList assistant’s setup method is shown in this code sample; the only addition is the last line of code (and preceding comment). It sets up the spinner, naming the model property as value, but the model is not in the arguments list; the list’s model, this.feedWgtModel, is used implicitly as the spinner’s model.

The feedList default data is defined at the beginning of the stage-assistant.js. Add the value property to each default list entry, and set it to false. This is the spinner’s model, and will start as false since there is no activity. You need to include this for every feedList entry, as in this example:

{
   title:"New York Times",
   url:"http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml",
   type:"rss", value:false, numUnRead:0, newStoryCount:0, stories:[]
},

There’s one other place where a new feed list entry is created; in the checkOk method of addDialog-assistant.js:

//  If a new feed, push the entered feed data on to the feedlist and
//  call processFeed to evaluate it.
if (this.feedIndex === null) {
    this.feeds.list.push({title:this.nameModel.value,
        url:this.urlModel.value, type:"", value:false, numUnRead:0,
        stories:[]});
    // processFeed - index defaults to last entry
    feedError = this.feeds.processFeed(transport);
}
else    {
    this.feeds.list[this.feedIndex] = {title:this.nameModel.value,
        url:this.urlModel.value, type:"", value:false, numUnRead:0,
        stories:[]};
    feedError = this.feeds.processFeed(transport, this.feedIndex);
}

The spinner is set up and integrated into the list’s template and model. All that remains is to activate and deactivate the spinner at the right times. Those times are just before the Ajax request is made (spinner activated) and after the response is received (spinner deactivated) whether the request was successful or not.

There are four changes to make, all in the feeds model:

updateFeedRequest

Activate before Ajax request

updateFeedFailure

Deactivate

updateFeedSuccess

Deactivate after processing new feed data, and activate before another feed update request is made

The sample below shows the changes to updateFeedSuccess(), which includes both an activate and a deactivate call:

// Process the feed, passing in transport holding the updated feed data
var feedError = this.processFeed(transport, this.feedIndex);

// If successful processFeed returns News.errorNone,
if (feedError !== News.errorNone)    {
    // There was a feed process error; unlikely, but could happen if the
    // feed was changed by the feed service. Log the error.
    if (feedError == News.invalidFeedError)    {
        Mojo.Log.info("Feed ", this.nameModel.value,
          " is not a supported feed type.");
    }
}

News.feedListChanged = true;
// Change feed update indicator & update widget
var spinnerModel = this.list[this.feedIndex];
spinnerModel.value = false;
this.updateListModel();

// If NOT the last feed then update the feedsource and request next feed
this.feedIndex++;
if(this.feedIndex < this.list.length) {
    this.currentFeed = this.list[this.feedIndex];

    // Request an update for the next feed but first
    // change the feed update indicator & update widget
    spinnerModel = this.list[this.feedIndex];
    spinnerModel.value = true;
    this.updateListModel();

    this.updateFeedRequest(this.currentFeed);
} else {

    // Otherwise, this update is done. Reset index to 0 for next update
    this.feedIndex = 0;
    News.feedListUpdateInProgress = false;

}
     .
     .
     .

In each case, we set the spinnerModel.value and call this.updateListModel() to update the model changing the state of the spinner. The spinner’s model is accessed by referencing the this.feedIndex, the array index for the feed that is being updated, then setting that entry’s value property to change just the spinner in that list entry.

Run the application, and the feeds will update one after another. If you wait for the feed interval to pass, they will update again. You will see a spinner appear between the feed title and the unread count badge on the left side, as shown in Figure 5-1, with the spinner on the BBC News feed item.

Spinner on News feed updates

Figure 5-1. Spinner on News feed updates

Spinners only take up space when they’re active. In some cases, if the feed title is long enough, you’ll see the title truncated to accommodate the spinner, then resize to fill the vacated space after the spinner is deactivated. Elegant integration of indicators is the type of polish that makes an application appealing and easy to do with Mojo’s widgets.

Progress Indicators

The progress pill is the most common progress indicator, and is styled to match the Mojo button and header styles. The other two indicators, progress bars and progress sliders, are more specialized, but are functionally derived from progress pills; you’ll manage them in the same way.

Progress Pill

Use a Progress Pill widget (Figure 5-2) to show download progress when loading from a database, or anytime you initiate a long-running operation and have a sense of the duration.

A Progress Pill widget example

Figure 5-2. A Progress Pill widget example

The indicator is designed to show a pill image that corresponds to the model’s value property, where a value of 0 has no pill exposed and a value of 1 has the pill completely filling the container. Initialize the indicator’s model value to 0, then progressively update the model property until it has a value of 1.

It’s best to use an interval timer. At each interval callback, increase the progress indicator’s value property and call the updateModel function. For example, start with the progress pill’s value property set to 0 and set an interval timer for 600 ms. Assuming the planned operation will take about 3 seconds, you would increase the value property from 0 to 0.2 at the first update and again by 0.2 at each update thereafter:

if (this.progressCounter > 1) {
    // This operation is complete!
    this.completeProgress();
}
else {
    this.pillModel.progress = this.pillModel.progress + 0.2;
    this.controller.modelChanged(this.pillModel);
}

Progress Bar

The Progress Bar widget is exactly the same as the progress pill, except that you use x-mojo-element="ProgressBar" in your scene file. Otherwise, you code it and manage it just as you do the progress pill. Figure 5-3 shows a progress bar.

A Progress Bar widget example

Figure 5-3. A Progress Bar widget example

In the default style, there isn’t room on the bar for a title or image, but the properties are supported nonetheless.

Progress Slider

For media or other applications where you want to show progress as part of a tracking slider, the Progress Slider widget is an ideal choice. Combining the Slider widget with the progress pill, the behavior is fully integrated, but not all of the configuration options are represented. Figure 5-4 shows a progress slider.

A Progress Slider widget example

Figure 5-4. A Progress Slider widget example

All of the slider properties are represented, and you configure the progress slider just as you would a Slider widget. You have a model property of value, which can be renamed through the attributes property. You manage it exactly as you would the progress pill, by progressively increasing the value property from 0 to 1.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.147.137