Chapter 2: Key Concepts
This chapter discusses some of the important concepts underlying use of the Bananas UI Toolkit, including the following:
BApplication and the Screen Stack
The core of your application is the BApplication class, which is subclassed from the HME Application class. BApplication contains a stack of screens (of class BScreen). You construct the user interface for your application by creating screens and pushing them onto the stack. Each BScreen contains one screenful of information. A BScreen has the same dimensions as the application.
The top screen on the stack is visible. Whenever a new screen is pushed onto the stack or a screen is popped off the stack, the old screen is hidden and the new screen is shown. The screen being hidden is said to be “exited,” and the new screen that is visible is said to be “entered.”
User Interface Layers
BApplication has three layered views that are used to organize your interface:
It is important to place screens in the normal layer of BApplication because BApplication performs sliding transitions (see “Transitions,” later in this section) by positioning the old and new screens inside normal and then using an animation call to normal.setTranslation() to slide the screens around. If you want your application to have a background image that’s shared across all screens, put it into the below layer of BApplication.
Views and Widgets
As shown in Figure 2-1, BView, derived from the HME View class, is the base class for widgets and screens. A screen contains one or more views.
BView is a bounding box associated with a resource. A BView object is capable of receiving focus, and it can be highlighted.
Figure 2-1. Class tree for BView
Screen Layers
A screen has three layers:
- above: contains views that are “above” the widgets. This layer contains whispering arrows and other decorations. (Whispering arrows are the arrows that provide a hint to the user about which arrow buttons are active at a particular spot in the UI.)
- normal: contains widgets such as buttons, text, lists, and keyboards
- below: contains backgrounds as well as highlights such as the background bar used in lists to show focus
You can supply a different background for every screen, or you can specify one global background for BApplication. In cases where both an individual screen and the application have backgrounds, the screen background obscures the application’s background.
Transitions
Each screen has an associated transition that describes its behavior when it is pushed and popped. Possible transitions are
See the Bananas sample application for a demonstration of screen transitions.
Methods for Entering and Exiting Screens
The BScreen class provides handleEnter() and handleExit() methods that are called when a screen is entered and exited. You can override these methods to implement custom behavior for your screens, such as starting and stopping any helper threads associated with the screen. If the user calls push or pop with an arg, the arg is passed in to handleEnter().
The isReturn parameter indicates whether your screen was just popped (=TRUE) or pushed (=FALSE). This mechanism can be used to create screens that accept arguments and return results. For example, in the Bananas sample (KeyboardScreen.java), when the user types an e-mail address using the e-mail keyboard screen and then returns to the keyboard menu screen, the text the user typed is passed back to the keyboard menu screen and shown in parentheses on the Email keyboard row (Figure 2-2).
Figure 2-2. Example of passing user data from one screen to the next
Use the handleExit() method to stop any threads your screen has created and to perform general cleanup.
Focus
Within each screen, one view contains the current focus. When a screen is pushed onto the stack, key events are first sent to the view that contains the default focus. For example, if your screen contains only a list widget, you probably want to call
to ensure that the list will have focus when your screen is pushed.
When a new screen is pushed onto the stack, the old screen’s focus is not modified. When the covering screen is popped, the old screen will have the same focus as before. This behavior provides continuity for the user.
If the currently focused view does not handle the event, it is sent to the view’s parent.
Changing Focus
Use screen.setFocus() to change the screen’s focus at any time. When the focus changes, events are sent to the views that are losing and gaining focus. These events propagate up the parent hierarchy, as with all HME events, until handleFocus() returns TRUE.
Focus Movement
The user presses arrow keys on the TiVo remote control to navigate within and between screens. Bananas facilitates this behavior. BScreen contains default behavior for handling left, right, up, and down actions. When the screen handles these actions, it attempts to automatically move the focus in the desired direction. Bananas offers two mechanisms for changing focus:
See “Actions for Whispering Arrows” and “Actions” to learn about actions and how they are generated.
Focus Manager
The Focus Manager (BFocusManager) handles focus movement in a fashion similar to that of the TiVo UI. The Focus Manager will try to move the focus in response to the user pressing an arrow key. It finds the view that is closest to the ray pointing from the center of the source view in the direction of the arrow key pressed.
To be a candidate for receiving focus, a view must
See the Bananas sample application for a demonstration of focus management (Figure 2-3).
Be sure to call view.setFocusable() if you want a view to be eligible for auto-focus management. For example, BButton calls setFocusable(true) in its constructor so that buttons are always candidates for focus. In contrast, BText is not focusable by default. BList itself is not focusable but contains rows that are focusable.
Figure 2-3. Example of focus management built into the toolkit
Highlights
A highlight is a view displayed at a position relative to the currently focused widget. There are three types of Bananas highlights, described in Table 2-1.
Table 2-1 Highlight Objects in Bananas Toolkit
Highlight Function Drawn in This Layer whispering arrows Indicate the direction in which the user can change focus. Whispering arrows are associated with buttons and are visible only when that view has focus. In Figure 2-3, Button 4 has three whispering arrows. above bars Indicate that a view has focus. Bars are always the same height but can be stretched to any width. below page hints Indicate whether there is more information on the previous or next screen. They imply that the user can hit channel up/down to scroll the page. above
Highlights are drawn either above or below the view being highlighted. They are associated with the underlying view but are not direct children of it. This makes it possible to place highlights outside the highlighted view. Because highlights are defined independently of the objects they’re highlighting, they can be shared by multiple views. For example, all the rows in a list can share the same set of highlights.
Highlights are laid out by assigning them a position. This position is always relative to the view being highlighted, even though the highlight is not a child of the view. For example, a highlight at 0,0 will always draw in the upper left corner of the highlighted view. A highlight at -100,0 will draw to the left of the highlighted view.
Some highlights are associated with the focus. For example, in a list widget, the arrows and bar indicate that a specific row has focus, and when the focus moves, the highlights move too.
The BHighlight class is used to create a specific highlight on a widget (for example, one whispering arrow). One widget can have multiple BHighlight objects associated with it.
The Bananas Toolkit also contains a low-level framework for managing highlights (the BHighlights class). Individual widgets sometimes contain helper methods that facilitate highlight creation.
There are three different ways to add highlights to a view. In order of complexity, they are as follows:
//add left arrow to a view; place it in left/top corner BHighlights h = view.getHighlights(); h.setWhisperingArrow("left", A_LEFT, A_TOP, "left");Actions for Whispering Arrows
Whispering arrows usually contain an action. When the user presses an arrow key on a view that contains a whispering arrow highlight, that action is fired automatically. For example:
BHighlights h = view.getHighlights(); // left arrow should fire "pop" action; right arrow should // move focus to the right h.setWhisperingArrow("left", A_LEFT -10, A_CENTER, "pop"); h.setWhisperingArrow("right", A_RIGHT +10, A_CENTER, "right");
Sounds
In addition to handling the screen stack, BApplication handles the playing of default sounds. It attempts to handle generic sounds for events such as pushing and popping screens and changing focus. Most of the time, the default sound is the sound you’ll want to use. If you do not want your application to play a certain sound, however, you can specify that it play a NULL sound for a given action. Make the following call to suppress the sound:
Actions
The Bananas sample program (SoundsScreen.java) shows how to play custom sounds by overriding the handleAction() method. An action is a type of event and is generated by Bananas or by your application, usually in response to a keypress. As do other events, actions propagate up through the parent hierarchy until they are handled (the handleAction() method then returns TRUE).
Custom Sounds
The following sample code shows overriding the sounds played when the user hits one of the four arrow buttons on the remote control.
// Play a custom sound when an action is selcted. public boolean handleAction(BView view, Object action) { // Override the default sound for a given action. if (action.equals(H_RIGHT)) { getBApp().play("speedup3.snd"); } else if (action.equals(H_LEFT)) { getBApp().play("slowdown1.snd"); } else if (action.equals(H_UP)) { getBApp().play("thumbsup.snd"); } else if (action.equals(H_DOWN)) { getBApp().play("thumbsdown.snd"); } return super.handleAction(view, action); }Default Application Behavior
When Bananas dispatches a key event, the following set of rules is followed to determine what sound is played:
- In response to a key event, was BApplication.play() called? (This function can be called either with a custom sound or with NULL.) If yes, be silent.
Note: In order for BApplication to detect that a sound was played, your application must call BApplication.play() directly.- Did the focus change or did the screen change? If so, play a sound according to Table 2-2.
- If none of the above occurred, play bonk.