How do you make a dropdown list that displays images to the user instead of text?

6k views Asked by At

The ObjectChoiceField field meets all my requirements but it is not pretty.

This is what I have:

String pets[] = {"Dog", "Cat", "Duck" };
ObjectChoiceField dd = new ObjectChoiceField("My Pet",pets,0,ObjectChoiceField.FIELD_LEFT);

But I would prefer to have pictures in the dropdown. My understanding is that the object array must contain objects that support the toString method. There are got to be a way to do I see it in other apps, I just can't find the correct object in the API.

It doesn't have to be an ObjectChoiceField.

4

There are 4 answers

0
Maksym Gontar On BEST ANSWER

I would use custom ButtonField and PopupScreen. Two reasons:

  • in mobile design it's better to use screen effectively, and classic desktop dropdown control seems to be less sufficient than popup, at least for complex items (image + text)
  • it's easier :)

alt text http://img405.imageshack.us/img405/3746/dropdown.jpg

DropDownItem:

class DropdownItem {
 Bitmap mBitmap;
 String mName;

 public DropdownItem(Bitmap bitmap, String name) {
  this.mBitmap = bitmap;
  this.mName = name;
 }
}

Custom ButtonField:

class BitmapButtonField extends ButtonField {
 protected DropdownItem mItem;
 boolean mTextItem;
 int mWidth;
 int mHeight;

 public BitmapButtonField(DropdownItem item, boolean textItem) {
  super(CONSUME_CLICK);
  mItem = item;
  mTextItem = textItem;
  mWidth = mItem.mBitmap.getWidth() + 6
    + (mTextItem ? getFont().getAdvance(mItem.mName) + 6 : 0);
  mHeight = mItem.mBitmap.getHeight() + 6;
  setMargin(0, 0, 0, 0);
  setPadding(0, 0, 0, 0);
  setBorder(BorderFactory.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
  setBorder(VISUAL_STATE_ACTIVE, BorderFactory
    .createSimpleBorder(new XYEdges(0, 0, 0, 0)));
 }

 protected void paint(Graphics graphics) {
  int color = (getVisualState() == VISUAL_STATE_FOCUS) ? Color.LIGHTGREY
    : Color.DARKGRAY;
  graphics.setColor(color);
  graphics.drawRect(1, 1, mWidth - 2, mHeight - 2);
  graphics.drawBitmap(3, 3, mItem.mBitmap.getWidth(), mItem.mBitmap
    .getHeight(), mItem.mBitmap, 0, 0);
  if (mTextItem)
   graphics.drawText(mItem.mName, mItem.mBitmap.getWidth() + 6, 3);

 }

 public int getPreferredWidth() {
  return mWidth;
 }

 public int getPreferredHeight() {
  return mHeight;
 }

 protected void layout(int width, int height) {
  setExtent(mWidth, mHeight);
 }
}

Dropdown control itself:

class DDImagesButton extends BitmapButtonField implements FieldChangeListener {
 DropdownItem[] mItems;
 int mIndex;

 public DDImagesButton(DropdownItem[] items) {
  super(items[0], false);
  mItems = items;
  updateIndex(0);
  setChangeListener(this);
 }

 protected void paint(Graphics graphics) {
  super.paint(graphics);

  int x = mItems[mIndex].mBitmap.getWidth() + 2;
  int y = 5;

  int y1 = y;
  int y2 = y + 10;
  int x1 = x;
  int x2 = x + 18;
  int[] xPts = new int[] { x1, x2, x1 + 9 };
  int[] yPts = new int[] { y1, y1, y2 };
  graphics.drawFilledPath(xPts, yPts, null, null);
 }

 public void fieldChanged(Field field, int context) {
  getScreen().getUiEngine().pushScreen(new DDImagesPopUp());
 }

 public void updateIndex(int index) {
  mIndex = index;
  mItem = mItems[mIndex];
  mWidth = mItem.mBitmap.getWidth() + 6 + 18 + 3;
  mHeight = mItem.mBitmap.getHeight() + 6;
  invalidate();
 }

