Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Java Development

Sample Code - Scrollable one-line text input field

by Developer on ‎07-29-2010 12:29 PM - edited on ‎09-20-2010 04:58 PM by Retired (15,678 Views)

One of the standard graphical input gadgets is a one-line text input field, potentially scrollable (because it might contain more characters than can be displayed on the screen yet the GUI designed does not want it to start wrapping around and pushing other fields down).  Anyone who has ever edited PATH environment variable under Windows® has seen this kind of a UI element.

 

On BlackBerry® device, the default implementation of TextField and its descendants wraps around once the text advance inside exceeds the maximum available width.  Besides, pressing ENTER key inserts a new line which requires some new vertical space all by itself.  How do we address these two points?  Can we manage without resorting to some sophisticated paint(), layout() and keyChar() overrides?

 

The answer is a resounding "Yes!"  We can do both using only the BlackBerry device primitives (which are written in native code and are thus much more efficient than custom fields we might create).

 

1.  Making the field never wrap to the next line:

This is achieved by telling the input field that it has enough width for all the characters it can hold.  A HorizontalFieldManager with HORIZONTAL_SCROLL style bit set will let its managed fields have next to unlimited width (for a total of 0x3FFFFFFF, I believe).  And, naturally, it will handle scrolling of the field(s) just fine - that's what HORIZONTAL_SCROLL is for.

2. Disabling ENTER key:

NO_NEWLINE style bit of the TextField disables having new lines in the input completely, including stripping them from initial values and copy/pasted text.  Just what we wanted!

 

Let's create some code:

 

public class OneLineTextField extends HorizontalFieldManager {
    private EditField _editField;

    public OneLineTextField(String label, String initialValue, int maxChars, long style) {
        super(HORIZONTAL_SCROLL);
        editField = new EditField(label, initialValue, maxChars, style | EditField.NO_NEWLINE | EditField.FOCUSABLE | EditField.EDITABLE);
        add(editField);
    }

    public String getText() {
        return editField.getText();
    }
    ...
}

 

Ellipsis denotes other methods potentially delegated to the editField just like getText above.

 

Wow!  Is that it?  Let's test and see:

2940i5107D31F34064BF3

 

Gray field is a label field where I redisplay the content of the OneLineTextField in order to see that it is stored and extracted correctly.

 

Let's type some more:

2941iE01E9F5E1B0667DC

 

Oops! Where is our label?  Hey, it scrolled along with the rest of the field!  Was that what we wanted?

 

Let's specify our goal a little more formally.  Now we want a non-scrolling (docked) label with the scrolling one-line input field.  IHow do we achieve this goal?

 

In order for the label to be non-scrolling, it has to be managed by a non-scrolling manager.  On the other hand, input text needs a scrolling manager around it.  After thinking for a while we decide that our whole field will be a non-scrollable HorizontalFieldManager (for the label to be beside the text and not above/below) with a LabelField (label) and a scrollable HorizontalFieldManager in turn managing an EditField.  Something like this:

 

public class OneLineTextField extends HorizontalFieldManager {
    private EditField _editField;

    public OneLineTextField(String label, String initialValue, int maxChars, long style) {
        super(NO_HORIZONTAL_SCROLL);
        HorizontalFieldManager textMgr = new HorizontalFieldManager(HORIZONTAL_SCROLL);
        add(new LabelField(label));
        _editField = new EditField("", initialValue, maxChars, style | EditField.NO_NEWLINE | EditField.FOCUSABLE | EditField.EDITABLE);
        textMgr.add(_editField);
        add(textMgr);
    }

    public String getText() {
        return _editField.getText();
    }
}

 Let's look at the results:

2942i899327C0B7A2944F

 

Same as before.  How about typing a little more?

 

2943iB677CE9C2DC626F7

 

Hey, that's better!  The label stayed put this time while the input text scrolled as it should.  And everything under 20 (!) lines of code.

Comments
by Developer ‎09-01-2010 04:08 PM - edited ‎09-01-2010 04:17 PM

For those wishing to add a box around the input area:

Do not setBorder around _editField!  setBorder around textMgr.  Otherwise your border will look "open" on the right side - because it will be drawn around the whole _editField, which has some very big width (most probably everything allowed by a scrollable horizontal field manager - Integer.MAX_VALUE >> 1). textMgr, on the other hand, has a very well defined extent.

 

If you are aiming at pre-4.6 devices (with Field.setBorder still unavailable), define textMgr like this:

