Blackberry: why is drawListRow() called with different y for ListField and KeywordFilterField

1.4k views Asked by At

I'm trying to move an app from using a KeywordFilterField to ListField and I'm struggling since several hours to find out, why is drawListRow() called with different y values - depending on which of these two ListField's I use:

If getRowHeight() returns 40, then the y values will be -

For KeywordFilterField are: 0; 40; 80; 120; ... (i.e. as expected)

But for Listfield I see: 9; 49; 89; 129; ... (i.e. offset by 9 for some reason)

Where is the 9 coming from? Is there a method in ListField or ListFieldCallback which I could call to get this value? I'm just trying to draw a light gray line between items of the list.

listfield

Below is my test code and the border.png (used as BasicEditField border) is attached:

border.png

package mypackage;

import java.util.*;
import net.rim.device.api.collection.*;
import net.rim.device.api.collection.util.*; 
import net.rim.device.api.system.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.ui.decor.*;
import net.rim.device.api.util.*;


public class MyList extends UiApplication {
    public static void main(String args[]) {
        MyList app = new MyList();
        app.enterEventDispatcher();
    }

    public MyList() {
        pushScreen(new MyScreen());
    }
} 

class MyScreen extends MainScreen {
    static final int EXTRA_ROWS = 2;

    MyItemList myItems = new MyItemList();
    ListField myList = new ListField(EXTRA_ROWS);

    Border myBorder = BorderFactory.createBitmapBorder(
        new XYEdges(12, 12, 12, 12),
        Bitmap.getBitmapResource("border.png"));

    Background myBg = BackgroundFactory.createSolidBackground(0x111111);
    StringProvider myProvider = new StringProvider("Search");

    BasicEditField myFind = new BasicEditField(USE_ALL_WIDTH) {
        protected void paint(Graphics g) {
            if (getTextLength() == 0) {
                g.setColor(Color.LIGHTGRAY);
                g.drawText(myProvider.toString(), 0, 0);
            }

            g.setColor(Color.BLACK);
            super.paint(g);
        }
    };

    public MyScreen() {
        getMainManager().setBackground(myBg);

        myFind.setBorder(myBorder);
        setTitle(myFind);

        myItems.doAdd(new MyItem(1, "Eins"));
        myItems.doAdd(new MyItem(2, "Zwei"));
        myItems.doAdd(new MyItem(3, "Drei"));
        myItems.doAdd(new MyItem(4, "Vier"));

        myList.setCallback(new MyListFieldCallback());
        add(myList);
    }

    private class MyListFieldCallback implements ListFieldCallback {

        public void drawListRow(ListField list, Graphics g, int index, int y, int width) {
            System.err.println("XXX index=" + index+ ", y=" + y + ", width=" + width);

            g.setColor(Color.WHITE);

            if (index < EXTRA_ROWS) {
                Font i = getFont().derive(Font.ITALIC);
                g.setFont(i);
                g.drawText("Add Item", 0, y);
                return;
            } 

            if (index >= EXTRA_ROWS) {
                MyItem item = (MyItem) myItems.getAt(index - EXTRA_ROWS);
                g.drawText(item.toString(), 0, y);

                g.setColor(0x333333);
                // XXX why do I need to subtract 9 here?
                g.drawLine(0, y-9, width, y-9);

                return;
            }

            g.drawText(list.getEmptyString(), 0, y);
        }

        public Object get(ListField list, int index) { 
            return myItems.getAt(index); 
        }

        public int getPreferredWidth(ListField list) { 
            return Display.getWidth(); 
        }

        public int indexOfList(ListField list, String prefix, int start) { 
            return 0; 
        }
    }

    class MyItemList extends SortedReadableList {
        public MyItemList() {
            super(new MyItem.MyComparator());        
        } 

        protected void doAdd(Object obj) {
            super.doAdd(obj);
            myList.setSize(size() + EXTRA_ROWS);  
        }

        protected boolean doRemove(Object obj) {
            myList.setSize(size() - 1 + EXTRA_ROWS);
            return super.doRemove(obj);        
        }
    }
}

class MyItem {
    int _num;
    String _name;

    public MyItem(int num, String name) {
        _num = num;
        _name = name;
    }

    public String toString() {
        return _num + ": " + _name;
    }

    static class MyComparator implements Comparator {
        public int compare(Object obj1, Object obj2) {
            MyItem item1 = (MyItem) obj1;
            MyItem item2 = (MyItem) obj2;

            return item1.toString().compareTo(item2.toString());
        }
    }

    static class MyProvider implements KeywordProvider {
        public String[] getKeywords(Object obj) {
            MyItem item = (MyItem) obj;
            return new String[]{ Integer.toString(item._num), item._name };
        }
    }
}

The produced output is:

[    64,890] XXX index=0, y=9, width=360
[    64,890] XXX index=1, y=49, width=360
[    64,898] XXX index=2, y=89, width=360
[    64,898] XXX index=3, y=129, width=360
[    64,906] XXX index=4, y=169, width=360
[    64,906] XXX index=5, y=209, width=360

UPDATE in reply to jprofitt

When I try your suggestion (I use red color for your text and lines):

    if (index >= EXTRA_ROWS) {
        MyItem item = (MyItem) myItems.getAt(index - EXTRA_ROWS);
        g.drawText(item.toString(), 0, y);

        g.setColor(Color.RED);                
        g.drawText("XXX", 0, y + (list.getRowHeight() - list.getFont().getHeight())/2);

        g.setColor(0x333333);
        // XXX why do I need to subtract 9 here?
        g.drawLine(0, y-9, width, y-9);

        g.setColor(Color.RED);
        g.drawLine(0, y, width, y);
        return;
    }

Then it doesn't really work - because the blue focus line does not align with your suggested (red) lines. It aligns with my (gray) lines, which means you really need to subtract -9 for some reason:

not aligned

Thank you! Alex

2

There are 2 answers

0
Vit Khudenko On BEST ANSWER

Yes, this is an odd behaviour. I guess this is smth OS 6 specific. Looks like in OS 6 ListField became so clever that it passes Y coordinate already prepared for direct usage in text drawing, so you don't have to do manual calculation (usually I calculate Y for text drawing in the same way jprofitt suggests). So assuming my guess is true I changed the code as follows:

if (index >= EXTRA_ROWS) {
    MyItem item = (MyItem) myItems.getAt(index - EXTRA_ROWS);
    g.drawText(item.toString(), 0, y);

    g.setColor(0x333333);
    // XXX why do I need to subtract 9 here?

    // use the offset instead
    int offset = (myList.getRowHeight() - getFont().getHeight()) >> 1;

    g.drawLine(0, y - offset, width, y - offset);

    return;
}

and it works fine (tested on all font sizes that are available in device settings).

1
jprofitt On

Alright I believe I got this figured out. What is going on is that your row height is greater than the font height. So when you draw your text right at y, you are drawing it top aligned to the actual row. In my case, row height was 40 and font height was 20. Half of that difference is where your y - 9 was coming in. If you change your drawText() calls to this, it should work without needing to subtract anything when drawing the line:

g.drawText(theString, 0, y + (list.getRowHeight() - list.getFont().getHeight())/2);

You could cache the font height and row height so you don't have to do the calculations in paint(), for efficiency's sake.