 class DDImagesPopUp extends PopupScreen implements FieldChangeListener {

  public DDImagesPopUp() {
   super(
     new VerticalFieldManager(VERTICAL_SCROLL
       | VERTICAL_SCROLLBAR));
   for (int i = 0; i < mItems.length; i++) {
    BitmapButtonField button = new BitmapButtonField(mItems[i],
      true);
    add(button);
    button.setChangeListener(this);
   }
   setFieldWithFocus(getField(mIndex));
  }

protected boolean keyChar(char key, int status, int time) {
    if (Keypad.KEY_ESCAPE == key) {
        this.close();
        return true;
    } else
        return super.keyChar(key, status, time);
}

  public void fieldChanged(Field field, int context) {
   updateIndex(getFieldWithFocusIndex());
   close();
  }
 }
}

Sample of use:

class Scr extends MainScreen {
 DDImagesButton ddImages1;
 DDImagesButton ddImages2;

 public Scr() {
  HorizontalFieldManager hfm = new HorizontalFieldManager();
  add(hfm);

  DropdownItem[] items = new DropdownItem[6];
  items[0] = new DropdownItem(Bitmap.getBitmapResource("1.png"),
    "Add Item");
  items[1] = new DropdownItem(Bitmap.getBitmapResource("2.png"),
    "Attachment");
  items[2] = new DropdownItem(Bitmap.getBitmapResource("3.png"), "Time");
  items[3] = new DropdownItem(Bitmap.getBitmapResource("4.png"), "User");
  items[4] = new DropdownItem(Bitmap.getBitmapResource("5.png"), "Group");
  items[5] = new DropdownItem(Bitmap.getBitmapResource("6.png"),
    "Information");
  ddImages1 = new DDImagesButton(items);
  hfm.add(ddImages1);

  ddImages2 = new DDImagesButton(items);
  hfm.add(ddImages2);
 }
}
0
paracycle On

My answer will be along the lines of jitter's response. The general idea for the kind of customizations that you want is to override the default behavior of the basic components.

Suppose, the choices that you want to display can be encapsulated by a class named Choice declared as follows:

private class Choice
{
  public Bitmap image;
  public String label;

  public Choice(String name)
  {
    this.image = Bitmap.getBitmapResource(name + ".png");
    this.label = name;
  }

  public String toString()
  {
    return this.label;
  }
}

then you can declare an ObjectListField instance as:

ObjectChoiceField choice = new ObjectChoiceField()
{      
  protected void paint(Graphics graphics)
  {
    // Get the current selected Choice
    Choice item = (Choice) this.getChoice(getSelectedIndex());

    int xOffset = 5; // 5 px padding on the left
    graphics.drawBitmap(xOffset, 0, 
                        item.image.getWidth(), 
                        item.image.getHeight(), 
                        item.image, 
                        0, 0);
    // Add text after the image and 10px padding.
    xOffset += item.image.getWidth() + 10; 
    graphics.drawText(item.label, xOfffset, 0);
  }            
};

set your choice items as:

choice.setChoices(new Choice[]{ new Choice("choice 1"), new Choice("choice 2")});

and then add it to your Screen (or FieldManager of your choice) using:

add(choice);

I have not been able to override the actual selection pop-up menu items. This seems to call the toString() method of your choice items. That is why I have overriden the default implementation of toString() in the Choice class so that we can display logical names in that pop-up.

0
jitter On

I'm not familiar with blackberry development but I guess you could subclass ObjectChoiceField and overwrite the layout(int, int) and the paint(Graphics) methods.

And then in paint(Graphics) maybe use the drawImage(...) method of the passed in Graphics-object to draw the image.

Just a wild guess

4
Thibaud de Souza On

If this is a java widget then chances are you can pass simple html as items to display in the choice field (or have toString() return simple html). Then if that's the case and you pass an image URL/relative path the image should display. AFAIK that would work in Swing at least, e.g ...

"< html> Dog < img src="dog.png">< /html>"

(spaces added for the code to show up in preview)