BlackBerry 6: ListFieldCallback.indexOfList() - how to filter while typing? - blackberry

I'm trying to display a TextField and a ListField below it:
And I would like to filter (aka "live search") the number of displayed rows, while the user is typing a word into the TextField.
I've tried calling ListField.setSearchable(true) but it doesn't change anything, even if I type words while having the ListField focussed.
And by the way I wonder which TextField to take. I've used AutoCompleteField because it looks exactly as I want the field to be (white field with rounded corners), but it is probably not the best choice (because I don't need AutoCompleteField's drop down list while typing).
Here is my current code -
MyScreen.java:
private ListField presetListField = new ListField();
private MyList presetList = new MyList(presetListField);
private MyScreen() {
int size;
getMainManager().setBackground(_bgOff);
setTitle("Favorites");
BasicFilteredList filterList = new BasicFilteredList();
String[] days = {"Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", "Sunday"};
int uniqueID = 0;
filterList.addDataSet(uniqueID, days, "days",
BasicFilteredList.COMPARISON_IGNORE_CASE);
// XXX probably a bad choice here?
AutoCompleteField autoCompleteField =
new AutoCompleteField(filterList);
add(autoCompleteField);
presetListField.setEmptyString("* No Favorites *", DrawStyle.HCENTER);
add(presetListField);
presetList.insert("Monday");
presetList.insert("Tuesday");
presetList.insert("Wednesday");
for (int i = 0; i < 16; i++) {
presetList.insert("Favorite #" + (1 + i));
}
}
MyList.java:
public class MyList implements ListFieldCallback {
private Vector _preset = new Vector();
private ListField _list;
public MyList(ListField list) {
_list = list;
_list.setCallback(this);
_list.setRowHeight(-2);
// XXX does not seem to have any effect
_list.setSearchable(true);
}
public void insert(String str) {
insert(str, _preset.size());
}
public void insert(String str, int index) {
_preset.insertElementAt(str, index);
_list.insert(index);
}
public void delete(int index) {
_preset.removeElementAt(index);
_list.delete(index);
}
public void drawListRow(ListField listField,
Graphics g, int index, int y, int width) {
Font f = g.getFont();
Font b = f.derive(Font.BOLD, f.getHeight() * 2);
Font i = f.derive(Font.ITALIC, f.getHeight());
g.setColor(Color.WHITE);
g.drawText((String)_preset.elementAt(index), Display.getWidth()/3, y);
g.setFont(i);
g.setColor(Color.GRAY);
g.drawText("Click to get frequency",
Display.getWidth()/3, y + g.getFont().getHeight());
g.setFont(b);
g.setColor(Color.YELLOW);
g.drawText(String.valueOf(100f + index/10f), 0, y);
}
public Object get(ListField list, int index) {
return _preset.elementAt(index);
}
public int indexOfList(ListField list, String prefix, int start) {
return _preset.indexOf(prefix, start);
}
public int getPreferredWidth(ListField list) {
return Display.getWidth();
}
}
Thank you!
Alex

Have you checked the net.rim.device.api.ui.component.KeywordFilterField ?

Related

Custom layout in blackberry

