labelField.getWidth() returns 0? Why does this happen?

643 views Asked by At

I'm working on my own custom manager, and I've gotten it complete so far, but it setsMargins using a percentage of the screen resolution.

Here's how I call the following class:

LabelIconCommandManager licm3 = new LabelIconCommandManager("Address blah bklahblah ", 0);
licm3.add(new ImageButtonField(b1, b2, b3, Field.FIELD_LEFT | ImageButtonField.CONSUME_CLICK));

Here's the class [I've marked in a comment where it returns 0 and where it returns 219. please tell me why this happens:

public class LabelIconCommandManager extends HorizontalFieldManager implements  BCMSField
{
    LabelIconCommandManager me = this;
    EvenlySpacedHorizontalFieldManager buttonManager = new EvenlySpacedHorizontalFieldManager(0);
    LabelField labelField;
    int side = 0;
    int HPADDING = 3;
    int VPADDING = 4;
    int screenWidth = Display.getWidth();
    int labelField_width = 40;
    public LabelIconCommandManager()
    {
        this("", 0);
    }
    public LabelIconCommandManager(String label, long style)
    {
        super(USE_ALL_WIDTH| FOCUSABLE);
        this.setBorder(BorderFactory.createBitmapBorder(new XYEdges(15, 20, 15, 20),Bitmap.getBitmapResource( "border_edit.png" )));
        this.setMargin(1,10,1,10);
        labelField = new LabelField(label,LabelField.ELLIPSIS)
        {
            public void layout(int width, int height)
            {
                // Done because otherwise ellipses dont work with labelfields
                super.layout((int)(screenWidth * 0.61), getHeight());
                setExtent((int)(screenWidth * 0.61), getHeight());
                labelField_width = labelField.getWidth();
                DisplayDialog.alert("labelField_width = " + labelField_width); // returns 219
            }
        };
        // Top Right Bottom Left
        labelField.setMargin(VPADDING, HPADDING, VPADDING, 0);
        // super because we want this horizontalfieldManager to add it
        super.add(labelField);
        super.add(buttonManager);
    }
    public void alternateConstructor(Attributes atts)
    {
        labelField = new LabelField(atts.getValue("label"), 0);
    }
    public void onFocus(int direction)
    {
        this.setBorder(BorderFactory.createBitmapBorder(new XYEdges(15, 20, 15, 20),Bitmap.getBitmapResource( "border_edit_select.png" )));
        // uses the same color as listStyleButtonField selections
        this.setBackground(BackgroundFactory.createSolidBackground(0x186DEF));
        super.onFocus(direction);
    }
    //Invoked when a field loses the focus.
    public void onUnfocus()
    {
        //top, right,bottom,left
        this.setBorder(BorderFactory.createBitmapBorder(new XYEdges(15, 20, 15, 20),Bitmap.getBitmapResource( "border_edit.png" )));
        this.setBackground(BackgroundFactory.createSolidTransparentBackground(Color.GRAY, 0));
        super.onUnfocus();
        invalidate();
    }
    // Overrride this managers add function
    public void add(Field imageButton)
    {
        // Add a button to the evenly spaced manager
        buttonManager.add(imageButton);
        // Based on how many buttons there are, set the margin of where the manager holding the buttons start [offset from labelField]
        if(buttonManager.getFieldCount() == 1)
        {
            //side = (int)(screenWidth * 0.1388);
            side = screenWidth - labelField_width - 32 - 10 - 15;
            DisplayDialog.alert("Screen Width = " + screenWidth);
            DisplayDialog.alert("labelField_width2 = " + labelField_width); // returns 0
            DisplayDialog.alert("Side = " + side);
        }
        else side = (int)(screenWidth * 0.05);
        buttonManager.setMargin(0,0,0,side);
    }
    public int getLabelWidth()
    {
        return labelField_width;
    }
}  

Here's a picture just to be more clear:

Snapshot

3

There are 3 answers

2
Nate On BEST ANSWER

Note: when I ran your code, I didn't actually see labelField_width set to 0. You initialize the value to 40 in the code you posted above. So, I do sometimes see it set to 40, or 219 (on a 360 px wide screen).

But, the problem is that I think you're trying to access the value of labelField_width too soon. The only place it's properly assigned is in the layout() method of your anonymous LabelField. Just because you declare and implement the layout() method in line with the instantiation, doesn't mean that it's called when the LabelField is created. This is actually one of the reasons I don't like anonymous classes.

Anyway, this code:

LabelIconCommandManager licm3 = new LabelIconCommandManager("Address blah bklahblah ", 0); 
licm3.add(new ImageButtonField(b1, b2, b3, Field.FIELD_LEFT | ImageButtonField.CONSUME_CLICK));

