Build Better Mobile UIs with a New Brew Framework : Page 3
Qualcomm's new Brew UI Framework brings user themes, form management, a rich new pool of UI controls, and a model-view-controller architecture to the Brew platform. This article demonstrates how to make use of these new features with a sample app designed to track the money that changes hands at the local Starbucks.
by Ray Rischpater
Sep 29, 2004
Page 3 of 3
Getting Started with An Application
It's a little daunting to begin with the Brew UI Toolkit, simply because there are so many new classes to consider. It's best to begin small, exploring directly the relationship between an application's form, some widgets, and their model, and then build out, gradually exploring new widgets and multiple forms as you build your application user interface. To help you get started, I've written IOU, an application which uses the Brew UI Toolkit to track the money that changes hands at the local Starbucks. You can see the application in Figure 1.
Figure 1. The IOU Entry Form: This form demonstrates an IForm instance containing static and dynamic Brew widgets.
Building an application with the Brew UI Toolkit is a little different than starting a traditional application in Brew, because the toolkit components aren't bundled with the Qualcomm Brew Simulator. Because these components are packaged as separate Brew extensions, make sure that the path to your application in the simulator also includes the MIF and DLL files for the forms and widgets releases included with the Brew UI Toolkit. (This is the same process you'd use to include any other extensions in your application on the simulator.) While Qualcomm has targeted Brew UI Widgets for inclusion in Brew 3.1 and beyond, it's possible to develop your application and test on handsets running Brew 2.0 now, by installing the forms.mod and widgets.mod files on your target handset as well.
While applications can use widgets without forms, it makes little sense to do so: forms give you a powerful way to conceptualize your application flow and group widgets. Any application that uses both widgets and forms must create a root form, a form for each screen, and a top-level container for each form.
This top-level form has two key responsibilities. First, it maintains the stack of forms and tracks the notion of which form has focus. Second, it presents a container user interface for your form, consisting of a title and soft key labels. By permitting the root form to own the responsibility of creating and configuring these widgets, device manufacturers can ensure a similar look and feel between all widget-based applications. (Of course, if your application has a drastically different user interface, you're free to create your own root form class, or simply use the IRootForm interface provided to access and mutate the title bar and soft key widgets as required.)
The input formthe most complex form in IOUis created by the InputForm_Create method, which uses lazy instantiation to create the form and its widgets.
Listing 1 seems long, but it's actually simple, consisting of four key parts:
Part 1: Consists of the CreateInstance invocations followed by the QueryInterface invocation, which creates a new form, its constraint container, and obtains the widget associated with the constraint container. The constraint container's bounds are set to be the bounds of the root form, obtained by getting the root forms' client bounds, and then the container widget is linked to the new form. Once the new form is initialized, the code sets the title and soft key widget labels using IFORM_SetText.
Part 2: Creates two widgets: the input field for the payer/payee information, as well as a label indicating the direction of cash flow. Because the code uses an IConstraintContainer as the base container for the form, it can rely on the container to perform all of the necessary layout mathematics, aligning each component on your behalf. (The Brew UI Toolkit also provides the IXYContainer interface, which, like its name suggests, permits absolute placement of its child widgets). The IConstraintContainer is especially helpful in applications like this that mimic paper forms, because you can use it to do all of your widget layout, and your application will dynamically resize to support different screen sizes on different handsets. (I could have used arithmetic signs to indicate this, but it seemed cleaner to have the UI change appearance to reflect the flow of money.) The IConstraintContainer constraint manager requires a WidgetConstraint structure with each widget, which you use to specify the type of constraint when placing a widget's top, left, right, and bottom points in the ConstraintContainer. For each of these points, you can specify whether the child widgets' coordinates should be relative to its sibling or the parent, or sized based on its contents, as well as an offset to apply to the calculated value. In turn, the constraint container dynamically lays out its children widgets, and reflows the widgets as required when new items are added or removed from its widget hierarchy.
Part 3: Involves more of the samewidget creation and placement, although instead of constraining the last two widgets relative to the bottom of the previous widget, the first (the currency symbol) is relative to the bottom, and the second (the currency input) is relative to the right of the currency symbol, so that both the symbol and input line are on the same line of the screen.
Part 4: Pushes the form on the root form's stack, making it the foremost form in the application. Just before returning SUCCESS, the code invokes IROOTFORM_PushForm to push the form on the top of the form stack. Of course, I do this regardless of whether the form was freshly created or not, because if the form already exists, it needs only be brought to the top of the stack.
All of the code in Listing 1 points to one other important thing to remember: when you assign an object to something in the widget library (a widget to a container, a model to a widget, and so forth), the assignee owns the object. You need to be sure to release your instance, otherwise memory will leak when your application exits.
In the next article, I show you how to use the root form to manage multiple forms as well as the role of the IValueModel and ITextModel models in managing user input from selection lists and text entry widgets.
Ray Rischpater is the chief architect at Rocket Mobile, Inc., specializing in the design and development of messaging and information access applications for today's wireless devices. Ray is the author of several books on software development including "eBay Application Development" and "Software Development for the QUALCOMM BREW Platform," both available from Apress, and is an active amateur radio operator.