Flutter: scaling of large canvas drops FPS - performance

I created a CustomPainter which paints large canvas of volume 10000 elements.
A CustomPaint containing this painter is wrapped in a RepaintBoundary.
Problem: When I use Transform.scale of the parent widget, fps slowing down is observed.
For scaling used this library: gesture_zoom_box
Code snippet below:
import 'package:flutter/material.dart';
import 'package:gesture_zoom_box/gesture_zoom_box.dart';
import 'dart:math';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: MyWidget(),
);
}
}
Map _generateData() {
final Map classes = {
"0": {"color": "#BF1C1C", "marker": "X"},
"1": {"color": "#F06D06", "marker": "Y"},
"2": {"color": "#B742C9", "marker": "Z"}
};
final rows = [];
final Random random = Random();
final List keys = List.from(classes.keys);
final listC = new List<int>.generate(100, (i) => i + 1);
final listR = new List<int>.generate(100, (i) => i + 1);
for (final _ in listR) {
final List row = [];
for (final _ in listC) {
row.add({"type": "box", "cls": keys[random.nextInt(keys.length)]});
}
rows.add(row);
}
final Map data = {
"map": {
"rows": rows
},
"cls": classes
};
return data;
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext ctx) {
final Map data = _generateData();
return Scaffold(
appBar: AppBar(
title: Text("MyApp")
),
body: GestureZoomBox(
child: LayoutBuilder(
builder: (ctx, constraints) {
final int rowsNum = data["map"]["rows"].length;
final int colsNum = data["map"]["rows"][0].length;
final double boxSide = min(constraints.maxWidth/colsNum, constraints.maxHeight/rowsNum);
final double width = boxSide * colsNum;
final double height = boxSide * rowsNum;
return Center(
child: Container(
width: width,
height: height,
child: RepaintBoundary(
child: CustomPaint(
painter: PixelGridPainter(data: data)
),
),
),
);
}
)
)
);
}
}
class PixelGridPainter extends CustomPainter {
final Map data;
PixelGridPainter({this.data});
#override
void paint(Canvas canvas, Size size) {
final rows = data['map']['rows'];
final cls = data['cls'];
final double boxSide = min(size.width/rows[0].length, size.height/rows.length);
final Paint paint = Paint();
paint.style = PaintingStyle.fill;
var i = 0;
for (final row in rows) {
final rowOffset = boxSide * i;
var j = 0;
for (final elem in row) {
final columnOffset = j * boxSide;
final Map elemCls = cls[elem['cls']];
final Color c = Color(int.parse(elemCls["color"].substring(1, 7), radix: 16) + 0xFF000000);
paint.color = c;
final rect = Rect.fromLTRB(columnOffset, rowOffset, columnOffset + boxSide, rowOffset + boxSide);
canvas.drawRect(
rect,
paint,
);
j += 1;
}
i += 1;
}
}
#override
bool shouldRepaint(PixelGridPainter old) {
return true;
}
}
I tried PictureRecorder to draw only a picture and got the necessary 60fps, but I need quick interaction with this widget, which is impossible with such a hack without creating images cache.
Does anyone know what the problem is?

Related

Flutter set start animation value

I want to pass in a value to set the starting animation value to 1.0 instead of starting from 0, but I don't know how this is achieved.
class AnimatedIconButton extends StatefulWidget {
AnimatedIconButton(this.img, this.start);
final String img;
final double start;
#override
_AnimatedIconButtonState createState() => _AnimatedIconButtonState();
}
class _AnimatedIconButtonState extends State<AnimatedIconButton>
with TickerProviderStateMixin {
AnimationController _controller;
Animation _animation;
void animateButton() {
if (_animation.value == 0)
_controller.forward();
else
_controller.reverse();
}
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(milliseconds: 300),
vsync: this,
);
_animation =
CurvedAnimation(parent: _controller, curve: Curves.easeOutQuad);
_controller.addListener(() {
setState(() {});
});
_animation.value = widget.start;// does not work
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialButton(
splashColor: Colors.white,
highlightColor: Colors.white,
child: Image.asset("images/${widget.img}.png",
width: 33 + (16 * _animation.value)),
onPressed: () {
animateButton();
},
);
}
}
You can use:
_controller.forward(from: 0.0);
or
_controller.reverse(from: 1.0);
And set value you need
Or for setting initial value you can use
_controller.value = widget.start;
Inside your code:
add _controller.forward(1.0);.
Here you can pass the value you want.
Like below:
void animateButton() {
if (_animation.value == 0)
_controller.forward(from: 1.0);
else
_controller.reverse(from: 1.0);
}
as i understand you want to make animation starts from 1.0 instead of starting from 0.0
so here we are ..
Make global variables like:-
Animation<double> animation;
AnimationController controller;
Tween<double> tween = Tween(begin: 0, end: 100);
Override initState method:-
#override
void initState() {
super.initState();
controller=AnimationController(vsync: this, duration: Duration(seconds: 2));
animation = tween.animate(controller);
}
finally you can change start & end animation values by this :-
void accessToStartOfAnimation() {
print(tween.begin);
tween.begin = 1;
print(tween.begin);
}
If you want to use an animation where forward() will drive the value in reverse, i.e. 1.0 → 0.0, the simplest solution is to use ReverseAnimation:
#override
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 500));
_hideAnimation = ReverseAnimation(
CurvedAnimation(
parent: _animationController,
curve: Curves.fastOutSlowIn,
),
),
}