I need a custom layout as below in BlackBerry.
I did same layout in Android. Now I need same layout in BlackBerry. I am new to BlackBerryapp development. The Fields of BlackBerry like Views in Android seem to be very confusing things to me.
I tried with VerticalFieldManager & HorizontalFieldManager by mixing these with BitmapField & LabelField to produce my layout.
I failed particularly in placing LabelField at bottom of screen. I used USE_ALL_HEIGHT & FIELD_BOTTOM style to put at bottom, but it is showing after scrolling long time.
My requirement is the header and footer should not scroll when my middle list is scrolling.
The easiest way to add header and footer fields that don't scroll with the content in the middle of the screen is to use MainScreen#setBanner() and MainScreen#setStatus().Here's an example:
public class HeaderFooterListScreen extends MainScreen {
private static final int BG_COLOR = Color.BLACK;
private static final int HIGHLIGHT_COLOR = Color.BLUE;
private static final int FONT_COLOR = Color.WHITE;
private static final int ROW_HEIGHT = 60;
private Object[] _rowData;
private Field _header;
private Field _footer;
private Field _spacer;
private int _orientation;
public HeaderFooterListScreen() {
super(MainScreen.VERTICAL_SCROLL | MainScreen.VERTICAL_SCROLLBAR);
Background bg = BackgroundFactory.createSolidBackground(BG_COLOR);
setBackground(bg);
getMainManager().setBackground(bg);
// header
Bitmap headerImg = Bitmap.getBitmapResource("header.png");
_header = new BitmapField(headerImg);
setBanner(_header);
// list
_rowData = new Object[] { "row one", "row two", "row three" }; //, "row four", "row five", "row six", "row seven", "row eight", "row nine", "row ten" };
ListField list = new ListField();
int c = Color.RED;
XYEdges edgeColors = new XYEdges(c, c, c, c);
XYEdges edgeThicknesses = new XYEdges(5, 5, 5, 5);
list.setBorder(BorderFactory.createSimpleBorder(edgeThicknesses, edgeColors, Border.STYLE_SOLID));
list.setCallback(new CustomListFieldCallback());
list.setRowHeight(ROW_HEIGHT);
list.setSize(_rowData.length);
add(list);
// footer
_footer = new LabelField("Footer Showing Status As Text", Field.USE_ALL_WIDTH | DrawStyle.HCENTER) {
public void paint(Graphics g) {
// change font color
int oldColor = g.getColor();
g.setColor(FONT_COLOR);
super.paint(g);
g.setColor(oldColor);
}
};
_footer.setFont(_footer.getFont().derive(Font.PLAIN, 24));
setStatus(_footer);
}
private void centerList() {
if (_spacer != null && _spacer.getManager() != null) {
// delete the old spacer field, if there was one
delete(_spacer);
}
int listHeight = _rowData.length * ROW_HEIGHT;
int availableHeight = getHeight() - _footer.getHeight() - _header.getHeight();
if (availableHeight > listHeight) {
boolean firstRun = (_spacer == null);
// add a spacer above the list to force it down enough to be centered
final int SPACE = (availableHeight - listHeight) / 2;
_spacer = new Field() {
protected void layout(int width, int height) {
setExtent(width, SPACE);
}
protected void paint(Graphics graphics) {
}
};
insert(_spacer, 0);
if (firstRun) {
getMainManager().setVerticalScroll(0);
}
}
}
// called when device orientation changes
protected void sublayout(int width, int height) {
super.sublayout(width, height);
if (_orientation != Display.getOrientation()) {
_orientation = Display.getOrientation();
// run with invokeLater() to avoid recursive sublayout() calls
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
// TODO: may have to adjust header, too?
centerList();
}
});
}
}
private class CustomListFieldCallback implements ListFieldCallback {
private final int PAD = 10;
public void drawListRow(ListField listField, Graphics graphics,
int index, int y, int width) {
int oldColor = graphics.getColor();
if (listField.getSelectedIndex() == index) {
graphics.setColor(HIGHLIGHT_COLOR);
} else {
graphics.setColor(BG_COLOR);
}
graphics.fillRect(0, y, width, listField.getRowHeight());
graphics.setColor(FONT_COLOR);
String text = (String)get(listField, index);
graphics.drawText(text, PAD, y + PAD, DrawStyle.LEFT);
graphics.setColor(oldColor);
}
public Object get(ListField listField, int index) {
return _rowData[index];
}
public int getPreferredWidth(ListField listField) {
return Display.getWidth();
}
public int indexOfList(ListField listField, String prefix, int start) {
return -1; // TODO?
}
}
}
You didn't specify how you wanted the list in the middle to work, so I just made some guesses. I also wasn't sure if the red border was something you wanted, or just something you used to describe your layout. Edit your question, or post a new question, if you have more requirements for the list.
Field Concepts
If you're coming from Android, and are unclear about the role of BlackBerry UI classes, like Fields and Managers, here's some resources:
another Stack Overflow answer I posted
BlackBerry Advanced UI Sample Code on Github
BlackBerry Layout Managers Tutorial
Results

To attach a ButtonField on every row of CustomListField in BlackBerry [duplicate]

