Does Blackberry support subscript superscript? I found the BlackBerry Forum thread "Subscript and superscript in RichTextField", but I am not able to access the BlackBerry knowledge base article.
How can I implement superscript & subscript in a LabelField?
If there's a problem accessing the BlackBerry knowledge base (i.e. from some countries?), here is the content of that page (posted by #MSohm of RIM):
A RichTextField does not nativly support subscript, superscript or
multiple colours. Multiple fonts, font sizes, and font formatting
(for example, Bold, Italic, Underlined) are supported. The following
links explain this further.
How To - Format text in a RichTextField Article Number: DB-00124
http://supportforums.blackberry.com/t5/Java-Development/Format-text-in-a-RichTextField/ta-p/445038
How To - Change the text color of a field Article Number: DB-00114
http://supportforums.blackberry.com/t5/Java-Development/Change-the-text-color-of-a-field/ta-p/442951
If you still want to do this, you could try subclassing RichTextField, or LabelField, and overriding the paint() method. There, you could change the font size and shift the y coordinate of the text. It depends how generic you want to make the solution. Perhaps you could post more information about your problem?
But, as a very simple, hardcoded example, the following code will create a LabelField that prints out: "CO2"
private class SubscriptLabelField extends LabelField {
private int _subscriptTop = 0;
private int _subscriptFontSize = 0;
public SubscriptLabelField(Object text, long style) {
super(text, style);
setFont(getFont());
}
public void setFont(Font newFont) {
super.setFont(newFont);
// we use a subscript that's located at half the normal font's height,
// and is 2/3 as tall as the normal font
int h = newFont.getHeight();
_subscriptTop = h / 2;
_subscriptFontSize = 2 * h / 3;
super.invalidate();
}
protected void layout(int width, int height) {
super.layout(width, height);
// add more space at the bottom for the subscript
int w = getExtent().width;
int h = getExtent().height;
int extraHeight = _subscriptFontSize - (getFont().getHeight() - _subscriptTop);
setExtent(w, h + extraHeight);
}
public void paint(Graphics g) {
// here we hardcode this method to simply draw the last char
// as a "subscript"
String text = getText();
String normalText = text.substring(0, text.length() - 1);
g.drawText(normalText, 0, 0);
// how much space will the normal text take up, horizontally?
int advance = g.getFont().getAdvance(normalText);
// make the subscript a smaller font
Font oldFont = g.getFont();
Font subscript = getFont().derive(Font.PLAIN, _subscriptFontSize);
g.setFont(subscript);
String subscriptText = text.substring(text.length() - 1);
g.drawText(subscriptText, advance, _subscriptTop);
// reset changes to graphics object just to be safe
g.setFont(oldFont);
}
}
And then use it like this:
public SubscriptScreen() {
super(MainScreen.VERTICAL_SCROLL | MainScreen.VERTICAL_SCROLLBAR);
SubscriptLabelField textField = new SubscriptLabelField("C02", LabelField.NON_FOCUSABLE);
// TODO: this line is just to show the adjusted boundaries of the field -> remove!
textField.setBackground(BackgroundFactory.createSolidBackground(Color.LIGHTGRAY));
add(textField);
}
which gives:
Related
How do I draw a radial gradient button in BlackBerry? I found "Drawing Radial Gradients" on the BlackBerry support forums. All I am able to implement on my own is a linear gradient.
This is a little tricky. Drawing linear gradients on field backgrounds is easy. Drawing radial gradients on field backgrounds is harder. Doing it on a button is harder still.
First of all, the example you link to does indeed look really bad. The biggest problem with that code is that it uses Graphics.drawArc() to construct the gradient out of concentric circles (lines). This is not at all smooth.
The biggest improvement you need to make over that is to use Graphics.fillArc() instead, which will look much smoother (although there may be a performance impact to this ...).
Your question didn't say anything about how you wanted the button to look when focused, or whether the corners needed to be rounded. That's where some of the difficulty comes in.
If you just extend the RIM ButtonField class, you'll probably have trouble with the default drawing for focus, and edge effects. It's probably necessary to directly extend the base Field class in a new, written-from-scratch, button field. I wouldn't necessarily recommend that you do all this yourself, since buttons require focus handling, click handling, etc. You should probably start with something like the BaseButtonField from the BlackBerry AdvancedUI open source library.
I have prototyped this for you, using that class as a base. (so, you'll need to download and include that source file in your project if you use this).
I created a GradientButtonField subclass:
private class GradientButtonField extends BaseButtonField {
private int startR;
private int startG;
private int startB;
private int endR;
private int endG;
private int endB;
/** the maximum distance from the field's center, in pixels */
private double rMax = -1.0;
private int width;
private int height;
private String label;
private int fontColor;
/**
* Create a gradient button field
* #param startColor the integer Color code to use at the button center
* #param endColor the integer Color code to use at the button edges
* #param label the text to show on the button
* #param fontColor color for label text
*/
public GradientButtonField (int startColor, int endColor, String label, int fontColor) {
// record start and end color R/G/B components, to
// make intermediate math easier
startR = (startColor >> 16) & 0xFF;
startG = (startColor >> 8) & 0xFF;
startB = startColor & 0xFF;
endR = (endColor >> 16) & 0xFF;
endG = (endColor >> 8) & 0xFF;
endB = endColor & 0xFF;
this.label = label;
this.fontColor = fontColor;
}
public String getLabel() {
return label;
}
protected void layout(int w, int h) {
width = Math.min(Display.getWidth(), w);
height = Math.min(Display.getHeight(), h);
if (rMax < 0.0) {
rMax = Math.sqrt((width * width)/4.0 + (height * height)/4.0);
}
setExtent(width, height);
}
private int getColor(double scale, boolean highlighted) {
int r = (int)(scale * (endR - startR)) + startR;
int g = (int)(scale * (endG - startG)) + startG;
int b = (int)(scale * (endB - startB)) + startB;
if (highlighted) {
// just brighten the color up a bit
r = (int)Math.min(255, r * 1.5);
g = (int)Math.min(255, g * 1.5);
b = (int)Math.min(255, b * 1.5);
}
return (65536 * r + 256 * g + b);
}
protected void paint(Graphics graphics) {
int oldColor = graphics.getColor();
// we must loop from the outer edge, in, to draw
// concentric circles of decreasing radius, and
// changing color
for (int radius = (int)rMax; radius >= 0; radius--) {
double scale = ((double)radius) / rMax;
boolean focused = (getVisualState() == Field.VISUAL_STATE_FOCUS);
graphics.setColor(getColor(scale, focused));
int x = width / 2 - radius;
int y = height / 2 - radius;
graphics.fillArc(x, y, 2 * radius, 2 * radius, 0, 360);
}
String text = getLabel();
graphics.setColor(fontColor);
graphics.drawText(text,
(width - getFont().getAdvance(text)) / 2,
(height - getFont().getHeight()) / 2);
// reset graphics object
graphics.setColor(oldColor);
}
}
To use this, the Manager that contains the button will need to constrain the button's size in its sublayout() implementation. Or, you can edit my GradientButtonField class to hardcode a certain size (via getPreferredWidth(), layout(), etc.), or whatever you want.
final Field button1 = new GradientButtonField(Color.DARKGRAY, Color.BLUE,
"Click Me!", Color.WHITE);
final Field button2 = new GradientButtonField(Color.DARKGRAY, Color.BLUE,
"Click Me, Too!", Color.WHITE);
Manager mgr = new Manager(Manager.NO_VERTICAL_SCROLL) {
public int getPreferredHeight() {
return Display.getHeight();
}
public int getPreferredWidth() {
return Display.getWidth();
}
protected void sublayout(int maxWidth, int maxHeight) {
setExtent(getPreferredWidth(), getPreferredHeight());
layoutChild(button1, 160, 80);
setPositionChild(button1, 20, 50);
layoutChild(button2, 120, 60);
setPositionChild(button2, 20, 150);
}
};
button1.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
Dialog.alert("clicked!");
}
});
mgr.add(button1);
mgr.add(button2);
add(mgr);
I did not round the corners, as that's a bit of work. Depending on what kind of backgrounds you're putting these buttons on, it might be easiest to create a PNG mask image (in your favorite drawing program), which is mostly transparent, and then just has filled corners that mask off the corners of the gradient below it. Then, use Graphics.drawBitmap() in the paint() method above, after you've drawn the radial gradient.
For focus highlighting, I just put in some simple code to brighten the colors when the button is focused. Again, you didn't say what you wanted for that, so I just did something simple.
Here's the result of the code above. The bottom button is focused:
I have HorizontalFieldManager,VerticalFieldManager and LabelField. LabelField placed in HorizontalFieldManager and multiple HorizontalFieldManager are placed in VerticalFieldManager.
When i try to get LabelField height using labelfield.getHeight(); it returns 0 . if there are multiple line in Labelfield, it also give me height returns 0. same issue i m facing for HorizontalFieldManager .
After getting there height i want to calculate VerticalFieldManager height and set height dynamically for the screen.
How can i calculate the height of Label or Horizontalfieldmanager?
Use labelField.getPreferredHeight() and manager.getPreferredHeight() not labelField.getHeight()
This should work for you
The method Farid suggests it's a bit difficult to use, because you will need labelWidth.
For multiline label this labelWidth may not be the same as parent managers available width, or some exact width you had set, because each line can have different width depending on if words did fit maxWidth or didn't.
NOTE: as Nate pointed out in comments, it's a good idea to also add advance for spaces.
I modified the code to include that.
Here is the method I use to overcome these problems:
public int getPreferredHeight() {
String text = getText();
int maxWidth = getManager().getPreferredWidth();
int spaceAdvance = getFont().getAdvance(' ');
String[] words = StringUtilities.stringToWords(text);
int lastWordAdvance = 0;
int lines = 1;
for (int i = 0; i < words.length; i++) {
int wordAdvance = getFont().getAdvance(words[i]) + spaceAdvance;
if (lastWordAdvance + wordAdvance < maxWidth ) {
lastWordAdvance += wordAdvance;
} else {
lines++;
lastWordAdvance = wordAdvance;
}
}
return (int)lines * getFont().getHeight();
}
If you can leave the decision about the size of the LabelField until after it has been laid out, then you will get it accurately from getHeight(). This means you can actually get the correct result, including factoring in any margin or padding for the LabelField, that I think
int maxWidth = getManager().getPreferredWidth();
will miss.
Initially this seems quite difficult, because typically it is good to know the height when you add the Field to the screen. But the general principle is that you should do this in the Manager's sublayout, so you are just moving the code that is dependent on the height, a bit later in the process. And this has the benefit that the layout is dynamic, so if the LabelField's text is changed, then layout will be invoked and your code that is dependent on the height gets re-invoked too.
It is also possible to use logic like this in sublayout():
super.sublayout(...);
if (myField.getHeight() < 100 ) {
myField.setMargin((100 - myField.getHeight())/2, 0, (100 - myField.getHeight())/2, 0);
super.sublayout(...);
}
This is not a production suitable example, hard coding a pixel height is not recommended. It is just an easy example to understand....
I am facing issue while displaying multiple columns in a row. I need multiple columns and multiple row list field. Now I am trying to make this using label field i one of my case its working quite good but in another case I am facing an issue please help me out. My code is:
VerticalFieldManager TOrderVFM = new VerticalFieldManager()
for ( int i = 0; i <10; i++)
{
HorizontalFieldManager temphfm1 = new HorizontalFieldManager(){
protected void sublayout(int width, int height)
{
int w = 480;
int h = 400;
super.sublayout(w, h);
super.setExtent(w, h);
}
};
TOrderVFM.add(temphfm1);
temphfm1.add(createDayName1(MTradeOrderSoap.objects[i].getProperty("orderDate").toString()));
temphfm1.add(createDayName1(MTradeOrderSoap.objects[i].getProperty("id").toString()));
temphfm1.add(.createDayName1(MTradeOrderSoap.objects[i].getProperty("label").toString()));
temphfm1.add(createDayName1(MTradeOrderSoap.objects[i].getProperty("quantityPending").toString()));
temphfm1.add(createDayName1(MTradeOrderSoap.objects[i].getProperty("securityName").toString()));
temphfm1.add(createDayName1(MTradeOrderSoap.objects[i].getProperty("priceType").toString()));
temphfm1.add(createDayName1(MTradeOrderSoap.objects[i].getProperty("orderOrigin").toString()));
temphfm1.add(ut.createDayName1(MTradeOrderSoap.objects[i].getProperty("orderStatus").toString()));
}
This loop is inserting values that are coming from the soap response and passing it to the method named createDayName() which is also given below.Now this all works good for my one of the screens but when i try to follow this for my another screen i am facing an error:-WARNING: Cannot layout field, insufficient height or width
I have set the width and height of both the managers but nothing seems to be working .Please provide me a support to do that.
public LabelField createDayName1(final String day)
{
LabelField cell = new LabelField("", Field.NON_FOCUSABLE) {
protected void layout(int width, int height)
{
int w = Display.getWidth()/7;
int h = 40;
super.layout(w, h);
super.setExtent(w, h);
}
protected void paint(Graphics g)
{
g.setColor(0xFF9912);
g.setFont(dayNameFont);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLACK);
// g.setColor(0x466385);
g.drawText(day.trim(), getWidth() / 2 - dayNameFont.getAdvance(day) / 3, getHeight() / 3 - dayNameFont.getHeight() / 2);
super.paint(g);
}
};
return cell;
}
In layout() and sublayout() you need to make sure you're comparing the width and height you are passing to setExtent() (and super.layout()/super.sublayout() for that matter) to the arguments getting sent to those methods because they are the maximum available width and height. If you try to tell the Field to be wider or taller (using setExtent()) than what is available, it won't be able to display properly.
Below code defines a horizontal field manager with two fields. How can I amend the code so that the background is just set on the two fields being added not on the whole manager. Note, im not attempting to add an individual background image to each of the fields, instead a shared background image that spans behind the two fields.
LabelField label = new LabelField("name");
TextField e = new TextField(Field.FOCUSABLE);
final Bitmap b = Constants.SETTINGS;
final Background bg = BackgroundFactory.createBitmapBackground(Constants.SETTINGS);
HorizontalFieldManager manager = new HorizontalFieldManager()
{
public void sublayout (int width, int height)
{
Field field;
int x = 0;
super.sublayout(b.getWidth(), height);
super.setExtent(b.getWidth(), height);
for (int i = 0; i < getFieldCount(); i++)
{
field = getField(i);
layoutChild(field, Display.getWidth()/2, height);
setPositionChild(field, x, 10);
x += Display.getWidth()/2;
}
}
};
manager.add (label);
manager.add (e);
add (manager);
Rather than putting them in a custom Manager, it may be easier to just override the Fields' layout() calls to be
protected void layout(int width, int height) {
super.layout(width, height);
setExtent(Display.getWidth()/2, this.getHeight());
}
and then you can just use a normal HorizontalFieldManager you can set a background on and a padding (hfm.setPadding(10, 10, 10, 10);). Adding a padding will reduce the available width for your Fields, so you should decrease their widths in the layout() calls.
You can offset each of their individual backgrounds with some fancy, expensive Bitmap footwork (math) to appear to "share" one image using setBackGround(), or you can override their draw methods to achieve the same effect with the ability to "move" across the bitmap according to their relative position...
That what you're after? :)
edit:
create a custom field to use your bitmap and feed it whatever content you would like, then override the paint to draw what you like where you like it...
protected void paint(Graphics g){
// conditionals, etc
g.drawBitmap(x, y, width, height, bitmap, left, top);
// color changes, etc
g.drawText(yourText);
// clean up
}
For the sake of this question, let us suppose that I want a row of buttons. I want to put as many buttons in that row as I can fit on the screen, but no more. In other words, as long as a prospective button will not be cut off or have its text shortened, add it.
It seems that I should be able to do something like:
HorizontalFieldManager hfm = new HorizontalFieldManager();
int remainingWidth = Display.getWidth();
int i =0;
while(true) {
ButtonField bf = new ButtonField("B " + i);
remainingWidth -= bf.getWidth();
if(remainingWidth<0)
break;
hfm.add(bf);
i++;
}
add(hfm);
But this doesn't work. bf.getWidth() is always 0. I suspect that this is because the button has not yet been laid out when I query for the width.
So, perhaps I could just make sure the buttons are always the same size. But this won't work for a few reasons:
Different BB platforms have different looks for buttons and text that will fit on a button on a Curve won't fit on a button on a Storm.
3rd party themes may change the look of buttons, so I can't even count on buttons being a certain size on a certain platform.
Is there no way for me to actually check the remaining space before adding a button? It feels like a fairly useful feature; am I just missing something?
Try to draw the Manager before adding component to it.
You should probably add the padding too, if the button has one.
public class Main extends UiApplication{
public static void main(String[] args) {
Main main = new Main();
main.enterEventDispatcher();
}
public Main() {
Screen main = new Screen();
this.pushScreen(main);
main.init();
}
}
public class Screen extends MainScreen {
HorizontalFieldManager hfm;
public Screen() {
hfm = new HorizontalFieldManager();
this.add(hfm);
}
public void init() {
int remainingWidth = Display.getWidth();
int i = 0;
while(true) {
ButtonField bf = new ButtonField("B " + i);
hfm.add(bf);
remainingWidth -= bf.getWidth();
System.out.println(remainingWidth);
if(remainingWidth <= 0) {
hfm.delete(bf);
break;
}
i++;
}
}
You're correct that getWidth() returns the width of the field after it's been laid out (that's why it's 0), but getPreferredWidth() will return the value that's given to the Layout Manager, and is probably what you want.
The other problem you have is that you're comparing remainingWidth to 0. If a button's preferred width is, say, 36 pixels, and the remaining width is 20 pixels, you'll draw a button in a spot where you don't have enough room to display it.
Try something like this:
HorizontalFieldManager hfm = new HorizontalFieldManager();
int remainingWidth = Display.getWidth();
for (int i; true; i++ ) {
ButtonField bf = new ButtonField("B " + i);
int preferredWidth = bf.getPreferredWidth();
if ( remainingWidth < preferredWidth )
break;
remainingWidth -= preferredWidth;
hfm.add(bf);
}
add(hfm);
Try to make your own HorizontalFiledManager (extends Manager) and override
protected void sublayout( int maxWidth, int maxHeight ){
setExtent(maxWidth,maxHeight);
}
Get the number of childs, and with a for loop go trough and start to lay out the childs (setPosition, layoutChild).
When you lay out the child, count his width and always check if the counted width + the next child`s width is greater then maxWidth then do not lay out and break the for loop.
Hopefully this help you.