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:
Related
My app displays some images that I created using Image.createImage(). In some cases, the images are completely blank, but only on iOS. The images work fine on Android. Also, I create several images using Image.createImage() and most of them work fine. I don't see any difference between those and these.
To reproduce, run the enclosed app on both Android and iOS. The app shows two images. The second one is taken from the bottom half of the first one. On Android, the images show up fine. On iOS, the images show up for a few seconds, then vanish. It turns out that they only show up while iOS is displaying the startup screen. Once it switches to the actual app, the images are blank, although they take up the same space. Further tests reveal that the images are the correct size but are filled with transparent pixels.
I should say that, in my actual application, the images scale with the size of the screen, and are colored according to a user preference, so I can't just load them from a resource.
(BTW Notice the change I made to the stop method. This is unrelated but worth mentioning.)
Here's the test case:
import com.codename1.ui.Component;
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.Dialog;
import com.codename1.ui.Graphics;
import com.codename1.ui.Image;
import com.codename1.ui.Label;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;
import com.codename1.io.Log;
import com.codename1.ui.Toolbar;
import java.util.Arrays;
/**
* This file was generated by Codename One for the purpose
* of building native mobile applications using Java.
*/
#SuppressWarnings("unused")
public class HalfImageBug {
private Form current;
private Resources theme;
public void init(Object context) {
theme = UIManager.initFirstTheme("/theme");
// Enable Toolbar on all Forms by default
Toolbar.setGlobalToolbar(true);
}
public void start() {
if (current != null) {
current.show();
return;
}
Form hi = new Form("Hi World", new BorderLayout());
hi.addComponent(BorderLayout.CENTER, makeComponent());
hi.show();
}
public void stop() {
current = Display.getInstance().getCurrent();
// This was originally if, but it should be while, in case there are multiple layers of dialogs.
while (current instanceof Dialog) {
((Dialog) current).dispose();
current = Display.getInstance().getCurrent();
}
}
public void destroy() {
}
private Component makeComponent() {
final Container container = new Container(new BoxLayout(BoxLayout.Y_AXIS));
container.setScrollableY(true);
container.add(new Label("Full Image:"));
Image fullIcon = createFullImage(0x44ff00, 40, 30);
Label fullImage = new Label(fullIcon);
container.add(fullImage);
container.add(new Label("---"));
container.add(new Label("Half Image:"));
Image halfIcon = createHalfSizeImage(fullIcon);
Label halfImage = new Label(halfIcon);
container.add(halfImage);
return container;
}
private Image createFullImage(int color, int verticalDiameter, int horizontalRadius) {
// Make sure it's an even number. Otherwise the half image will have its right and left halves reversed!
int diameter = (verticalDiameter / 2) * 2;
final int iconWidth = 2 * horizontalRadius;
int imageWidth = iconWidth + 2;
int imageHt = diameter + 2;
Image fullImage = Image.createImage(imageWidth, imageHt);
Graphics g = fullImage.getGraphics();
g.setAntiAliased(true);
g.setColor(color);
g.fillRect(0, 0, imageWidth, imageHt);
g.setColor(darken(color, 25));
g.fillArc(1, 1, iconWidth, diameter, 180, 360);
g.setColor(0xbfbfbf);
final int smallerHt = (9 * diameter) / 10;
g.fillArc(0, 0, iconWidth, smallerHt, 180, 360);
Image maskImage = Image.createImage(imageWidth, imageHt);
g = maskImage.getGraphics();
g.setAntiAliased(true);
g.setColor(0);
g.fillRect(0, 0, imageWidth, imageHt);
g.setColor(0xFF);
g.fillArc(1, 1, iconWidth, diameter, 180, 360);
fullImage = fullImage.applyMask(maskImage.createMask());
return fullImage;
}
private Image createHalfSizeImage(Image fullImage) {
int imageWidth = fullImage.getWidth();
int imageHt = fullImage.getHeight();
int[] rgbValues = fullImage.getRGB();
// yeah, I've since discovered a much more sensible way to do this, but it doesn't fix the bug.
int[] bottomHalf = Arrays.copyOfRange(rgbValues, rgbValues.length / 2, rgbValues.length);
//noinspection StringConcatenation
Log.p("Cutting side image from " + imageWidth + " x " + imageHt + " to " + imageWidth + " x " + (imageHt / 2));
return Image.createImage(bottomHalf, imageWidth, imageHt / 2);
}
private static int darken(int color, int percent) {
if ((percent > 100) || (percent < 0)) {
throw new IllegalArgumentException("Percent out of range: " + percent);
}
int percentRemaining = 100 - percent;
return (darkenPrimary((color & 0xFF0000) >> 16, percentRemaining) << 16)
| (darkenPrimary((color & 0xFF00) >> 8, percentRemaining) << 8)
| (darkenPrimary(color & 0xFF, percentRemaining));
}
private static int darkenPrimary(int primaryValue, int percentRemaining) {
if ((primaryValue < 0) || (primaryValue > 255)) {
throw new IllegalArgumentException("Primary value out of range (0-255): " + primaryValue);
}
return (primaryValue * percentRemaining) / 100;
}
}
This is discussed in this issue.
Generally the images initially appear because of the screenshot process that shows them so they never really show up on iOS natively.
A common cause for these issues is creating images off of the EDT which doesn't seem to be the issue in this specific code.
It's hard to see what is going on so I guess we'll need to evaluate the issue.
Here's a workaround. This works, but doesn't anti-alias very well. It will do until the iOS code gets fixed.
The problem is as described elsewhere. The Graphics.fillArc() and drawArc() methods work fine on Android, but often fail on iOS. Here's the behavior:
if width == height, they correctly draw a circle.
if width < height, they should draw an ellipse, but they draws a circle, centered over the intended ellipse, with a diameter equal to width.
if width > height, they draw nothing.
The workaround draws a circle against a transparent background, then draws that circle, squeezed in one direction to an ellipse, into the proper place. It doesn't do a very good job of anti-aliasing, so this is not a good substitute for working code, but it will do until the bug gets fixed. (This workaround handles fillArc, but it shouldn't be hard to modify it for drawArc()
/**
* Workaround for fillArc bug. Graphics.fillArc() works fine on android, but usually fails on iOS. There are three
* cases for its behavior.
*
* If width < height, it draws a circle with a diameter equal to width, and concentric with the intended ellipse.<br>
* If width > height, it draws nothing.<br>
* If width == height, it works correctly.
*
* To work around this we create a separate image, draw a circle, re-proportion it to the proper ellipse, then draw
* it to the Graphics object. It doesn't anti-alias very well.
*/
public static void fillArcWorkaround(Graphics masterG, int x, int y, int width, int height, int startAngle, int arcAngle) {
if (width == height) {
masterG.fillArc(x, y, width, height, startAngle, arcAngle);
} else {
int max = Math.max(width, height);
Image tempCircle = Image.createImage(max, max);
Graphics tempG = tempCircle.getGraphics();
tempG.setColor(masterG.getColor());
tempG.fillRect(0, 0, max, max);
// At this point tempCircle is just a colored rectangle. It becomes a circle when we apply the circle mask. The
// region outside the circle becomes transparent that way.
Image mask = Image.createImage(max, max);
tempG = mask.getGraphics();
tempG.setAntiAliased(masterG.isAntiAliased());
tempG.setColor(0);
tempG.fillRect(0, 0, max, max);
tempG.setColor(0xFF); // blue
tempG.fillArc(0, 0, max, max, startAngle, arcAngle);
tempCircle = tempCircle.applyMask(mask.createMask());
// Now tempCircle is a filled circle of the correct color. We now draw it in its intended proportions.
masterG.setAntiAliased(true);
masterG.drawImage(tempCircle, x, y, width, height);
}
}
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:
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
}
Ok, I'm developing for the blackberry Bold 9700 and I'm trying to get a 1X4 grid (1 row, 4 columns) to span the entire width of the blackberry screen, but it keeps coming up short. I mean, the grid is aligned to the left by default, which is fine if I can get the whole grid to span the entire width (it won't matter then). Can some developer tell me what I'm doing wrong? I thought you just add GridFieldManager.USE_ALL_WIDTH in the constructor when declaring a new grid but it still won't work for me.
final class App3_MainScreen extends MainScreen {
private int numColumns, size;
// Constructor
App3_MainScreen() {
// declare a layout manager to take care of all the layout stuff
numColumns = 4;
size = 4;
VerticalFieldManager vfm = new VerticalFieldManager();
vfm.add(new LabelField("using all width & long label...", LabelField.ELLIPSIS | Field.FIELD_HCENTER));
int borderHeight = Display.getHeight()/2;g
int borderWidth = Display.getWidth()/2;
Manager gridFieldManager = new GridFieldManager(1, 4, GridFieldManager.USE_ALL_WIDTH | GridFieldManager.AUTO_SIZE); // 1 row and 4 columns
gridFieldManager.add(new ButtonField(""+borderHeight, Field.FIELD_HCENTER));
gridFieldManager.add(new ButtonField("222", Field.FIELD_HCENTER));
gridFieldManager.add(new ButtonField("333", Field.FIELD_HCENTER));
gridFieldManager.add(new ButtonField(""+borderWidth, Field.FIELD_RIGHT));
// set padding around each buttonField - top=0, right=5, bottom=0, left=5
gridFieldManager.setPadding(0, 5, 0, 5);
int gfmHeight = 48 * (size / numColumns);
gridFieldManager.setBorder(BorderFactory.createSimpleBorder(
new XYEdges(borderHeight/10, 0, borderHeight/10, 0), // top, right, bottom, left
Border.STYLE_DASHED));
add(gridFieldManager);
}}
I've provided an example below that does the trick. It's based on the original code you provided, but is cleaned up and made generic for clarity.
Basically, GridFieldManager doesn't explicitly support USE_ALL_WIDTH. Being a Manager, it inherits this constant, but its documentation doesn't express that it is a supported state. Your best bet is to rely on the FIXED_SIZE state and calculate the width of each of your columns based on the size of the display (displayWidth / numColumns). Then you can use GridFieldManager#setColumnProperty() to define the fixed width for the columns.
Make sure to take into account the padding applied to the columns and you're good to go.
Hope this helps.
/**
* Shows an example implementation of how to have a GridFieldManager
* sized to the width of the Display.
*/
final class ScreenWidthGridExample extends MainScreen
{
/**
* Number of rows in the grid.
*/
private static final int NUM_ROWS = 1;
/**
* Number of columns in the grid.
*/
private static final int NUM_COLUMNS = 4;
/**
* The grid's column padding.
*/
private static final int COLUMN_PADDING = 5;
/**
* Toggle switch to show the border around the grid.
*/
private static final boolean SHOW_BORDER = true;
/**
* Allocated a new instance of the ScreenWidthGridExample.
*/
ScreenWidthGridExample() {
// Set up the GridFieldManager
GridFieldManager gfm =
new GridFieldManager(NUM_ROWS, NUM_COLUMNS,
GridFieldManager.FIXED_SIZE);
gfm.setColumnPadding(COLUMN_PADDING);
if(SHOW_BORDER) {
gfm.setBorder(BorderFactory.createSimpleBorder(
new XYEdges(0, 0, 0, 0), // top, right, bottom, left
Border.STYLE_DASHED));
}
add(gfm);
// Size the columns of the GridFieldManager. Make sure to calculate
// for the padding applied to the columns.
int columnWidth = (Display.getWidth() / NUM_COLUMNS) -
gfm.getColumnPadding();
for(int i = 0; i < NUM_COLUMNS; i++) {
gfm.setColumnProperty(i, GridFieldManager.FIXED_SIZE, columnWidth);
}
// Populate the columns.
gfm.add(new ButtonField("1", Field.FIELD_HCENTER));
gfm.add(new ButtonField("2", Field.FIELD_HCENTER));
gfm.add(new ButtonField("3", Field.FIELD_HCENTER));
gfm.add(new ButtonField("4", Field.FIELD_HCENTER));
}
}