how can i make the unity gui window dragable

i am trying to make my window dragable
i'm currently making a multiplayer game and the status to be shown has this window
public bool finishSession = false;
public bool showHelp = false;
private ArrayList messages = new ArrayList();
private string currentTime = "";
private string newMessage = "";
private Vector2 windowScrollPosition;
private SmartFox smartFox;
private GUIStyle windowStyle;
private GUIStyle userEventStyle;
private GUIStyle systemStyle;
public Rect rctWindow;
public float windowPanelPosX;
public float windowPanelPosY;
public float windowPanelWidth;
public float windowPanelHeight;
public StatusWindow() {
smartFox = SmartFoxConnection.Connection;
}
public void AddSystemMessage(string message) {
messages.Add(new StatusMessage(StatusMessage.StatusType.SYSTEM, message));
windowScrollPosition.y = 100000;
}
public void AddStatusMessage(string message) {
messages.Add(new StatusMessage(StatusMessage.StatusType.STATUS, message));
windowScrollPosition.y = 100000;
}
public void AddTimeMessage(string message) {
//messages.Add(new StatusMessage(StatusMessage.StatusType.TIME, message));
//windowScrollPosition.y = 100000;
currentTime = message;
}
public void Draw(float panelPosX, float panelPosY, float panelWidth, float panelHeight) {
windowPanelPosX = panelPosX;
windowPanelPosY = panelPosY;
windowPanelWidth = panelWidth;
windowPanelHeight = panelHeight;
// Status history panel
rctWindow = new Rect(windowPanelPosX, windowPanelPosY, windowPanelWidth, windowPanelHeight);
rctWindow = GUI.Window (1, rctWindow, DoMyWindow, "Interreality Portal Status", GUI.skin.GetStyle("window"));
GUI.DragWindow();
}
void DoMyWindow(int windowID)
{
windowStyle = GUI.skin.GetStyle("windowStyle");
systemStyle = GUI.skin.GetStyle("systemStyle");
userEventStyle = GUI.skin.GetStyle("userEventStyle");
//Cuadro blanco
GUILayout.BeginArea (new Rect (10, 25, windowPanelWidth - 20, windowPanelHeight - 70), GUI.skin.GetStyle ("whiteBox"));
GUILayout.BeginVertical ();
//General information area
if (smartFox != null && smartFox.LastJoinedRoom != null) {
GUILayout.Label ("Current room: " + smartFox.LastJoinedRoom.Name);
//if (currentGameState == GameState.RUNNING ) {
//GUILayout.Label(trisGameInstance.GetGameStatus()); //ACPR
//}
}
GUILayout.Label ("Activity: 1 - Construct");
GUILayout.Label ("Elapsed time: " + currentTime);
//Message area
windowScrollPosition = GUILayout.BeginScrollView (windowScrollPosition);
foreach (StatusMessage message in messages) {
DrawStatusMessage (message);
}
GUILayout.EndScrollView ();
//Cierra cuadro blanco
GUILayout.EndVertical ();
GUILayout.EndArea ();
//Logout area
GUILayout.BeginArea (new Rect (windowPanelWidth / 2, windowPanelHeight - 70 + 30, windowPanelWidth / 2 + 10, 30));//, GUI.skin.GetStyle("whiteBox"));
GUILayout.BeginHorizontal ();
if (GUILayout.Button ("Help", GUI.skin.GetStyle ("greenBtn"))) {
showHelp = true;
}
GUILayout.Space (10);
if (GUILayout.Button ("End Session", GUI.skin.GetStyle ("redBtn"))) {
finishSession = true;
}
GUILayout.EndHorizontal ();
GUILayout.EndArea ();
GUI.DragWindow();
}
private void DrawStatusMessage(StatusMessage message) {
GUILayout.BeginHorizontal();
GUILayout.Space(5);
switch (message.GetStatusType()) {
case StatusMessage.StatusType.SYSTEM:
GUILayout.Label(message.GetMessage(), systemStyle);
break;
case StatusMessage.StatusType.STATUS:
GUILayout.Label(message.GetMessage(), windowStyle);
break;
case StatusMessage.StatusType.TIME:
GUILayout.Label(message.GetMessage(), userEventStyle);
break;
default:
// Ignore and dont print anything
break;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.Space(1);
GUI.DragWindow();
}
class StatusMessage {
public enum StatusType {
IGNORE = 0,
SYSTEM,
STATUS,
TIME,
};
private StatusType type;
private string message;
public StatusMessage() {
type = StatusType.IGNORE;
message = "";
}
public StatusMessage(StatusType type, string message) {
this.type = type;
this.message = message;
}
public StatusType GetStatusType() {
return type;
}
public string GetMessage() {
return message;
}
}
}
but when i'mm trying to drag the window it doesn't drag it
i tried a simpler class with jnothing that works fine but when i call this window it doesn't drag
StatusWindow statusWindow = null;
void Start(){
statusWindow = new StatusWindow();
}
public Rect windowRect = new Rect(20, 20, 120, 50);
void OnGUI() {
windowRect = GUI.Window(0, windowRect, DoMyWindow, "My Window");
statusWindow.Draw (100, 100, 100, 100);
}
void DoMyWindow(int windowID) {
GUI.Button(new Rect(10, 20, 100, 20), "Can't drag me");
GUI.DragWindow();
}
If anyone who knows much about Unity GUI can help that would be great

