Creating Custom Managers BLACKBERRY

Now let’s turn our attention to the username, password, and domain fields. We want the labels to line up to the right. To do that, we’ll have to make two changes. The first is easy—we’ll stop using the built-in labels of the EditFields and ObjectChoiceField and replace them with LabelFields. We’ll use HorizontalFieldManagers as we do for the buttons to keep the labels and edit fields on the same line. The new code for the UiFunMainScreen constructor follows:

We’ve given the labels the Field.FIELD_RIGHT style, which will be important later butdoesn’t affect the appearance because of the way the horizontal field managers function in this configuration. The appearance of the application is the same as before, but the horizontal field managers are outlined to clarify the discussion that will follow .

Separate labels and fields in horizontal field managers.

Separate labels and fields in horizontal field managers.

One quick way to get the labels and fields to line up the way we want them is to create two vertical field managers, one for the labels and one for the fields. We can rely on the fact that the fields and labels are the same height to make them line up vertically. The two vertical field managers go inside one horizontal field manager, which is added to the screen. We’re not going to pursue this, but for illustrative purposes Figure shows how the screen looks, again with the managers outlined.

Using VerticalFieldManagers to line up the components

Using Vertical Field Managers to line up the components

That looks pretty good. So what’s the problem? We’re relying on the fact that the label fields are the same height as the other fields. This is not guaranteed—a fact we can illustrate by typing in a long username.

Knocking the fields out of alignment

Knocking the fields out of alignment

What we really want is a grid of labels and fields. We’ll do this by creating a grid field manager.

Creating a Manager

Managers are, in many ways, simpler to implement than fields. You’re only required to implement one method, sublayout, and unless you’re doing something really complex, the basic net.rim.device.api.ui.Manager paint method will still work and draw your fields wherever you positioned them. Managers do have to be concerned with things like moving the focus from field to field, but if we’re clever enough about things, we won’t have to worry about handling that ourselves either. The functionality that net.rim.device.api.ui.Manager gives us will be enough.

Understanding GridFieldManager

GridFieldManager will let the user specify a number of grid columns when it’s instantiated. The number of rows will vary depending on the number of fields added.For a horizontal or vertical manager, it’s clear where fields are positioned, either left to right or top to bottom in the order they’re added (we’ll ignore insert for now). For a grid manager, it’s not as clear which way we should add them.Should they be added left to right, then top to bottom, or the other way around? So we’ll just choose to go left to right and then top to bottom.

This is our field layout for the grid manager; the numbers represent the order the fields were added to the manager.

This is our field layout for the grid manager; the numbers represent the order the fields were added to the manager.

Implementing the Basic Framework

We’ll subclass directly from net.rim.device.api.ui.Manager and build our Manager from scratch. The only thing we’ll need to keep track of is the number of columns. The Manager class already maintains the list of fields for us:

Implementing the sublayout Method

All the magic in a manager happens in the sublayout method.It’s similar to a field’s layout method, down to the requirement that you call setExtent within the method to set the manager’s size (and in fact, behind the scenes, the manager’s layout method calls sublayout). Remember, a manager is ultimately a type of field. The other requirement in a manager’s sublayout method is that you position and size all the fields contained by the manager. This is done through the layoutChild and setPositionChild methods. The layoutChild method will result more or less directly in a call to the child field’s layout method, meaning that before you call layoutChild the child field will return 0 from getWidth and getHeight. Here’s where we have to think about how exactly we’ll determine how wide each column should be.

In our case, we’re using the manager in a limited fashion, and we control how it’s used. Therefore, we can make some assumptions about the types of fields that will be contained; specifically, we can ensure that fields will in general all be able to fit in a row across the screen. When designing general-purpose managers, many of these design decisions can quickly become very complex; it’s usually good practice to keep in mind where you’re going to be using your manager and design to that, rather than attempting to design for the general case right away. We’ll also assume that we don’t have to worry about having enough vertical space for the manager. In fact, if we end up in a constrained vertical space, the manager will just be cut off at the bottom, and it’s not certain that we could really do much better than that. So our manager’s sublayout method will do the following:

  1. Go through the fields in the first (leftmost) column and call layoutChild on each one, so get an accurate width and height.
  2. Determine the width of the first column from the maximum width of all the fields.
  3. Subtract that width from the total available, and continue to layout the fields in each column in turn, left to right.
  4. Set the position for all fields in the first row based on the column widths.
  5. Set the position for all fields in the second row based on the column widths and the maximum height of all the fields in the first row, and continue top to bottom for all the rows.
  6. Set the extent of the field based on the total width of all columns and the total height of all rows.