i m writing one application in which i have created custom list field for displaying listview.
my CustomListField contains one image and text in a row. i m gettiing field change listener on click of listfield row but i want to put fieldchange listener on image too..
can anyone tell me how can i do that.
here is my code.
public class CustomListField extends ListField implements ListFieldCallback {
private Vector _listData;
private int _MAX_ROW_HEIGHT = 60;
public CustomListField(Vector data) {
_listData = data;
setSize(_listData.size());
setSearchable(true);
setCallback(this);
setRowHeight(_MAX_ROW_HEIGHT);
}
protected void drawFocus(Graphics graphics, boolean on) {
XYRect rect = new XYRect();
graphics.setGlobalAlpha(150);
graphics.setColor(Color.BLUE);
getFocusRect(rect);
drawHighlightRegion(graphics, HIGHLIGHT_FOCUS, true, rect.x, rect.y, rect.width, rect.height);
}
public int moveFocus(int amount, int status, int time) {
this.invalidate(this.getSelectedIndex());
return super.moveFocus(amount, status, time);
}
public void onFocus(int direction) {
super.onFocus(direction);
}
protected void onUnFocus() {
this.invalidate(this.getSelectedIndex());
}
public void refresh() {
this.getManager().invalidate();
}
public void drawListRow(ListField listField, Graphics graphics, int index, int y, int w) {
listField.setBackground(BackgroundFactory.createBitmapBackground(Bitmap.getBitmapResource("listing_bg.png")));
ListRander listRander = (ListRander) _listData.elementAt(index);
graphics.setGlobalAlpha(255);
graphics.setFont(Font.getDefault().getFontFamily().getFont(Font.PLAIN, 24));
final int margin = 5;
final Bitmap thumb = listRander.getListThumb();
final String listHeading = listRander.getListTitle();
final Bitmap nevBar = listRander.getNavBar();
// list border
graphics.setColor(Color.GRAY);
graphics.drawRect(0, y, w, _MAX_ROW_HEIGHT);
// thumbnail border & thumbnail image
graphics.setColor(Color.BLACK);
// graphics.drawRoundRect(margin-2, y+margin-2,thumb.getWidth()+2, thumb.getHeight()+2, 5, 5);
graphics.drawBitmap(margin, y + margin, thumb.getWidth(), thumb.getHeight(), thumb, 0, 0);
// drawing texts
// graphics.setFont(Font.BOLD);
graphics.drawText(listHeading, margin + thumb.getWidth(), y + margin);
graphics.setColor(Color.GRAY);
// graphics.setFont(Font.smallFont); // graphics.drawText(listDesc, 2*margin+thumb.getWidth(), y+ margin+20); // //
// graphics.drawText(listDesc2, 2*margin+thumb.getWidth(), y+ margin+32);
// draw navigation button
final int navBarPosY = y + (_MAX_ROW_HEIGHT / 2 - nevBar.getHeight() / 2);
final int navBarPosX = Graphics.getScreenWidth() - nevBar.getWidth() + margin;
graphics.drawBitmap(navBarPosX, navBarPosY, nevBar.getWidth(), nevBar.getHeight(), nevBar, 0, 0);
}
public Object get(ListField listField, int index) {
String rowString = (String) _listData.elementAt(index);
return rowString;
}
public int indexOfList(ListField listField, String prefix, int start) {
for (Enumeration e = _listData.elements(); e.hasMoreElements();) {
String rowString = (String) e.nextElement();
if (rowString.startsWith(prefix)) {
return _listData.indexOf(rowString);
}
}
return 0;
}
public int getPreferredWidth(ListField listField) {
return 3 * listField.getRowHeight();
}
/*
protected boolean trackwheelClick(int status, int time) {
invalidate(getSelectedIndex());
Dialog.alert(" U have selected :" + getSelectedIndex());
return super.trackwheelClick(status, time);
}
*/
}
i want to put click listner on star image of listfield row
and following is output of abbove code.
I did something very similar to this on a past project:
Background
As Arhimed said in his answer, and as you can read about on the BlackBerry forums here, you can't have full-fledged Field objects within the ListField. The content of ListField rows is just drawn directly in drawListRow() as text, and Bitmaps, etc. The contents aren't Field instances, and therefore, are not focusable.
So, what I did was to replace ListField with a subclass of Manager. Originally, I used a VerticalFieldManager, but I ran into problems with that. I've also been seeing a lot of issues on stack overflow, where people subclass VerticalFieldManager, customize just one small behaviour, and everything starts breaking. It seems to me that VerticalFieldManager works well if you accept its normal behaviour, and if you need something more, just extend Manager directly. Performing layout for vertically stacked rows is pretty easy.
I then made each row its own Manager, and implemented custom layout in sublayout() to place the row's Fields where I wanted them. I could then also make the row focusable, and then a bitmap/button on the row separately focusable (like your star). Clicking the row invokes one action, and clicking the star invokes another one.
I will note, however, that in my app, performance was not an issue, because I only had 10-20 rows. Also, I did have to modify my code to match your example, so consider this code only lightly tested. However, I did build it into an app, so it should perform fine as long as my assumptions, and your description were valid.
Implementation
First, it wasn't clear to me what your ListRander is (you didn't show that code). However, in my code, I need a data class to contain details about one row. It looked like that's how you used ListRander, so that's what I used:
public class ListRander {
private String _title;
private Bitmap _thumb;
public ListRander(String title, Bitmap thumb) {
_title = title;
_thumb = thumb;
}
public String getTitle() {
return _title;
}
public Bitmap getThumb() {
return _thumb;
}
}
Then, I replaced your CustomListField class with my own:
public class CustomListField extends Manager implements FocusChangeListener {
private int _MAX_ROW_HEIGHT = 60;
private boolean _searchable = false;
private Vector _listData;
private FieldChangeListener _fieldListener;
public CustomListField(Vector data) {
super(FOCUSABLE | VERTICAL_SCROLL | VERTICAL_SCROLLBAR);
setSearchable(true);
setEditable(false);
setListData(data);
}
public void setChangeListener(FieldChangeListener listener) {
// we need to save this listener, because we set it to listen to all new rows
_fieldListener = listener;
int numFields = getFieldCount();
for (int f = 0; f < numFields; f++) {
getField(f).setChangeListener(listener);
}
super.setChangeListener(listener);
}
public int getRowHeight() {
return _MAX_ROW_HEIGHT;
}
public void setSearchable(boolean searchable) {
_searchable = searchable;
}
public int getSelectedIndex() {
return getFieldWithFocusIndex(); // TODO??
}
public Object get(int index) {
return _listData.elementAt(index);
}
public int indexOfList(String prefix, int start) {
if (start >= _listData.size() || !_searchable) {
return -1;
} else {
int result = getSelectedIndex(); // the default result if we find no matches
for (Enumeration e = _listData.elements(); e.hasMoreElements(); ) {
String rowString = (String) e.nextElement();
if (rowString.startsWith(prefix)) {
return _listData.indexOf(rowString);
}
}
return result;
}
}
protected boolean navigationClick(int status, int time) {
CustomListRow focus = (CustomListRow) getFieldWithFocus();
if (focus != null) {
// see if the row wants to process this click
if (!focus.navigationClick(status, time)) {
// let our FieldChangeListener know that this row has been clicked
fieldChangeNotify(getFieldWithFocusIndex());
}
return true;
} else {
return false;
}
}
protected void sublayout(int width, int height) {
int w = Math.min(width, getPreferredWidth());
int h = Math.min(height, getPreferredHeight());
int rowHeight = getRowHeight();
int numRows = getFieldCount();
setExtent(w, h);
setVirtualExtent(w, rowHeight * numRows);
for (int i = 0; i < numRows; i++) {
Field f = getField(i);
setPositionChild(f, 0, rowHeight * i);
layoutChild(f, w, rowHeight);
}
}
public int getPreferredWidth() {
return Display.getWidth();
}
public int getPreferredHeight() {
return Display.getHeight();
}
public void setListData(Vector listData) {
_listData = listData;
if (listData != null) {
int listSize = listData.size();
int numRows = getFieldCount();
for (int s = 0; s < listSize; s++) {
if (s < numRows) {
// we can reuse existing CustomListRows
CustomListRow row = (CustomListRow) getField(s);
row.setData((ListRander) listData.elementAt(s));
} else {
CustomListRow row = new CustomListRow((ListRander) listData.elementAt(s));
row.setChangeListener(_fieldListener);
row.setFocusListener(this);
add(row);
}
}
if (listSize < numRows) {
// delete the excess rows
deleteRange(listSize, numRows - listSize);
}
} else {
deleteAll();
}
invalidate();
}
public void focusChanged(Field field, int eventType) {
// we handle scrolling here, when focus changes between rows
if (eventType == FOCUS_GAINED) {
if (field.getTop() < getVerticalScroll()) {
// field is off the top of the screen, so scroll up
setVerticalScroll(field.getTop());
} else if (field.getTop() >= getVerticalScroll() + getVisibleHeight()) {
// field is off the bottom of the screen, so scroll down
setVerticalScroll(field.getTop() - getVisibleHeight() + getRowHeight());
}
}
}
}
Finally, one row is represented by my CustomListRow class:
public class CustomListRow extends Manager implements FieldChangeListener {
private static final int _MAX_ROW_HEIGHT = 60;
private ListRander _data;
private BitmapField _thumb;
private LabelField _title;
private FocusableBitmapField _star;
private static final Bitmap _starImg = Bitmap.getBitmapResource("star.png");
private static final Bitmap _bgImg = Bitmap.getBitmapResource("listing_bg.png");
private SeparatorField _separator;
private int _fontColor = Color.BLACK;
private boolean _highlighted = false;
private int _width;
// subclass exists to expose focus methods (make public)
private class FocusableBitmapField extends BitmapField {
public FocusableBitmapField() {
super(_starImg, BitmapField.FOCUSABLE | BitmapField.EDITABLE);
}
public void onFocus(int direction) {
super.onFocus(direction);
}
public void onUnfocus() {
super.onUnfocus();
}
}
public CustomListRow(ListRander data) {
super(Field.FOCUSABLE | Manager.NO_VERTICAL_SCROLL | Manager.NO_VERTICAL_SCROLLBAR);
setBackground(BackgroundFactory.createBitmapBackground(_bgImg));
_width = Display.getWidth();
long labelStyle = (DrawStyle.LEFT | DrawStyle.TOP | DrawStyle.ELLIPSIS);
_title = new LabelField("", labelStyle) { // custom anonymous class to change font color
protected void paint(Graphics g) {
int c = g.getColor();
g.setColor(_fontColor);
super.paint(g);
g.setColor(c);
}
};
_title.setFont(Font.getDefault().getFontFamily().getFont(Font.PLAIN, 24));
_thumb = new BitmapField();
_star = new FocusableBitmapField();
_star.setChangeListener(this);
_separator = new SeparatorField() { // custom anonymous class to change separator color
protected void paint(Graphics g) {
int c = g.getColor();
g.setColor(Color.GRAY);
super.paint(g);
g.setColor(c);
}
};
setData(data);
add(_thumb);
add(_title);
add(_star);
add(_separator);
}
public ListRander getData() {
return _data;
}
public void setData(ListRander value) {
if (value != _data) {
_data = value;
_title.setText(value.getTitle());
_thumb.setBitmap(value.getThumb());
}
}
private void onStarClicked() {
Dialog.alert("Star has been clicked or tapped!");
}
private void onRowClicked() {
Dialog.alert("Row has been clicked or tapped!");
}
public void fieldChanged(Field field, int context) {
if (field == _star) {
onStarClicked();
}
}
public boolean navigationClick(int status, int time) {
if (_star.isFocus()) {
onStarClicked();
return true;
} /* else {
onRowClicked();
return true;
} */
return false; // we will not consume this event
}
protected void highlight(boolean onRow) {
_fontColor = onRow ? Color.WHITE : Color.BLACK; // change font color for contrast
_highlighted = onRow;
invalidate();
}
protected void onFocus(int direction) {
// called when focus first transfers to this row, from another Field
if (direction == 1) {
// coming from top to bottom, we highlight the row first, not the star
highlight(true);
} else if (direction == -1) {
// coming from bottom to top, we highlight the star button first, not the row
_star.onFocus(direction);
highlight(false);
}
}
protected void onUnfocus() {
// remove highlighting of the row, if any
highlight(false);
super.onUnfocus();
}
protected int moveFocus(int amount, int status, int time) {
// called when this row already has focus (either on row, or star button)
if (amount > 0) {
// moving top to bottom
if (!_star.isFocus()) {
// we were on the row, now move to the star button
_star.onFocus(1);
highlight(false);
amount--; // consume one unit of movement
}
} else {
// moving from bottom to top
if (_star.isFocus()) {
// we were on the star button, now move back over to the row
_star.onUnfocus();
highlight(true);
amount++; // consume one unit of movement
}
}
return amount;
}
protected boolean touchEvent(net.rim.device.api.ui.TouchEvent event) {
// We take action when the user completes a click (a.k.a. unclick)
int eventCode = event.getEvent();
if ((eventCode == TouchEvent.UNCLICK) || (eventCode == TouchEvent.DOWN)) {
// Get the touch location, within this Manager
int x = event.getX(1);
int y = event.getY(1);
if ((x >= 0) && (y >= 0) && (x < _width) && (y < _MAX_ROW_HEIGHT)) {
int field = getFieldAtLocation(x, y);
if ((field >= 0) && (getField(field) == _star)) {
// Let event propagate to (star) button field
return super.touchEvent(event);
} else {
if (eventCode == TouchEvent.UNCLICK) {
// A completed click anywhere else in this row should popup details for this selection
fieldChangeNotify(1);
onRowClicked();
} else {
// This is just a soft touch (TouchEvent.DOWN), without full click
setFocus();
}
// Consume the event
return true;
}
}
}
// Event wasn't for us, let superclass handle in default manner
return super.touchEvent(event);
}
protected void sublayout(int width, int height) {
height = Math.min(getPreferredHeight(), height);
setExtent(_width, height);
final int margin = 5;
int thumbWidth = _thumb.getPreferredWidth();
layoutChild(_thumb, thumbWidth, _thumb.getPreferredHeight());
setPositionChild(_thumb, margin, margin);
int starWidth = _star.getPreferredWidth();
int starHeight = _star.getPreferredHeight();
layoutChild(_star, starWidth, starHeight);
setPositionChild(_star, width - starWidth - margin, (height - starHeight) / 2);
// this assumes you want margin between all fields, and edges
layoutChild(_title, width - thumbWidth - starWidth - 4 * margin, _title.getPreferredHeight());
setPositionChild(_title, margin + thumbWidth /* + margin */, margin); // TODO?
}
protected void paintBackground(Graphics g) {
super.paintBackground(g);
if (_highlighted) {
// you can't override drawFocus() for a Manager, so we'll handle that here:
int oldColor = g.getColor();
int oldAlpha = g.getGlobalAlpha();
XYRect rect = new XYRect();
g.setGlobalAlpha(150);
g.setColor(Color.BLUE);
getFocusRect(rect);
drawHighlightRegion(g, HIGHLIGHT_FOCUS, true, rect.x, rect.y, rect.width, rect.height);
g.setGlobalAlpha(oldAlpha);
g.setColor(oldColor);
}
}
public int getPreferredWidth() {
return _width;
}
public int getPreferredHeight() {
return _MAX_ROW_HEIGHT;
}
}
Usage
This is how you might use the whole list field (maybe in a Screen class):
public class ListScreen extends MainScreen implements FieldChangeListener {
public ListScreen() {
try {
Vector data = new Vector();
Bitmap icon = Bitmap.getBitmapResource("list_icon.png");
for (int i = 0; i < 15; i++) {
ListRander lr = new ListRander("Product Name " + i, icon);
data.addElement(lr);
}
CustomListField list = new CustomListField(data);
add(list);
list.setChangeListener(this);
} catch (Exception e) {
e.printStackTrace();
}
}
public void fieldChanged(Field field, int context) {
if (field instanceof CustomListRow) {
CustomListRow row = (CustomListRow) field;
Dialog.alert(row.getData().getTitle() + " was selected!");
}
}
}
In my app, it made sense for the CustomListRow itself to handle the equivalent of your star click. However, for me, it did not make sense to have the row click handled that way. So, I let you set a FieldChangeListener on the CustomListField itself, which is called back when any row is selected. See the example above in my screen class. If you want to handle the row click inside the CustomListRow class, too, that's fine. I laid out a onRowClicked() method there. Search in the code for where that's commented out, and you can reactivate, an implement that method (onRowClicked()).
Issues
My app didn't require list searching. I laid out a sample implementation of that, like ListField has. But, I didn't test it. That's your job, if you need it. I just got you started with the CustomListField implementation (see indexOfList()).
I didn't see what your "nav bar" was for. A bar is usually a full-width item, like a status bar, or toolbar. I don't see anything like that in your screenshot. A nav item might be a little arrow at the right side of each row, to bring up details. But, I didn't see that in your screenshot either. So, I ignored that code. If you need a nav bar, you obviously know what it should be, and can add that to my code above.
I couldn't tell whether or not you just added the star as part of the row's background image, or if you had a separate image for that. I added a separate star.png to represent the star. I would assume that clicking the star fills it in, or highlights it, or something. But, you didn't describe that problem, so I assume you can handle that. If you need a custom field to represent the star, that can have selected and unselected images, just post that as a new question.
You had some code that appeared like it was trying to set the row width to 3x the row height, but that didn't match your screen shot. Most lists are full-screen width anyway. So, I remove that code. My CustomListRow class implements getPreferredWidth() and requests the full screen width. Change if you like.
Unlike Android's ListView the BB's ListField is not designed to have a focusable/clickable fields inside of list items. So any attempt to workaround this will have some negative side effects.
A relatively easy/quick workaround would be to switch to VerticalFieldManager (check this other stack overflow question). But if the list is too long (more than several hundreds, I believe) you risk to "eat" too much memory.
If the app is designed for touch screens only, then you can try to stay with ListField + do some manual tracking of touch event coordinates. So when you detect a list field click (in a way you would normally do it) you can check whether the touch coordinates correspond to the star image area (at least on the X axis). I am not going to invent/provide an implementation, but just giving an idea.

ListField item background Color

I just want to know how can I change ListField's item background color. I have two items in my ListField like this one.
|First One|Second One.................|
I need to change first one's background color.
My drawListRow(..) method looks like this
public void drawListRow(ListField listField, Graphics graphics,
int index, int y, int width) {
int oldColor = 0;
try {
oldColor = graphics.getColor();
String txt = (vector.elementAt(index)).toString();
int xPos = 15;
int yPos = 5 + y;
//graphics.clear();
graphics.setColor(Color.GREEN);
graphics.fillRect(0, y, (Display.getWidth()*10/100), yPos);
graphics.drawText(txt, xPos, yPos);
//graphics.fillRect(0,(index*Display.getHeight()/10),Display.getWidth(),Display.getHeight()/10);
} finally {
graphics.setColor(oldColor);
}
}
But this is not working.
Though you have attached an image, I am still confused. The image didn't answer some question, for example, how it will look on a row get focused (I didn't understand actually).
But you can check following output and code. I think you can customize the look as you wish if you check the code.
Generated Output
How to use
public class MyScreen extends MainScreen {
private Vector listElements;
public MyScreen() {
setTitle("Custom ListField Demo");
// data for the ListField
listElements = new Vector();
for (int i = 0; i < 4; i++) {
listElements.addElement("Some text for row " + i);
}
ListField taskList = new ListField() {
// disable default focus drawing
protected void drawFocus(Graphics graphics, boolean on) {
};
};
taskList.setCallback(new ListCallback(listElements));
taskList.setSize(listElements.size());
taskList.setRowHeight(40);
add(taskList);
}
}
ListCallback implementation
class ListCallback implements ListFieldCallback {
final int COLOR_INDEX_NORMAL_BG = 0x1D6789;
final int COLOR_INDEX_FOCUSED_BG = 0x0E8CB3;
final int COLOR_NORMAL_BG = 0x2A2A2A;
final int COLOR_FOCUSED_BG = 0x1F1F1F;
private Vector listElements;
public ListCallback(Vector listElements) {
this.listElements = listElements;
}
public void drawListRow(ListField list, Graphics graphics, int index, int y,
int width) {
int rowHeight = list.getRowHeight(index);
boolean isSelectedRow = (list.getSelectedIndex() == index);
int indexBgColor = isSelectedRow ? COLOR_INDEX_FOCUSED_BG : COLOR_INDEX_NORMAL_BG;
int rowBgColor = isSelectedRow ? COLOR_FOCUSED_BG : COLOR_NORMAL_BG;
final int indexWidth = width / 10;
// draw row background
fillRectangle(graphics, rowBgColor, 0, y, width, rowHeight);
// draw index background
fillRectangle(graphics, indexBgColor, 0, y, indexWidth, rowHeight);
// set text color, draw text
Font font = list.getFont();
graphics.setColor(Color.WHITE );
graphics.setFont(font);
String indexText = "" + (index + 1);
String textToDraw = "";
try {
textToDraw = (String) listElements.elementAt(index);
} catch (Exception exc) {
}
int xText = (indexWidth - font.getAdvance(indexText)) / 2;
int yText = (rowHeight - font.getHeight()) / 2;
graphics.drawText(indexText, xText, y + yText, 0, indexWidth);
final int margin = 5;
int availableWidth = (width - indexWidth) - 2 * margin;
xText = indexWidth + margin;
yText = (rowHeight - font.getHeight()) / 2;
graphics.drawText(textToDraw, xText, y + yText, DrawStyle.ELLIPSIS, availableWidth);
}
private void fillRectangle(Graphics graphics, int color, int x, int y, int width, int height) {
graphics.setColor(color);
graphics.fillRect(x, y, width, height);
}
public Object get(ListField list, int index) {
// not implemented
return "";
}
public int indexOfList(ListField list, String prefix, int string) {
// not implemented
return 0;
}
public int getPreferredWidth(ListField list) {
return Display.getWidth();
}
}
If you need to change onFocus Background color than add drwFocus method on your ListField.
protected void drawFocus(Graphics graphics, boolean on) {
//get the focus rect area
XYRect focusRect = new XYRect();
getFocusRect(focusRect);
boolean oldDrawStyleFocus = graphics.isDrawingStyleSet(Graphics.DRAWSTYLE_FOCUS);
try {
if (on) {
//set the style so the fields in the row will update its color accordingly
graphics.setDrawingStyle(Graphics.DRAWSTYLE_FOCUS, true);
int oldColour = graphics.getColor();
try {
graphics.setColor(0xc8d3db); //set the color and draw the color
graphics.fillRect(focusRect.x, focusRect.y,
focusRect.width, focusRect.height);
} finally {
graphics.setColor(oldColour);
}
//to draw the row again
drawListRow(this, graphics, getSelectedIndex(),
focusRect.y, focusRect.width);
// drawRow(graphics, focusRect.x,focusRect.y, focusRect.width,focusRect.height);
}
} finally {
graphics.setDrawingStyle(Graphics.DRAWSTYLE_FOCUS, oldDrawStyleFocus);
}
}
Check the edited answer,
protected void drawFocus(Graphics graphics, boolean on) {
XYRect focusRect = new XYRect();
getFocusRect(focusRect);
boolean oldDrawStyleFocus = graphics.isDrawingStyleSet(Graphics.DRAWSTYLE_FOCUS);
try {
if (on) {
graphics.setDrawingStyle(Graphics.DRAWSTYLE_FOCUS, true);
int oldColour = Color.BLACK;
try {
graphics.fillRect(focusRect.x, focusRect.y,
focusRect.width, focusRect.height);
} finally {
graphics.setColor(oldColour);
}
//to draw the row again
drawListRow(this, graphics, getSelectedIndex(),
focusRect.y, focusRect.width);
}
} finally {
graphics.setDrawingStyle(Graphics.DRAWSTYLE_FOCUS, oldDrawStyleFocus);
}
}