HorizontalFieldManager textMgr = new HorizontalFieldManager(HORIZONTAL_SCROLL) {
  protected void sublayout(int maxWidth, int maxHeight) {
    super.sublayout(maxWidth - 2, maxHeight - 2);
    setPositionChild(_editField, 1, 1);
    setExtent(getWidth() + 2, getHeight() + 2);
  }

  protected void paintBackground(Graphics g) {
    g.clear();
    g.drawRect(getHorizontalScroll(), getVerticalScroll(), getWidth(), getHeight());
  }
};
by New Developer on ‎10-01-2010 02:33 PM

@arkadyz

Thanks - that solves the exact issue I was having.

Seems obvious in hindsight. Smiley Happy

by Developer on ‎10-06-2010 12:54 PM

After playing with this gadget for a while, I found out that my previous comment about adding a border for pre-4.6 devices is not completely correct.

 

Once the input field starts scrolling, the letters start touching the left border (which becomes obvious once you start thinking about it). Field positioning and painting of the border needs to be done at the class level.  Since I'm overriding sublayout() anyway, I can switch from HorizontalFieldManager to Manager.

 

I also experimented with giving the border varying thickness and 3-D look, plus adding some padding between the border and the input text.

 

Below is the new complete code (much bigger than the one suggested in the article, but you must pay for enhanced functionality).  It is not commented, but is still short enough for more advanced users to grasp the concept and modify as they see fit.

 

Note the setFont, setBorderThickness and setPaddingWidth methods - use them to customize the layout of this gadget:

 

import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.component.EditField;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.Manager;
public class OneLineTextField extends Manager {
private EditField _editField;
private int _border = 2;
private int _padding = 0;
private HorizontalFieldManager _textMgr;
private LabelField _label;
public OneLineTextField(String label, String initialValue, int maxChars, long style) {
super(NO_HORIZONTAL_SCROLL | NO_VERTICAL_SCROLL);
_textMgr = new HorizontalFieldManager(HORIZONTAL_SCROLL);
_label = new LabelField(label);
add(_label);
_editField = new EditField("", initialValue, maxChars, style | EditField.NO_NEWLINE | EditField.FOCUSABLE | EditField.EDITABLE | EditField.NON_SPELLCHECKABLE);
_textMgr.add(_editField);
add(_textMgr);
}
public String getText() {
return _editField.getText();
}

public void setBorderThickness(int border) {
_border = border;
updateLayout();
}
public void setPaddingWidth(int padding) {
_padding = padding;
updateLayout();
}
public void setText(String newText) {
_editField.setText(newText);
}
public void setFont(Font font){
_label.setFont(font);
_editField.setFont(font);
updateLayout();
}
protected void sublayout(int w, int h) {
int height;
int width;
layoutChild(_label, w, h);
height = _label.getHeight();
width = _label.getWidth();
layoutChild(_textMgr, w - width - 2 * (_border + _padding), h - 2 * (_border + _padding));
width += _textMgr.getWidth() + 2 * (_border + _padding);
height = Math.max(height, _textMgr.getHeight() + 2 * (_border + _padding));
setPositionChild(_label, 0, (height - _label.getHeight())/2);
setPositionChild(_textMgr, _label.getWidth() + _border + _padding, (height - _textMgr.getHeight())/2);
setExtent(width, height);
}
protected void paintBackground(Graphics g) {
int width = _textMgr.getWidth() + 2 * (_border + _padding);
int height = _textMgr.getHeight() + 2 * (_border + _padding);
int hPos = _label.getWidth();
g.clear();
int prevColor = g.getColor();
for (int i = 0; i < _border; ++i) {
g.setColor(Color.BLACK);
g.drawLine(hPos + i, i, hPos + width - 1 - i, i); // top
g.drawLine(hPos + i, i, hPos + i, height - 1 - i); // left
g.setColor(0xAAAAAA);
g.drawLine(hPos + width - 1 - i, i, hPos + width - 1 - i, height - 1 - i); // right

g.setColor(0xCCCCCC);
g.drawLine(hPos + i, height - 1 - i, hPos + width - 1 - i, height - 1 - i); // bottom
}
g.setColor(prevColor);
}
}

by Developer on ‎04-23-2012 02:29 PM

What I have found useful when using a field similar to this was an addition for focus movement.

On NavigationMovement add a check for the cursorposition, and if it is equal to the text length try to move the focus to the next field. This avoids scrolling ad infinitum in the manager.

Users Online
Currently online: 28 members 1,886 guests
Recent signins:
Please welcome our newest community members: