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.
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;
}
}
The task is to backup/restore Persistable object with BB Desktop Manager or in any other way. The main aim is to keep data between device firmware updates...
I have:
public final class UserList implements Persistable {
//The persistable objects.
private Hashtable fData;
//Initialize the class with empty values.
public UserList() {
fData = new Hashtable();
}
//Initialize the class with the specified values.
public UserList(Hashtable p) {
fData = p;
}
public Hashtable getData() {
return fData;
}}
I also have implemented SyncItem (as found in one of the examples)
public final class UserListSync extends SyncItem {
private static UserList fList;
private static final int FIELDTAG_NAME = 1;
private static final int FIELDTAG_AGE = 2;
private static PersistentObject store;
static {
store = PersistentStore.getPersistentObject(0x3167239af4aa40fL);
}
public UserListSync() {
}
public String getSyncName() {
return "Sync Item Sample";
}
public String getSyncName(Locale locale) {
return null;
}
public int getSyncVersion() {
return 1;
}
public boolean getSyncData(DataBuffer db, int version) {
boolean retVal = true;
synchronized (store) {
if (store.getContents() != null) {
fList = (UserList)store.getContents();
}
}
try {
Enumeration e = fList.getData().keys();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
String value = (String) fList.getData().get(key);
//Write the name.
db.writeShort(key.length() + 1);
db.writeByte(FIELDTAG_NAME);
db.write(key.getBytes());
db.writeByte(0);
//Write the age.
db.writeShort(value.length() + 1);
db.writeByte(FIELDTAG_AGE);
db.write(value.getBytes());
db.writeByte(0);
}
} catch (Exception e) {
retVal = false;
}
return retVal;
}
//Interprets and stores the data sent from the Desktop Manager.
public boolean setSyncData(DataBuffer db, int version) {
int length;
Hashtable table = new Hashtable();
Vector keys = new Vector();
Vector values = new Vector();
boolean retVal = true;
try {
//Read until the end of the Databuffer.
while (db.available() > 0) {
//Read the length of the data.
length = db.readShort();
//Set the byte array to the length of the data.
byte[] bytes = new byte[length];
//Determine the type of data to be read (name or age).
switch (db.readByte()) {
case FIELDTAG_NAME:
db.readFully(bytes);
keys.addElement(new String(bytes).trim());
break;
case FIELDTAG_AGE:
db.readFully(bytes);
values.addElement(new String(bytes).trim());
break;
}
}
} catch (Exception e) {
retVal = false;
}
for (int i = 0; i < keys.size(); i++) {
table.put(keys.elementAt(i), values.elementAt(i));
}
try {
//Store the new data in the persistent store object.
fList = new UserList(table);
store.setContents(fList);
store.commit();
} catch (Exception e) {
retVal = false;
}
return retVal;
}}
The entry poing is following:
public class SyncItemSample extends UiApplication {
private static PersistentObject store;
private static UserList userList;
static {
store = PersistentStore.getPersistentObject(0x3167239af4aa40fL);
}
public static void main(String[] args) {
SyncItemSample app = new SyncItemSample();
app.enterEventDispatcher();
}
public SyncItemSample() {
UserListScreen userListScreen;
//Check to see if the store exists on the BlackBerry.
synchronized (store) {
if (store.getContents() == null) {
//Store does not exist, create it with default values
userList = new UserList();
store.setContents(userList);
store.commit();
} else {
//Store exists, retrieve data from store.
userList = (UserList)store.getContents();
}
}
//Create and push the UserListScreen.
userListScreen = new UserListScreen(userList);
pushScreen(userListScreen);
}}
And here is an implementation of screen:
public final class UserListScreen extends MainScreen {
Vector fLabels = new Vector();
Vector fValues = new Vector();
VerticalFieldManager leftColumn = new VerticalFieldManager();
VerticalFieldManager rightColumn = new VerticalFieldManager();
UserList fList;
public UserListScreen(UserList list) {
super();
fList = list;
//Create a horizontal field manager to hold the two vertical field
//managers to display the names and ages in two columns.
VerticalFieldManager inputManager = new VerticalFieldManager();
HorizontalFieldManager backGround = new HorizontalFieldManager();
//Array of fields to display the names and ages.
LabelField title = new LabelField("User List",
LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH);
setTitle(title);
final TextField fld1 = new TextField(TextField.NO_NEWLINE);
fld1.setLabel("input label");
inputManager.add(fld1);
final TextField fld2 = new TextField(TextField.NO_NEWLINE);
fld2.setLabel("input value");
inputManager.add(fld2);
final ButtonField fld3 = new ButtonField();
fld3.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
fList.getData().put(fld1.getText().trim(), fld2.getText().trim());
refresh();
}
});
fld3.setLabel("add");
inputManager.add(fld3);
add(inputManager);
//Add the column titles and a blank field to create a space.
LabelField leftTitle = new LabelField("label ");
leftColumn.add(leftTitle);
LabelField rightTitle = new LabelField("value");
rightColumn.add(rightTitle);
refresh();
//Add the two vertical columns to the horizontal field manager.
backGround.add(leftColumn);
backGround.add(rightColumn);
//Add the horizontal field manager to the screen.
add(backGround);
}
private void refresh() {
leftColumn.deleteAll();
rightColumn.deleteAll();
fLabels.removeAllElements();
fValues.removeAllElements();
//Populate and add the name and age fields.
Enumeration e = fList.getData().keys();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
String value = (String) fList.getData().get(key);
final LabelField tmp1 = new LabelField(key);
final LabelField tmp2 = new LabelField(value);
leftColumn.add(tmp1);
rightColumn.add(tmp2);
fLabels.addElement(tmp1);
fValues.addElement(tmp2);
}
}
public boolean onClose() {
System.exit(0);
return true;
}}
So as you see it should be very easy...
So all of these I run application, add values to Persistent object and they are added correctly, are stored during device resets and so on...
When I run Desktop Manager and make a Backup it seems that UserList is backed-up, as size of backup grows together with adding new data into persistent store.
But when I run "Wipe device" on my BB 9300 (and all data from Persistent store is cleared as it is expected) and then run Restore from just made backup file - nothing is updated in the Application and persistent store is seems to be empty.
In some examples I have found adding alternate entry point "init" but I can't tune eveything like it is described with my EclipsePlugin
Could you advice me how to store data in backup file and the to retrieve the same data from backup and load it back to the application, or how to log any of events with Desktop Manager?
If someone has experienced the same problem you can try to disconnect the device before wiping it. It is strange but it helped :)
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 ?
Right now, I'm trying to figure out how to implement the following:
Suppose I have a custom Manager that has about 10 or so BitmapFields layed out in a horizontal manner (similar to a slideshow contained in a HFM ) . What I want to achieve is to be able to move the image HFM via touchEvent horizontally, where a BitmapField would take focus on the left-hand side of the custom Manager. In other words, will I have to give a value to setHorizontalScroll and if so, is it a matter of just incrementing that value when the user makes a left or right touch event. Also, how can I get the focus of a Field within a given position on the screen (i.e. the left-most Field on the HFM) when the HFM is scrolling sideways via touchEvent?
1 - yes, setHorizontalScroll should work, don't forget to use HORIZONTAL_SCROLL in manager constructor
2 - try to test each Field getContentRect() for EventTouch getX(int) and getY(int)
UPDATE
To simplify global field position calculation use
public XYPoint getGlobalXY(Field field) {
XYPoint result = new XYPoint(field.getLeft(), field.getTop());
if (field.getManager() != null) {
result.translate(getGlobalXY(field.getManager()));
}
return result;
}
Thread safe message dialog:
public void showMessage(final String message) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.inform(message);
}
});
}
Sample code:
class Scr extends MainScreen {
HorizontalFieldManager hfm;
public Scr() {
add(new LabelField("asdfsad"));
hfm = new HorizontalFieldManager(HORIZONTAL_SCROLL);
for (int i = 0; i < 5; i++) {
Bitmap bmp = new Bitmap(100, 100);
Graphics g = Graphics.create(bmp);
g.setFont(g.getFont().derive(100));
String txt = String.valueOf(i);
int x = g.getFont().getAdvance(txt);
g.drawText(txt, x, 0);
BitmapField bf = new BitmapField(bmp);
hfm.add(bf);
}
add(hfm);
}
protected boolean touchEvent(TouchEvent message) {
if (message.getEvent() == TouchEvent.CLICK) {
int x = message.getX(1);
int y = message.getY(1);
XYRect r = hfm.getExtent();
r.setLocation(getGlobalXY(hfm));
if (r.contains(x, y)) {
XYRect rf = hfm.getField(2).getExtent();
rf.setLocation(getGlobalXY(hfm.getField(2)));
if (x < rf.x) {
showMessage("left side");
} else if (x > rf.X2()) {
showMessage("right side");
} else {
showMessage("field");
}
}
}
return super.touchEvent(message);
}
}