This post was prompted by a comment on my Simple Grid Layout post. How do you make an app like Google or Yahoo chat with a fixed editable area at the bottom of the screen and a scrollable main area? Again, the answer is: roll your own layout manager…
Basically we’ll end up with something like this:

It’s not much to look at - we’re using very plain-vanilla BlackBerry controls (instead of custom fields) but it has the elements - fixed title, scrollable main area, fixed text entry field at the bottom. The framework of a chat UI - everything else is just a coat of paint.
First, I should say that I’m actually going to leave out a few details that you’d want in a real application, like good focus handling - so keep in mind, this is an illustrative example only, not a complete component to use in your application (though all code will work, and feel free to use it!)
The Chat Layout Manager
The idea is to make a very special-purpose manager that will manage three fields: The main field (the scrollable chat area), a field at the top (the title) and the bottom field (the typing window for the chat program). The manager will expand the space given to the main field based on screen height minus the height of the top and bottom fields. In our case the main ‘Field’ will actually be a VerticalFieldManager which holds a RichTextField, so we get scrolling.
The manager itself is very simple - the magic is all in the layout method. We set the field’s extent to the max width and height available, and then layout (if they exist) the top and bottom fields, which forces those fields to set their heights, and lets us know how much space is available for the main area. It’s easier to show than explain, so take a look:
ChatLayoutManager.java
package com.thinkingblackberry;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Manager;
public class ChatLayoutManager extends Manager {
private Field bottomField;
private Field mainField;
private Field titleField;
public ChatLayoutManager(long style) {
super(style);
}
protected void sublayout(int width, int height) {
setExtent(width, height);
int y = 0;
if (bottomField != null) {
layoutChild(bottomField, width, height);
// This goes at the bottom of the screen
setPositionChild(bottomField, 0, height-bottomField.getHeight());
height -= bottomField.getHeight();
}
if (titleField != null) {
layoutChild(titleField, width, height);
// This goes at the top of the screen
setPositionChild(titleField, 0, 0);
height -= titleField.getHeight();
y += titleField.getHeight();
}
if (mainField != null) {
layoutChild(mainField, width, height);
// This goes just below the title field (if any)
setPositionChild(mainField, 0, y);
}
}
public void setMainField(Field f) {
mainField = f;
add(f);
}
public void setBottomField(Field f) {
bottomField = f;
add(f);
}
public void setTitleField(Field f) {
titleField = f;
add(f);
}
}
Notice we need methods for setting the top, bottom and main fields explicitly. Don’t call the field’s add method directly, the behavior isn’t really defined here. Ideally you’d just provide appropriate methods on the screen that uses this manager and let the clients do things that way (one of those finishing touches I alluded to above).
The Chat Screen
Having done that, we’ll create our own screen class and use this manager as that screen’s delegate manager. Why do we need to create our own class? It’s because we have to set the delegate manager for a screen in the screen’s constructor - we can’t override it anywhere else, and the only place to call the constructor is from our own constructor. Therefore we need our own class. It’s not actually too bad, Screen does a lot for us - if we pass in Screen.DEFAULT_MENU and Screen.DEFAULT_CLOSE as style flags, we get a basic menu and ESC will close the screen.
Notice that we add our RichTextField (the main ‘chat’ control) to a VerticalFieldManager and add that to the main area of the manager - this is so the main area scrolls.
Another thing we’re doing in the screen is detecting text changes in the edit field and switching to the rich text field when enter is pressed. When we switch to the rich text field we’re setting the cursor position to the bottom of that field - behind the scenes this has the effect of setting the focus rectangle to the bottom of that field, which then causes the VerticalFieldManager to scroll to make it visible - if it’s not already.
package com.thinkingblackberry;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.Screen;
import net.rim.device.api.ui.component.AutoTextEditField;
import net.rim.device.api.ui.component.EditField;
import net.rim.device.api.ui.component.RichTextField;
import net.rim.device.api.ui.container.VerticalFieldManager;
public class ChatScreen extends Screen implements FieldChangeListener {
private ChatLayoutManager chatManager;
private RichTextField richTextField;
private EditField editField;
public ChatScreen() {
super(new ChatLayoutManager(0), Screen.DEFAULT_CLOSE | Screen.DEFAULT_MENU);
chatManager = (ChatLayoutManager)getDelegate();
VerticalFieldManager vfm = new VerticalFieldManager(Manager.VERTICAL_SCROLL | Manager.VERTICAL_SCROLLBAR);
String str = getSampleText();
richTextField = new RichTextField(str);
vfm.add(richTextField);
editField = new AutoTextEditField("Chat:", "");
editField.setChangeListener(this);
chatManager.setBottomField(editField);
chatManager.setMainField(vfm);
}
protected void sublayout(int width, int height) {
setPositionDelegate(0, 0);
layoutDelegate(width, height);
setPosition(0, 0);
setExtent(width, height);
}
public void fieldChanged(Field field, int context) {
if (field == editField) {
String text = editField.getText();
if (text.length() > 0 && text.charAt(text.length()-1) == '\n') {
editField.setText(text.substring(0, text.length() - 1));
richTextField.setCursorPosition(richTextField.getTextLength()-1);
richTextField.setFocus();
}
}
}
private String getSampleText() {
String str = "";
try {
InputStream is = getClass().getResourceAsStream("/res/lipsum.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int read = is.read(buffer);
while(read > 0) {
baos.write(buffer, 0, read);
read = is.read(buffer);
}
is.close();
baos.close();
str = new String(baos.toByteArray());
} catch (IOException ex) {
}
return str;
}
}
So these two, plus a little application to wrap them, will give you the basic UI layout of a chat program. It’s a bit rough, but let me know if you have any questions.






6 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.
Thanks a lot! This posting seems to be the only source of information on how to do user input in Blackberry! Just got your prototype, did the same in my code, and that’s it. Thanks!
U r doing a gr8 job!!!!! Carry On!!!!
Thanks for all the help
Hi,
I have tested your code and I keep having the message: “Field id not a child of this manager” when the code reaches the layoutChild line.
Do you know what can it be?
Hi, Anthony!
I think the same could be done in a bit more simple way. I mean no need to create a custom manager. You may just use MainScreen. It has setTitle(String string) to set the title, add(Field field) to add the main part (richTextField) and setStatus(Field field) to add editField.
One more point - in your ChatScreen code I don’t see how you actually set the title.
Thanks!
I just get exceptions when I try to created it (also already took out the dependency on a file); what am I missing
Just a quick comment to say that I really, REALLY appreciate your posts…as I’m someone who started writing Blackberry apps out of frustration over the lack of available Blackberry apps, and the existing RIM API documentation is spotty at best.
Just a quick question on the above article…when you say “Don’t call the field’s “add” method directly, it’s not really defined here.” are you talking about the Main, Bottom and Title fields?
Again, thanks, and keep up the great work!