Will first instantiate the LabelField (inside the LabelIconCommandManager constructor). As I said, that does not trigger the layout() method. The second line above (add()) will trigger your overridden method:

// Overrride this managers add function 
public void add(Field imageButton) 
{ 

which is where you see the bad value for labelField_width. That method gets called before layout(). That's the problem.

Since it looks like you only use that width to set the buttonManager margin, you could just wait a little longer to do that. If you wait until the LabelIconCommandManager sublayout() method is called, your LabelField will have had its layout() method called, and labelField_width assigned correctly:

protected void sublayout(int maxWidth, int maxHeight) {
   // make sure to call superclass method first!
   super.sublayout(maxWidth, maxHeight);

   // now, we can reliably use the label width:
   side = screenWidth - labelField_width - 32 - 10 - 15; 
   buttonManager.setMargin(0,0,0,side); 
}

That method goes in the LabelIconCommandManager class. And then, you can remove the other place you call buttonManager.setMargin().

0
Eugen Martynov On

Some brief summary from Nate post.

When you construct manager and add fields don't expect that it will be layouted correctly. Manager doesn't know the context - where it will be placed. So layout method for field will be called only when you add his manager to the screen (when layout for manager will be also called). And this is correct.

Move the calculation of your side variable to layout method.

If you really need side value before you put manager to screen. You could precalculate it by using Field.getPrefferedWidth() which returns meaningful values for standard fields (getFont().getAdvance(text) for LabelField, probably also with borders please check yourself). But be careful with this values.

0
Eugen Martynov On

Please review code below. It's manager which has label and buttons. And it puts label at the left side and buttons at the right.

import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.decor.Border;

import java.util.Vector;

public class TabFieldManager extends Manager {

public TabFieldManager(long style) {
    super(style);
}

protected void sublayout(int width, int height) {
    LabelField label = null;

    Vector tabs = new Vector();
    int tabsWidth = 0;
    int tabHeight = 0;
    int tabPaddingTop = 0;
    int tabPaddingLeft = 0;

    for (int i=0; i < getFieldCount(); i++) {
        Field field = getField(i);
        if (field instanceof LabelField) {
            label = (LabelField) field;
        } else if (field instanceof ButtonField){
            tabs.addElement(field);

            layoutChild(field, width, height);

            int fieldwidth = field.getWidth() > 0 ? field.getWidth() : field.getPreferredWidth() ;
            tabsWidth += fieldwidth + getBorderAndPaddingWidth(field);

            int fieldHeight = field.getHeight() > 0 ? field.getHeight() : field.getPreferredHeight();
            if (fieldHeight > tabHeight) {
                tabHeight = getBorderAndPaddingHeight(field) + fieldHeight;
            }

            int fieldPaddingTop = field.getPaddingTop();
            if (fieldPaddingTop > tabPaddingTop) {
                tabPaddingTop = fieldPaddingTop;
            }

            int fieldPaddingLeft = field.getPaddingLeft();
            if (fieldPaddingLeft > tabPaddingLeft) {
                tabPaddingLeft = fieldPaddingLeft;
            }
        }
    }

    if (label != null) {
        layoutChild(label, width - tabsWidth, height);
        int y = tabHeight - label.getHeight() >> 1;
        setPositionChild(label, tabPaddingLeft , y);
    }
    for (int i = 0; i < tabs.size(); i++) {
        Field tabField = (Field) tabs.elementAt(i);
        setPositionChild(tabField, width - tabsWidth, getBorderAndPaddingHeight(tabField));
        tabsWidth -= tabField.getWidth() + getBorderAndPaddingWidth(tabField);
    }

    setExtent(width, tabHeight);
}

private int getBorderAndPaddingHeight( Field field ) {
    int height = field.getPaddingTop() + field.getPaddingBottom();
    Border border = field.getBorder();
    if( border != null ) {
        height += border.getTop() + border.getBottom();
    }

    return height;
}

private int getBorderAndPaddingWidth( Field field ){
    int width = field.getPaddingLeft() + field.getPaddingRight();
    Border border = field.getBorder();
    if( border != null ) {
        width += border.getLeft() + border.getRight();
    }
    return width;
}

protected int moveFocus(int amount, int status, int time) {
    if ((status & Field.STATUS_MOVE_FOCUS_VERTICALLY) == Field.STATUS_MOVE_FOCUS_VERTICALLY && amount > 0) {
        return amount;
    } else
        return super.moveFocus(amount, status, time);
}

protected int nextFocus(int amount, int axis) {
    if (amount > 0 && axis == Field.AXIS_VERTICAL)
        return -1;
    else
        return super.nextFocus(amount, axis);
}

}