How to get multiple selected list items from list field checkbox and add into an arraylist in blackberry

Please anyone help me get selected listitems from a listfieldcheckbox, and add them into an arraylist. If possible, give any useful links also. Here's my code so far (I am new to blackberry application development). Please help.
package mypackage;
import java.util.Vector;
import net.rim.device.api.system.Characters;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.ListField;
import net.rim.device.api.ui.component.ListFieldCallback;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
import net.rim.device.api.util.IntVector;
/**
* A class extending the MainScreen class, which provides default standard
* behavior for BlackBerry GUI applications.
*/
public final class MyScreen extends MainScreen implements ListFieldCallback
{
private Vector _listData = new Vector();
private Vector _checkedData = new Vector();
private ListField listField;
private static final String[] _elements = {"First element", "Second element","Third element"
};
//private static final String[] _elements1 = {"hai","welcome","where r u"
//};
private MenuItem _getDataMenu,selectall,Delete;
Vector result = new Vector();
protected void makeMenu(Menu menu, int instance)
{
menu.add(_getDataMenu);
menu.add(selectall);
menu.add(Delete);
//Create the default menu.
super.makeMenu(menu, instance);
}
private class ChecklistData
{
private String _stringVal;
private boolean _checked;
ChecklistData(String stringVal, boolean checked)
{
_stringVal = stringVal;
_checked = checked;
}
//Get/set methods.
private String getStringVal()
{
return _stringVal;
}
private boolean isChecked()
{
return _checked;
}
//Toggle the checked status.
private void toggleChecked()
{
_checked = !_checked;
}
}
public Vector getCheckedItems() {
return _checkedData;
}
/**
* Creates a new MyScreen object
*/
public MyScreen()
{
// Set the displayed title of the screen
setTitle("MyTitle");
VerticalFieldManager main = new VerticalFieldManager(VerticalFieldManager.USE_ALL_HEIGHT|
VerticalFieldManager.USE_ALL_WIDTH|VerticalFieldManager.VERTICAL_SCROLL);
this.add(main);
HorizontalFieldManager hfm = new HorizontalFieldManager();
main.add(hfm);
listField = new ListField(){
//Allow the space bar to toggle the status of the selected row.
protected boolean keyChar(char key, int status, int time)
{
boolean retVal = false;
//If the spacebar was pressed...
if (key == Characters.SPACE)
{
//Get the index of the selected row.
int index = getSelectedIndex();
//Get the ChecklistData for this row.
ChecklistData data = (ChecklistData)_listData.elementAt(index);
//Toggle its status.
data.toggleChecked();
//Update the Vector with the new ChecklistData.
_listData.setElementAt(data, index);
//Invalidate the modified row of the ListField.
invalidate(index);
//Consume this keyChar (key pressed).
retVal = true;
}
return retVal;
}
};
listField.setCallback(this);
reloadList();
int elementLength = _elements.length;
for(int count = 0; count < elementLength; ++count)
{
_listData.addElement(new ChecklistData(_elements[count], false));
//_listData.addElement(new ChecklistData(_elements1[count], false));
listField.insert(count);
}
main.add(listField);
_getDataMenu =new MenuItem("Get Data", 200, 10) {
public void run(){
int index = listField.getSelectedIndex();
ChecklistData data = (ChecklistData)_listData.elementAt(index);
String message = "Selected data: " + data.getStringVal() + ", and status: " + data.isChecked();
//Dialog.alert(message);
// get all the checked data indices
IntVector selectedIndex = new IntVector(0, 1);
//ChecklistData data;
for (int i=0;i<_listData.size();i++) {
data = (ChecklistData)_listData.elementAt(i);
if(data.isChecked()) {
selectedIndex.addElement(i);
String selectedvalues = data.getStringVal();
System.out.println("Selected items are:"+selectedvalues);
}
}
data = null;
// now selectedIndex will contain all the checked data indices.
//String message = "Selected data: " + data.getStringVal() + ", and status: " + data.isChecked();
}
};
selectall = new MenuItem("Selectall", 200, 10){
public void run(){
int elementLength = _elements.length;
for(int count = 0; count < elementLength; ++count)
{
_listData.setElementAt(new ChecklistData(_elements[count], true), count);
}
}
};
Delete = new MenuItem("Delete", 200, 10){
public void run(){
int index = listField.getSelectedIndex();
_listData.removeElementAt(index);
// update the view
listField.delete(index);
listField.invalidate(index);
}
};
}
private void reloadList() {
// TODO Auto-generated method stub
_listData.setSize(_listData.size());
}
public void drawListRow(ListField list, Graphics graphics, int index, int y, int w)
{
ChecklistData currentRow = (ChecklistData)this.get(list, index);
StringBuffer rowString = new StringBuffer();
if (currentRow.isChecked())
{
rowString.append(Characters.BALLOT_BOX_WITH_CHECK);
}
else
{
rowString.append(Characters.BALLOT_BOX);
}
//Append a couple spaces and the row's text.
rowString.append(Characters.SPACE);
rowString.append(Characters.SPACE);
rowString.append(currentRow.getStringVal());
//Draw the text.
graphics.drawText(rowString.toString(), 0, y, 0, w);
/*if (currentRow.isChecked()) {
if( -1 ==_checkedData.indexOf(currentRow))
_checkedData.addElement(currentRow);
rowString.append(Characters.BALLOT_BOX_WITH_CHECK);
}
else {
if( -1 !=_checkedData.indexOf(currentRow))
_checkedData.removeElement(currentRow);
rowString.append(Characters.BALLOT_BOX);
} */
}
//Returns the object at the specified index.
public Object get(ListField list, int index)
{
return _listData.elementAt(index);
}
public int indexOfList(ListField list, String p, int s)
{
//return listElements.getSelectedIndex();
return _listData.indexOf(p, s);
}
//Returns the screen width so the list uses the entire screen width.
public int getPreferredWidth(ListField list)
{
return Display.getWidth();
}
protected boolean navigationClick(int status, int time) {
int index1 = listField.getSelectedIndex();
/*System.out.println("Selected item index:"+index1);
//int[] list =listField.getSelection();
//String s = Integer.toString(list);
System.out.println(" items are:"+_elements[index1]);
//ChecklistData data = (ChecklistData)_listData.elementAt(index1);*/
//Get the ChecklistData for this row.
ChecklistData data = (ChecklistData)_listData.elementAt(index1);
String message = "Selected data: " + data.getStringVal() + ", and status: " + data.isChecked();
System.out.println("message is:"+message);
//Toggle its status.
data.toggleChecked();
//Update the Vector with the new ChecklistData.
_listData.setElementAt(data, index1);
//Invalidate the modified row of the ListField.
listField.invalidate(index1);
return true;
}
}

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

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.
Below is my test code and the border.png (used as BasicEditField border) is attached:
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:
Thank you!
Alex
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).
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.

Resources