Clear? Here’s how all the code looks:

There’s a lot to this method, so let’s walk through it.The first for loop iterates through all the fields in the manager, one column at a time and calls layoutChild on each one to set its size:

For each column, we lay out all the fields in whatever width we have available,and then take the maximum width as the column width. We subtract that width from the total remaining width to get the available width for the next columns. Once we’ve determined the column widths, the next for loop again goes through all the fields in the manager, to position them on screen using setPositionChild:

We have the column widths already so we know where they should be positioned horizontally.As we lay out each row, we keep track of the field with the largest height in that row, and use that as the row height:

When we’re at the last field of a row (where the index is one less than a multiple of the number of columns), we shift the vertical position for the next row downward, and start over again:

Finally, we set the size for the manager by adding up all the column widths, and taking the total of all the row heights:

Seeing the Grid Field Manager in Action

OK, now let’s see how the grid looks. We’ll modify UiFunMainScreen’s constructor again to use the grid field manager to hold our separate labels and edit fields:

Remember, because we’ve instantiated a two-column grid, the labels will all appear in the left column and the edit fields on the right. Now, start the simulator, and you should see something like Figure.

Using the first version of the grid field manager

Using the first version of the grid field manager

We’re doing better already. The username can wrap, and all the labels stay aligned with the field. Incidentally if you’re paying attention, you might realize that when the text in the username field wraps, it causes the screen’s layout method to be run again. This behavior is fine and expected: layout happens infrequently,but it does happen.

Aligning the Labels

There’s one problem though. While the edit fields are aligned properly, the labels are still aligned to the left, rather than the right, despite the fact that we gave them the Field.FIELD_RIGHT styles. Remember earlier that I mentioned that styles were dependent on the field and the manager? Our GridFieldManager has to explicitly support the FIELD_RIGHT style to get the alignment to work the way we expect it to.The change is simple. If the field has the FIELD_RIGHT style set, we just need to shift it to the right by the width of the column minus the width of the field. In sublayout replace the following lines:

Now, our manager positions all of the label fields correctly.

The labels are aligned on the right. Notice that wrapping is supported for the password field too.

The labels are aligned on the right. Notice that wrapping is supported for the password field too.

If you wanted to support Field.FIELD HCENTER, the change would be similar. I’ll leave that as an exercise for you.

Focus Movement

If you play with this application, you’ll notice that the focus moves as you’d expect it to: when the cursor is in the username field, scrolling down or right will move the cursor to the password field and then to the domain drop-down.This is because of the default focus movement behavior, which is to use the order the fields are added to the manager as the focus order and to move to later fields in the focus order when the trackball is moved right or down and to earlier fields when the trackball is moved left or up. This behavior wouldn’t work if we had more than one column of focusable fields in our manager. Imagine we have two columns of focusable fields. Figures through illustrate the focus movement problems.The numbers represent the order fields are added to the grid field manager.

Grid manager with six fields. Fields number 2, 3, 5 and 6 are focusable.

Grid manager with six fields. Fields number 2, 3, 5 and 6 are focusable.

Field 2 has focus initially.

Field 2 has focus initially.

A rightward trackball movement correctly moves the focus from field 2 to field 3.

A rightward trackball movement correctly moves the focus from field 2 to field 3.

A downward trackball movement moves the focus from field 3 to field 5, which is not the behavior we expect.

A downward trackball movement moves the focus from field 3 to field 5, which is not the behavior we expect.

This problem exists because the default focus-moving algorithm doesn’t take into account field position on the screen, only field order within the manager. So moving right and down are considered to be the same action. You can fix this by overriding navigationMovement in your manager, but the discussion about how to do so is beyond the scope of this book. The source code available on the book’s web site includes a sample implementation for moving focus in a grid field.

Tidying Up the Login Success Screen

Now that we’ve got a few components, let’s revisit LoginSuccessScreen. With some simple modifications to the constructor to use our CustomLabelField and GridFieldManager, we can make the screen look a little better.

Here, we’ve moved the username and domain labels and values into a grid, similar to our main screen. Notice that we’ve given the right column labels the Field. USE_ALL_WIDTH value, which, in combination with the sublayout method of the grid field manager, will make them use the entire screen width except for the space taken by the first column. Go back and see if you can figure out why, and try to figure out what would happen if we applied Field.USE_ALL_WIDTH to the labels for the first column. The screen with the constructor provided in this section will look something.

The redone login success screen

The redone login success screen

Now, you should be familiar with how to make fields and managers.


All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

BLACKBERRY Topics