I'm developing an application in BlackBerry and I have many HorizontalFieldManagers filled with components like shown in the image below:
And I have to add many HorizontalFieldManagers like these within a for loop which are dynamically populated. When taken together, it looks like a single row in a list. So far, I have been able to do this.
But the problem is, the user should be able to click a "row" like this. But as the HorizontalFieldManager is not focusable or clickable, and because it has 4 components inside it and all 4 are equally important, I have not been able to figure-out a way to do this.
So can anyone please suggest a way to do what I'm trying? Basically, the user should be able to click a "row" which is shown in the image. This "row" is made up of many components (2 HorizontalFieldManagers, 1 VerticalFieldManager, 1 BitmapField and 3 LabelFields).
Any help greatly appreciated!
BBdev directly answered your question, but as you noticed, the performance is quite slow. This is because using a manager to stamp out hundreds of rows like this adds a lot of overhead to the layout step of the UI. Layout will run against all of those managers, every time something changes on the screen - including each time you add a new row. This is fundamentally quadratic.
The way to speed this up is to use a ListField instead. The one pain point of a ListField is that you have to do the drawing directly, instead of relying on the standard OS fields. But the advantage is that the ListField performs very quickly - each row has a fixed height, so the ListField can quickly determine which rows are visible, and then call the paint code just for those rows.
This means the work scales with the visible size of the field, instead of the virtual size of the field. This is a very desirable property to maintain when writing UI code, as the virtual depth of a UI has no bounds, but the physical screen has a fixed number of pixels, so by scaling the work with the visible portion of the UI, you maintain good performance.
The below code will make your HorizontalFieldManager clickable, And add your component in this Hfm as you want.
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.TouchEvent;
import net.rim.device.api.ui.Touchscreen;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.component.NullField;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.container.MainScreen;
public class sample extends MainScreen implements FieldChangeListener{
HorizontalFieldManager logInDetailManager=null;
int background_color=0;
public sample() {
logInDetailManager = new HorizontalFieldManager(Manager.USE_ALL_WIDTH|Field.FOCUSABLE){
protected void sublayout(int maxWidth, int maxHeight) {
int height=40;
super.sublayout(Display.getWidth(), height);
setExtent(Display.getWidth(), height);
}
protected void paint(Graphics graphics) {
graphics.setBackgroundColor(background_color);
graphics.clear();
invalidate();
super.paint(graphics);
}
protected void onFocus(int direction) {
super.onFocus(direction);
background_color=Color.RED;
invalidate();
}
protected void onUnfocus() {
invalidate();
background_color=Color.GREEN;
}
protected boolean navigationClick(int status, int time) {
if(Touchscreen.isSupported()){
return false;
}else{
fieldChangeNotify(1);
return true;
}
}
protected boolean touchEvent(TouchEvent message)
{
if (TouchEvent.CLICK == message.getEvent())
{
FieldChangeListener listener = getChangeListener();
if (null != listener)
this.setFocus();
listener.fieldChanged(this, 1);
}
return super.touchEvent(message);
}
};
logInDetailManager.setChangeListener(this);
logInDetailManager.add(new LabelField("hello"));
logInDetailManager.add(new NullField(Field.FOCUSABLE));
add(logInDetailManager);
add(new LabelField("good",Field.FOCUSABLE));
}
public void fieldChanged(Field field, int context) {
if(field==logInDetailManager){
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.inform("Hi how are you?");
}
});
}
}
}
Hope this will help you . Cheers :)
How to handle the focus color when you have an array of horizontal fields instead?
for (int i = 0; i < listSize; i++) {
logInDetailManager[i] = new HorizontalFieldManager(
Manager.USE_ALL_WIDTH | Field.FOCUSABLE) {
Since one item of the array is always focused the whole list is all red.
Related
I am facing lot of issues while implement the auto suggest :I will tell you my issues .
1) Focus is always comes on auto suggest /complete field.First there is button .But focus is also on autosuggest field as shown in figure
2) When I write m on auto suggest field monday is display when I click the track pad it display monday on autosuggest field But Monday of pop up is not hide.
3) what is this encircle .How to change is gray color (or remove this color).some times it display on whole field and some time on half field
4) I just want a auto suggest with fix width not whole width .Like this on a screen with black background and autosuggest (edit field have white background no gray when focus comes on auto suggest)
Example
Label Auto suggest
Days Auto-complete of days
public final class MyScreen extends MainScreen implements JsonObserver
{
/**
* Creates a new MyScreen object
*/
public MyScreen()
{
// Set the displayed title of the screen
setTitle("Drop-down List Demo");
VerticalFieldManager vfm=new VerticalFieldManager(){
protected void sublayout(int maxWidth, int maxHeight) {
// TODO Auto-generated method stub
super.sublayout(maxWidth, maxHeight);
setExtent(maxWidth, maxHeight/2);
}
protected void paint(Graphics graphics) {
// TODO Auto-generated method stub
graphics.setColor(Color.BLACK);
graphics.fillRect(0, 0, Display.getWidth(), Display.getHeight()/2);
super.paint(graphics);
}
};
vfm.add(new ButtonField("add"));
BasicFilteredList filterList = new BasicFilteredList();
String[] days = {"Monday","Tuesday","Wednesday",
"Thursday","Friday","Saturday","Sunday"};
filterList.addDataSet(1,days,"days",BasicFilteredList.COMPARISON_IGNORE_CASE);
AutoCompleteField autoCompleteField = new AutoCompleteField(filterList);
vfm.add(autoCompleteField);
vfm.add(new ButtonField("addpppp"));
add(vfm);
}
}
I want to trap events on two custom Fields which I had created by extending the Field class.
The events should only be touch events.
One field uses graphics.drawRect(10,10,20,20) and other field uses graphics.drawRect(50,50,20,20).
(I will not use hardcoded values, but am writing them here just for an example).
I should be able to trap the events individually; that means different events on different fields.
My code looks like:
Main screen Class:
package mypackage;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.container.MainScreen;
public final class MyScreen extends MainScreen
{
CustomFieldManager cfm;
public MyScreen()
{
cfm=new CustomFieldManager();
CustomButtonField cb=new CustomButtonField(Field.FOCUSABLE,20,20,40,40);
CustomButtonField cb1=new CustomButtonField(Field.FOCUSABLE,70,70,40,40);
new CustomButtonField(cb,cb1);
cfm.add(cb);
cfm.add(cb1);
add(cfm);
}
}
Field class:
package mypackage;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.component.Dialog;
public class CustomButtonField extends Field {
int a,b,c,d;
CustomButtonField c1,c2;
public CustomButtonField(long style,int a,int b,int c,int d){
super(style);
this.a=a;
this.b=b;
this.c=c;
this.d=d;
}
public CustomButtonField(Object o1,Object o2){
c1=(CustomButtonField)o1;
c2=(CustomButtonField)o2;
}
public void movefirst(){
Dialog.alert("god");
}
protected void drawFocus(Graphics graphics, boolean on) {
}
protected void layout(int width, int height) {
setExtent(200, 200);
}
protected void paint(Graphics graphics) {
graphics.drawRect(a, b, c, d);
}
protected boolean navigationClick(int status, int time) {
fieldChangeNotify(0);
return true;
}
}
and manager class :
package mypackage;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.component.Dialog;
public class CustomFieldManager extends Manager {
CustomButtonField first, second;
public CustomFieldManager() {
super(Manager.NO_HORIZONTAL_SCROLL | Manager.NO_VERTICAL_SCROLL);
}
protected void sublayout(int arg0, int arg1) {
int numberOfFields = getFieldCount();
for (int i = 0; i < numberOfFields; i++) {
// Get the field.
first = (CustomButtonField) getField(i);
setPositionChild(first, 0, 0);
layoutChild(first, 110, 110);
}
setExtent(200, 200);
}
setPositionChild(first, 50, 50);
invalidate();
}
}
If I understood you right, you just need to override this method:
protected boolean touchEvent(TouchEvent message)
in both custom field classes. And implement the functionality you want.
I think we need some clarification.
A Field in BlackBerry Java is given an 'extent' - basically a rectangle - and it paints into this area. When displayed on the screen, all 'events' in this Field's extent should be directed to that Field automatically by the framework. As far as the Field is concerned, the extent always starts at 0,0, and extends as many pixels as the Field has. In fact it is this extent that defines the size of the Field.
Now within paint, you can paint what you like for the each Field (as long as you stay inside its extent). So if you have a Field with an extent that was say 100 x 100 pixels, then you paint the left hand side of your Field red and the right hand side blue, by doing something like this:
graphics.setColor(Color.Red);
graphics.drawRect(0,0,50,100);
graphics.setColor(Color.Blue);
graohics.drawRect(50,0,100,100);
Now if you did this, you might want separately detectable areas - say the red and the blue areas. And while this is possible and you can detect it using touchEvent(), you would find it a lot easier to actually display two Fields, one all red and one all blue. Separate Fields make it a lot easier to use the standard BB methods, and in addition, also make it possible to use the trackpad to scroll round the different Fields. Remember you can't use touchevent() on non touch phones.
Anyway, with this additional information, can you please clarify your question. Do you have one Field with different areas? If you have two different Fields, remember that you are painting within each Field's extent.
Hi i have implemented scrolling text horizontally through this link LabelField Marquee. But i have one problem,the text is scrolling quite good but its been over-written on the original text which was added.Can anyone have any idea how to cope from this problem? I also tried to refresh the view by invalidate() but of no use. i have added the screenshot of the problem which i am facing.
Any help would be appreciable.
Thank you.
I would suggest you to change paint method to next:
public void paint(Graphics graphics) {
currentText = this.getText();
if (currentChar < currentText.length()) {
currentText = currentText.substring(currentChar);
}
graphics.drawText(currentText, 0, 0, DrawStyle.ELLIPSIS, 200);
}
So don't call super.paint() in your paint.
I've rewritten (in a more simple way) the answer you linked. It works fine.
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.DrawStyle;
import java.util.Timer;
import java.util.TimerTask;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
public class MyScreen extends MainScreen {
public MyScreen() {
super();
MarqueeLabel testLabel2 = new MarqueeLabel("This is a long long " +
"long long long long long long long long long long long " +
"long long marquee", Field.FOCUSABLE);
add(testLabel2);
}
class MarqueeLabel extends LabelField {
// Here MarqueeLabel code from your SO linked example
}
}
I'm struggling with the BrowserField. I would like a screen with a BrowserField at the top, and one at the bottom. I would like to point each one to a URL containing an image (and maybe HTML) - kind of how one might see an AdMob advert.
So I want to define a custom size for the BrowserField. Can that be done?
At the moment, the BrowserField is taking up the whole screen. (* see update below)
I have tried to subclass it anonymously - but it is final.
Is this a possible use case, or should I look for a different solution to the problem altogether...?
Update:
I have placed the BrowserField within a VerticalFieldManager and sized that accordingly (as per Blackberry BrowserField does not fit to screen).
(re-edit: this was the right idea, but I had made a mistake in my code that made me think it was still broken. Accepted answer below provides perfect code to accomplish exactly what I was hoping for.)
I've layouted two BrowserFields. One is aligned to top, the other is aligned to bottom. Their height is defined by TOP_BROWSER_FIELD_HEIGHT and BOTTOM_BROWSER_FIELD_HEIGHT accordingly. In the middle there is another manager that consume all the available height that is left after layouting the BrowserFields.
import net.rim.device.api.browser.field2.BrowserField;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.component.ButtonField;
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.ui.decor.BackgroundFactory;
public class BrowserFieldScreen extends MainScreen implements FieldChangeListener {
private static final int TOP_BROWSER_FIELD_HEIGHT = Display.getHeight() / 3;
private static final int BOTTOM_BROWSER_FIELD_HEIGHT = Display.getHeight() / 3;
private BrowserField topBrowserField;
private BrowserField bottomBrowserField;
private VerticalFieldManager vfmCenter;
private ButtonField startButton;
public BrowserFieldScreen() {
super(NO_VERTICAL_SCROLL | NO_HORIZONTAL_SCROLL | USE_ALL_WIDTH | USE_ALL_HEIGHT);
final VerticalFieldManager topVfm = new VerticalFieldManager(USE_ALL_HEIGHT | USE_ALL_WIDTH);
topBrowserField = new BrowserField();
topVfm.add(topBrowserField);
final VerticalFieldManager bottomVfm = new VerticalFieldManager(USE_ALL_HEIGHT | USE_ALL_WIDTH);
bottomBrowserField = new BrowserField();
bottomVfm.add(bottomBrowserField);
vfmCenter = new VerticalFieldManager(USE_ALL_WIDTH);
vfmCenter.setBackground(BackgroundFactory.createSolidBackground(Color.GRAY));
HorizontalFieldManager hfmCenter = new HorizontalFieldManager(USE_ALL_HEIGHT | FIELD_HCENTER);
startButton = new ButtonField("START", FIELD_VCENTER);
startButton.setChangeListener(this);
hfmCenter.add(startButton);
vfmCenter.add(hfmCenter);
VerticalFieldManager vfm = new VerticalFieldManager() {
protected void sublayout(int maxWidth, int maxHeight) {
setExtent(maxWidth, maxHeight);
// layout BrowserFields first
layoutChild(topVfm, maxWidth, TOP_BROWSER_FIELD_HEIGHT);
layoutChild(bottomVfm, maxWidth, BOTTOM_BROWSER_FIELD_HEIGHT);
// layout center field manager last to make it occupy all the height left
int maxCenterHeight = maxHeight - (TOP_BROWSER_FIELD_HEIGHT + BOTTOM_BROWSER_FIELD_HEIGHT);
layoutChild(vfmCenter, maxWidth, maxCenterHeight);
int yPos = 0;
setPositionChild(topVfm, 0, yPos);
yPos += TOP_BROWSER_FIELD_HEIGHT;
setPositionChild(vfmCenter, 0, yPos);
yPos += vfmCenter.getHeight();
setPositionChild(bottomVfm, 0, yPos);
};
};
vfm.add(topVfm);
vfm.add(vfmCenter);
vfm.add(bottomVfm);
add(vfm);
}
public void fieldChanged(Field field, int context) {
if (field == startButton) {
topBrowserField.requestContent("http://www.google.com");
bottomBrowserField.requestContent("http://www.yahoo.com");
}
}
}
The result is
The below class extends labelfield but when I display a large amount text it does'nt wrap to a new line. The text just trails across the screen. When I use LabelField the text wraps. Do I need to update the paint method?
Thanks
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.component.LabelField;
public class FCLabelField extends LabelField {
private Object text;
private Font font;
private int colour;
private long style;
public FCLabelField(Object text, long style , Font font, int colour) {
super(text, style);
this.text = text;
this.font = font;
this.colour = colour;
}
protected void paint(Graphics graphics) {
graphics.setColor(colour);
graphics.setFont(font);
graphics.drawText(text.toString(), 0, 0, DrawStyle.HCENTER, getContentWidth());
}
}
This works -
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.component.LabelField;
public class FCLabelField extends LabelField {
private Object text;
private Font font;
private int colour;
private long style;
public FCLabelField(Object text, long style , Font font, int colour) {
super(text, style);
this.text = text;
this.colour = colour;
super.setFont(font);
}
protected void paint(Graphics graphics) {
graphics.setColor(this.colour);
super.paint(graphics);
}
}
In your first version you are overriding the paint method and not calling the superclass' paint method. In the second, you are, this allows the code in the base class to paint the text.
If you don't want to call the superclass' paint method, you have to change your paint method to calculate the extent of the string you're going to draw and to split it at the appropriate points, making multiple calls to drawText to draw each fragment separately at a different y location. That's what the paint method in LabelField does by default, so you need to emulate it.
When you do call the superclass paint method, the reason setting the font on the superclass works and setting the font in your paint method doesn't is because the superclass' paint method is calling setFont on the Graphics object, overwriting what you just did in your paint method.