JavaFX Animation: Move Shape (Star) randomly

My problem is, that after every keyframe the x- and y-Position of the rectangle should change by random.
Right now only when I start the program, the rectangles position is set by random, but not in the animation itself.
How can I do this, thanks a lot...
public class TimeLines extends Application {
private Rectangle rectBasicTimeline;
private Timeline timeline;
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Do Animation");
int x = new Random().nextInt(500);
int y = new Random().nextInt(400);
rectBasicTimeline = new Rectangle(x, y, 100, 50);
rectBasicTimeline.setFill(Color.RED);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
final Timeline timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.setAutoReverse(true);
final KeyValue kx = new KeyValue(rectBasicTimeline.xProperty(), x + 200);
final KeyValue ky = new KeyValue(rectBasicTimeline.yProperty(), y + 200);
final KeyValue kScale = new KeyValue(rectBasicTimeline.scaleXProperty(), 2);
final KeyValue kFade = new KeyValue(rectBasicTimeline.opacityProperty(), 0);
final KeyFrame kf = new KeyFrame(Duration.millis(3000), kx, ky, kScale, kFade);
timeline.getKeyFrames().add(kf);
timeline.play();
}
});
AnchorPane root = new AnchorPane();
root.getChildren().addAll(btn, rectBasicTimeline);
Scene scene = new Scene(root, 800, 600);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
I agree with #Roland, no need to create multiple Timeline. Reclicking the button just restarts the timeline with new KeyFrame:
public class StarFall extends Application
{
private Polygon star;
private Timeline timeline;
private final double shs = 5.0; // Star Hand Size
private final Random random = new Random();
#Override
public void start( Stage primaryStage )
{
// init shape
Pos initPos = getRandomPos();
star = new Polygon();
star.setLayoutX( initPos.x );
star.setLayoutY( initPos.y );
star.setFill( Color.YELLOW );
// the shape
star.getPoints().addAll( new Double[]
{
0.0, shs * 3,
shs * 2, shs * 2,
shs * 3, 0.0,
shs * 4, shs * 2,
shs * 6, shs * 3,
shs * 4, shs * 4,
shs * 3, shs * 6,
shs * 2, shs * 4
} );
// init timeline
timeline = new Timeline();
timeline.setCycleCount( Timeline.INDEFINITE );
timeline.setAutoReverse( true );
// init button
Button btnStart = new Button( "Do Animation" );
btnStart.setOnAction( ( e ) -> playNextKeyFrame() );
Button btnStop = new Button( "Stop Animation" );
btnStop.setLayoutX( 200 );
btnStart.setLayoutX( 0 );
btnStop.setOnAction( ( e ) -> timeline.stop() );
// init scene with root
AnchorPane root = new AnchorPane( btnStart, btnStop, star );
Scene scene = new Scene( root, 800, 600 );
// show
primaryStage.setScene( scene );
primaryStage.show();
}
private void playNextKeyFrame()
{
// generate next random start and end positions for star
Pos startPos = getRandomPos();
Pos endPos = getRandomPos();
// initial values (resetting)
star.setLayoutX( startPos.x );
star.setLayoutY( startPos.y );
star.setScaleX( 1 );
star.setScaleY( 1 );
star.setOpacity( 1 );
// target values
KeyValue kx = new KeyValue( star.layoutXProperty(), endPos.x );
KeyValue ky = new KeyValue( star.layoutYProperty(), endPos.y );
KeyValue kScaleX = new KeyValue( star.scaleXProperty(), 3 );
KeyValue kScaleY = new KeyValue( star.scaleYProperty(), 3 );
KeyValue kFade = new KeyValue( star.opacityProperty(), 0.0 );
// delay animation before start. Use this instead of THread.sleep() !!
timeline.setDelay( Duration.millis( random.nextInt( 2000 ) + 100 ) );
// restart timeline with new values
timeline.stop();
timeline.getKeyFrames().clear();
timeline.getKeyFrames().add( new KeyFrame( Duration.millis( 3000 ),
( e ) -> playNextKeyFrame(), kx, ky, kFade, kScaleX, kScaleY ) );
timeline.play();
}
private Pos getRandomPos()
{
int x = random.nextInt( 500 );
int y = random.nextInt( 400 );
Pos p = new Pos();
p.x = x + 200;
p.y = y + 200;
return p;
}
private class Pos
{
int x;
int y;
}
public static void main( String[] args )
{
launch( args );
}
}
You initialize the random numbers and hence the location once and use it all the time. What you need to do is to perform the animation cycle once and then create a new animation when the current one finishes.
Example:
import java.util.Random;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TimeLines extends Application {
private Rectangle rectBasicTimeline;
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Do Animation");
int x = new Random().nextInt(500);
int y = new Random().nextInt(400);
rectBasicTimeline = new Rectangle(x, y, 100, 50);
rectBasicTimeline.setFill(Color.RED);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
play();
}
});
AnchorPane root = new AnchorPane();
root.getChildren().addAll(btn, rectBasicTimeline);
Scene scene = new Scene(root, 800, 600);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void play() {
double x = new Random().nextInt(500);
double y = new Random().nextInt(400);
final Timeline timeline = new Timeline();
// cycle count = 2 because of autoreverse
timeline.setCycleCount(2);
timeline.setAutoReverse(true);
final KeyValue kx = new KeyValue(rectBasicTimeline.xProperty(), x + 200);
final KeyValue ky = new KeyValue(rectBasicTimeline.yProperty(), y + 200);
final KeyValue kScale = new KeyValue(rectBasicTimeline.scaleXProperty(), 2);
final KeyValue kFade = new KeyValue(rectBasicTimeline.opacityProperty(), 0);
final KeyFrame kf = new KeyFrame(Duration.millis(1000), kx, ky, kScale, kFade);
timeline.getKeyFrames().add(kf);
timeline.setOnFinished(e -> {
// create new animation after this animation finishes
play();
});
timeline.play();
}
/**
* #param args
* the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
I wouldn't suggest this approach though, e. g. you run into problems with multiple timelines when you click the button multiple times. But I have no information what you're trying to do, so I'll leave it at that.

GWT - Fading in/out a background image

I have a custom class as follows which works fine, the button grows/shrinks to accomodate the text and the bg image changes on a click.
Probem I want to solve is how to "fadeIN" one or other image when clicked/notClicked is called
Here is my code
public ExpandingOvalButton(String text) {
if (text.length() > 15) {
label.getElement().getStyle().setFontSize(20, Unit.PX);
} else {
label.getElement().getStyle().setFontSize(30, Unit.PX);
}
int width = 120;
initWidget(panel);
label.setText(text);
// width = width + (text.length() * 8);
String widthStr = width + "px";
image.setWidth(widthStr);
image.setHeight("100px");
button = new PushButton(image);
button.setWidth(widthStr);
button.setHeight("50px");
panel.add(button, 0, 0);
panel.add(label, 18, 14);
}
public void isClicked()
{
image.setUrl("images/rectangle_green.png");
}
public void unClicked()
{
image.setUrl("images/rectangle_blue.png");
}
#Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
public void setButtonEnabled(boolean enabled) {
// panel.setVisible(enabled);
// this.label.setVisible(enabled);
this.button.setVisible(enabled);
}
Here's a general utility class to fade any element:
public class ElementFader {
private int stepCount;
public ElementFader() {
this.stepCount = 0;
}
private void incrementStep() {
stepCount++;
}
private int getStepCount() {
return stepCount;
}
public void fade(final Element element, final float startOpacity, final float endOpacity, int totalTimeMillis) {
final int numberOfSteps = 30;
int stepLengthMillis = totalTimeMillis / numberOfSteps;
stepCount = 0;
final float deltaOpacity = (float) (endOpacity - startOpacity) / numberOfSteps;
Timer timer = new Timer() {
#Override
public void run() {
float opacity = startOpacity + (getStepCount() * deltaOpacity);
DOM.setStyleAttribute(element, "opacity", Float.toString(opacity));
incrementStep();
if (getStepCount() == numberOfSteps) {
DOM.setStyleAttribute(element, "opacity", Float.toString(endOpacity));
this.cancel();
}
}
};
timer.scheduleRepeating(stepLengthMillis);
}
}
Calling code for instance:
new ElementFader().fade(image.getElement(), 0, 1, 1000); // one-second fade-in
new ElementFader().fade(image.getElement(), 1, 0, 1000); // one-second fade-out
You could use GwtQuery. It provides fadeIn & fadeOut effects (and many other JQuery goodies), it is cross-browser compatible and seems to be pretty active.

BlackBerry - Custom menu toolbar

I'm a beginner in BlackBerry programming, I need to replace in my application the default menu (when you press the menu button) by a custom menu, horizontal. The best to describe is I want the same result as the WeatherEye application for BlackBerry...
alt text http://www.blackberrybing.com/resource/pics/201002/WeatherEye-OS-45.jpg
I know how to create the default menu, but this one I have no idea!
Thank you,
What you will need to do is:
create SizebleVFManager (contentManager) as an extension of VerticalFieldManager
set display width and height = (display height - menu height) size to contentManager
add contentManager to screen
create HorizontalFieldManager (menuManager)
create BitmapButtonField (menuButton) as an extension of ButtonField
set FieldChangeListeners to menuButtons
add menuButtons to menuManager
add menuManager to screen
Sample of SizebleVFManager :
class SizebleVFManager extends VerticalFieldManager
{
int mWidth = 0;
int mHeight = 0;
public SizebleVFM(int width, int height, long style) {
super(style);
mWidth = width;
mHeight = height;
}
public SizebleVFM(int width, int height) {
mWidth = width;
mHeight = height;
}
public int getPreferredWidth() {
return mWidth;
}
public int getPreferredHeight() {
return mHeight;
}
protected void sublayout(int width, int height) {
width = getPreferredWidth();
height = getPreferredHeight();
super.sublayout(width, height);
setExtent(width, height);
}
}
...
SizebleVFManager contentManager =
new SizebleVFManager(Display.getWidth(), Display.getHeight(),
VERTICAL_SCROLL|VERTICAL_SCROLLBAR);
See also
sample of BitmapButtonField and Toolbar
PS though its better to use standard menu...
UPDATE
If you want to disable default menu functionality, cancel MENU keydown:
protected boolean keyDown(int keycode, int time) {
if(Keypad.KEY_MENU == Keypad.key(keycode))
{
return true;
}
else
return super.keyDown(keycode, time);
}
UPDATE
I've installed that wonderful weather application and understood this sample may be more alike with several improvements:
use CyclicHFManager as an extension of HorizontalFieldManager
show/hide menuManager on Menu button click
CyclicHFManager is a manager which will keep focus on the same place visually and run all fields over, in cycle. Like in BlackBerry - Custom centered cyclic HorizontalFieldManager
class CyclicHFManager extends HorizontalFieldManager {
int mFocusedFieldIndex = 0;
boolean mCyclicTurnedOn = false;
public void focusChangeNotify(int arg0) {
super.focusChangeNotify(arg0);
if (mCyclicTurnedOn) {
int focusedFieldIndexNew = getFieldWithFocusIndex();
if (focusedFieldIndexNew != mFocusedFieldIndex) {
if (focusedFieldIndexNew - mFocusedFieldIndex > 0)
switchField(0, getFieldCount() - 1);
else
switchField(getFieldCount() - 1, 0);
}
}
else
{
mFocusedFieldIndex = getFieldWithFocusIndex();
}
}
private void switchField(int prevIndex, int newIndex) {
Field field = getField(prevIndex);
delete(field);
insert(field, newIndex);
}
}
alt text http://img109.imageshack.us/img109/6176/toolbarj.jpg
And whole code sample:
abstract class AScreen extends MainScreen {
boolean mMenuEnabled = false;
SizebleVFManager mContentManager = null;
CyclicHFManager mMenuManager = null;
public AScreen() {
mContentManager = new SizebleVFManager(Display.getWidth(), Display
.getHeight(), VERTICAL_SCROLL | VERTICAL_SCROLLBAR);
add(mContentManager);
// mMenuManager = new CyclicHFManager(Display.getWidth(), 60);
mMenuManager = new CyclicHFManager();
mMenuManager.setBorder(BorderFactory.createBevelBorder(new XYEdges(4,
0, 0, 0), new XYEdges(Color.DARKBLUE, 0, 0, 0), new XYEdges(
Color.WHITE, 0, 0, 0)));
mMenuManager.setBackground(BackgroundFactory
.createLinearGradientBackground(Color.DARKBLUE, Color.DARKBLUE,
Color.LIGHTBLUE, Color.LIGHTBLUE));
for (int i = 0; i < 10; i++) {
Bitmap nBitmap = new Bitmap(60, 60);
Graphics g = new Graphics(nBitmap);
g.setColor(Color.DARKBLUE);
g.fillRect(0, 0, 60, 60);
g.setColor(Color.WHITE);
g.drawRect(0, 0, 60, 60);
Font f = g.getFont().derive(Font.BOLD, 40);
g.setFont(f);
String text = String.valueOf(i);
g.drawText(text, (60 - f.getAdvance(text)) >> 1, (60 - f
.getHeight()) >> 1);
Bitmap fBitmap = new Bitmap(60, 60);
g = new Graphics(fBitmap);
g.setColor(Color.DARKBLUE);
g.fillRect(0, 0, 60, 60);
g.setColor(Color.GOLD);
g.drawRect(0, 0, 60, 60);
g.setFont(f);
g.drawText(text, (60 - f.getAdvance(text)) >> 1, (60 - f
.getHeight()) >> 1);
BitmapButtonField button = new BitmapButtonField(nBitmap, fBitmap,
fBitmap);
button.setCookie(String.valueOf(i));
button.setPadding(new XYEdges(0, 18, 0, 18));
button.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
Dialog.inform("Button # " + (String) field.getCookie());
}
});
mMenuManager.add(button);
}
}
protected boolean keyDown(int keycode, int time) {
if (Keypad.KEY_MENU == Keypad.key(keycode)) {
if (mMenuManager.getManager() != null) {
delete(mMenuManager);
mMenuManager.mCyclicTurnedOn = false;
mContentManager.updateSize(Display.getWidth(), Display
.getHeight());
} else {
add(mMenuManager);
mMenuManager.getField(2).setFocus();
mMenuManager.mCyclicTurnedOn = true;
mContentManager.updateSize(Display.getWidth(), Display
.getHeight()
- mMenuManager.getHeight());
}
return true;
} else
return super.keyDown(keycode, time);
}
}
class FirstScreen extends AScreen {
public FirstScreen() {
mContentManager.add(new LabelField("This is a first screen"));
}
}
public class ToolbarMenuApp extends UiApplication {
public ToolbarMenuApp() {
pushScreen(new FirstScreen());
}
public static void main(String[] args) {
(new ToolbarMenuApp()).enterEventDispatcher();
}
}

Resources