I have a working system where one Custom component can be updated by user clicking button or by timer. But I'm not sure this is the most efficient way and if it is thread safe.
Is there any better way to solve this?
I have component based on Canvas: MyBox
One class that holds the data: MyColorBox
Listening interface: MyColorBoxListener
From the main class:
pane.getChildren().add( new MyBox( myboxdata));
The data can be updated by timers or user clicking by calling:
myboxdata.update();
The user can choice Play, Stop or Next. Play and Stop will control the timer. If the timer is stoppe it will be possible to click Next.
My data class:
import javafx.scene.paint.Color;
public class MyColorBox {
Color color[] = new Color[]{Color.BLUE, Color.RED, Color.GREEN};
Color color_second;
int counter = 0;
MyColorBoxListener listener;
public MyColorBox(MyColorBoxListener listener) {
this.listener = listener;
}
public MyColorBox() {
setListener(null);
}
public void setListener(MyColorBoxListener listener) {
this.listener = listener;
}
public void update() {
if(++counter>=color.length) counter=0;
if(listener!=null)
listener.updated();
}
public Color getCurrentColor() {
return color[counter];
}
}
Listener interface:
public interface BouncingBoxListener {
public void updated();
}
My Component:
public class MyBox extends Canvas implements MyColorBoxListener {
MyColorBox box;
public MyBox( MyColorBox box) {
super(100,100);
this.box = box;
box.setListener(this);
draw();
}
public void draw() {
GraphicsContext gc = this.getGraphicsContext2D();
gc.setStroke( box.getCurrentColor());
gc.setLineWidth(2);
gc.strokeRect(0,0, this.getWidth(), this.getHeight());
}
#Override
public void updated() {
draw();
}
}
Related
I have a world that is the GameScreen (20ish objects) which lays all objects as intended. However, when I get GameOver I want to be a blank canvas with just the background and some new objects(a couple objects), but all the existing objects from GameScreen carry over and I cant figure out how to stop it or delete them on the GameOver screen
public class GameScreen extends World
{
public GameScreen()
{
super(600, 400, 1);
prepare();
}
private void prepare()
{
addObjects.......
}
}
public class GameLost extends GameScreen
{
public GameLost()
{
removeObjects(GameScreen);
prepare();
}
private void prepare()
{
addObjects...
}
I'm pretty sure you don't want GameLost extends GameScreen.
Do
class GameLost {
private Background bg;
public void paint() {
paint(bg);
}
}
class GameScreen {
private Background bg;
private List<GameObjects>...
public void paint() {
paint(bg);
gameObjects.forEach(go -> paint(go));
}
}
which makes it easy to move over the background from game screen to game over screen, without the game objects.
I want to develop a simple interactive game (like arcanoid). I already have implemented a menu and different views, and now I need to develop the actually game (draw flying ball, some movable platform) and I don't know how to do this. I need something like canvas where I can draw my graphic each frame.
I have tryed to implement this with Canvas and Timer. But it doesn't want update graphic itself, but only when user clicks on screen or similar. Also I saw com.google.gwt.canvas.client.Canvas, but I cannot understand how to use it in Vaadin application.
So my question is next: is it possible to draw some graphic each frame with high framerate in any way? If possible, how can I do this?
P.S. I use the Vaadin 7.3.3.
ADDED LATER:
Here is a link to my educational project with implementation below.
I'll be glad if it helps someone.
ORIGINAL ANSWER:
Well... I found the solution myself. First of all, I have created my own widget - "client side" component (according to this article).
Client side part:
public class GWTMyCanvasWidget extends Composite {
public static final String CLASSNAME = "mycomponent";
private static final int FRAMERATE = 30;
public GWTMyCanvasWidget() {
canvas = Canvas.createIfSupported();
initWidget(canvas);
setStyleName(CLASSNAME);
}
Connector:
#Connect(MyCanvas.class)
public class MyCanvasConnector extends AbstractComponentConnector {
#Override
public Widget getWidget() {
return (GWTMyCanvasWidget) super.getWidget();
}
#Override
protected Widget createWidget() {
return GWT.create(GWTMyCanvasWidget.class);
}
}
Server side part:
public class MyCanvas extends AbstractComponent {
#Override
public MyCanvasState getState() {
return (MyCanvasState) super.getState();
}
}
Then I just add MyCanvas component on my View:
private void createCanvas() {
MyCanvas canvas = new MyCanvas();
addComponent(canvas);
canvas.setSizeFull();
}
And now I can draw anything on Canvas (on client side in GWTMyCanvasWidget) with great performance =). Example:
public class GWTMyCanvasWidget extends Composite {
public static final String CLASSNAME = "mycomponent";
private static final int FRAMERATE = 30;
private Canvas canvas;
private Platform platform;
private int textX;
public GWTMyCanvasWidget() {
canvas = Canvas.createIfSupported();
canvas.addMouseMoveHandler(new MouseMoveHandler() {
#Override
public void onMouseMove(MouseMoveEvent event) {
if (platform != null) {
platform.setCenterX(event.getX());
}
}
});
initWidget(canvas);
Window.addResizeHandler(new ResizeHandler() {
#Override
public void onResize(ResizeEvent resizeEvent) {
resizeCanvas(resizeEvent.getWidth(), resizeEvent.getHeight());
}
});
initGameTimer();
resizeCanvas(Window.getClientWidth(), Window.getClientHeight());
setStyleName(CLASSNAME);
platform = createPlatform();
}
private void resizeCanvas(int width, int height) {
canvas.setWidth(width + "px");
canvas.setCoordinateSpaceWidth(width);
canvas.setHeight(height + "px");
canvas.setCoordinateSpaceHeight(height);
}
private void initGameTimer() {
Timer timer = new Timer() {
#Override
public void run() {
drawCanvas();
}
};
timer.scheduleRepeating(1000 / FRAMERATE);
}
private void drawCanvas() {
canvas.getContext2d().clearRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
drawPlatform();
}
private Platform createPlatform() {
Platform platform = new Platform();
platform.setY(Window.getClientHeight());
return platform;
}
private void drawPlatform() {
canvas.getContext2d().fillRect(platform.getCenterX() - platform.getWidth() / 2, platform.getY() - 100, platform.getWidth(), platform.getHeight());
}
}
I want to be able to have a canvas component that adds spots to places where the user double clicks, when a spot is added, and then I want to be able to notify the listbox placed in the main frame that a spot has been added.
My knowledge of event broadcasting is poor, is there a better way of doing this?
Main class: initialize drawing component:
will this be best for an unnamed inner class?
private JList lbx;
private DefaultListModel<String> lbxModel;
private DrawComp draw;
public static void main(String args[]) {
draw.addListener(new DrawCompListener() {
#Override
public void spotAdded(DrawComp source, Spot spot) {
lbxModel.addElement("spot+" added");
}
});
}
interface that will be passed through to the queue:
public interface DrawCompListener {
void spotAdded(DrawComp source, Spot spot);
Queue Class that will implement the interface: (should this be here?)
public class DrawCompListenerQueue implements DrawCompListener {
private ArrayList<DrawCompListener> listeners = new ArrayList<DrawCompListener>();
public void addListener(DrawCompListener listener) {
listeners.add(listener);
}
#Override
public void spotAdded(DrawComp source, Spot spot) {
for(DrawCompListener listener : listeners) {
listener.spotAdded(source, spot);
}
}
The actual drawing component to place the spots and pass through the info to the main class for the Jlist
public class DrawComp extends JPanel implements MouseListener {
private Vector<Spot> spots = new Vector<Spot>();
private DrawCompListenerQueue listenerQ = new DrawCompListenerQueue();
public void addListener(DrawCompListener listener) {
listenerQ.addListener(listener);
}
adding a spot:
public void mouseClicked(MouseEvent e) {
// left-click = add spot
if ((e.getButton() == MouseEvent.BUTTON1) && (e.getClickCount() == 2)) {
// add spot
Spot s = new Spot();
s.p = e.getPoint();
spots.add(s);
repaint();
// notify listeners
listenerQ.spotAdded(this, s);
Do not reinvent the wheel use the Glazed list lib.
Please have a look at the following code
First, Please note I am a 100% newbie to Java Mobile.
In here, I am making the light on and vibrate on when user click the button. However, I really wanted to create a SOS application which turn the whole screen into white, and go to black, like that, in the thread. I guess I didn't achieve that by this app because even the lights are on, the buttons are still there. I tried to turn the "Form" color to "white" but it seems like JME has no "Color" class.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class Midlet extends MIDlet{
private Form f;
private Display d;
private Command start,stop;
private Thread t;
public Midlet()
{
t = new Thread(new TurnLightOn());
}
public void startApp()
{
f = new Form("Back Light On");
d = Display.getDisplay(this);
d.setCurrent(f);
start = new Command("Turn On",Command.OK,0);
stop = new Command("Turn Off",Command.OK,1);
f.addCommand(start);
f.setCommandListener(new Action());
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional)
{
this.notifyDestroyed();
}
private class Action implements CommandListener
{
public void commandAction(Command c, Displayable dis)
{
f.append("Light is Turnning On");
t.start();
}
}
private class ActionOff implements CommandListener
{
public void commandAction(Command c, Displayable dis)
{
}
}
private class TurnLightOn implements Runnable
{
public void run()
{
f.append("Working");
for(int i=0;i<100;i++)
{
try
{
d.flashBacklight(200);
d.vibrate(200);
Thread.sleep(1000);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
}
}
}
Use the javax.microedition.lcdui.Canvas instead of Form. This example can get you started
public void startApp()
{
f = new Form("Back Light On");
d = Display.getDisplay(this);
start = new Command("Turn On",Command.OK,0);
stop = new Command("Turn Off",Command.OK,1);
f.addCommand(start);
f.setCommandListener(new Action());
myCanvas = new MyCanvas();
d.setCurrent(myCanvas);
myCanvas.repaint();
}
Now create a canvas and implement paint method like this:
class MyCanvas extends Canvas {
public void paint(Graphics g) {
// create a 20x20 black square in the center
// clear the screen first
g.setColor(0xffffff);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0xffffff); // make sure it is white color
// draw the square, <b>changed to rely on instance variables</b>
<b>g.fillRect(x, y, getWidth(), getHeight());</b>
}
}
I need a listener that will constantly check if a static boolean value has been changed so that I can repaint a component on a frame. Can someone please help me I really don't know much about listeners and haven't worked with them much? Help will be greatly appreciated.
edit(more clarity): I have two separate classes in which on class is the "main frame" the second class is an extension of JLabel and implements MouseListner for a "clickable photo". The "main frame" creates instances of the photo and when the photo is clicked the "main frame" is supposed to paint on the panel a description of the photo. This is "main frame"
MenuBar menuBar;
static AnnotationVisual a;
Picture pic;
Picture pic2;
GalleryScreen(int rows, int columns){
this.setBorder(BorderFactory.createEmptyBorder(500,500,0,0));
pic = new Picture("pic1", "Z:/My Documents/Downloads/Ball.jpg", new Coordinate(0,0));
pic2 = new Picture("pic2", "Z:/My Documents/Downloads/hoop.jpg" , new Coordinate(1,0));
this.add(pic);
this.add(pic2);
a = new AnnotationVisual();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
if(a.shouldAnnotate()){
FontMetrics size= g.getFontMetrics();
if(getWidth()>=(a.dispX()+size.stringWidth(a.annotationText()))){
g.setColor(Color.white);
g.fillRect(a.dispX()-3,a.dispY()-12,size.stringWidth(a.annotationText())+5,15);
g.setColor(Color.black);
g.drawRect(a.dispX()-3,a.dispY()-12,size.stringWidth(a.annotationText())+5,15);
g.drawString(a.annotationText(), a.dispX(), a.dispY());
}else{
String sub="";
int letters=0;
g.setColor(Color.white);
g.fillRect(a.dispX()-3,a.dispY()-12,getWidth(),15);
g.setColor(Color.black);
for(int i=0;i<a.annotationText().length();i++){
if(a.dispX()+letters+16<=getWidth()){
sub+=a.annotationText().substring(i,i+1);
letters=size.stringWidth(sub);
}else{
sub=sub+"...";
i=a.annotationText().length();
}
}
g.drawRect(a.dispX()-3,a.dispY()-12,size.stringWidth(sub)+3,15);
g.drawString(sub,a.dispX(),a.dispY());
}
}
}
public static AnnotationVisual getA()
{
return a;
}
This is "clickable photo"
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.*;
import javax.swing.*;
public class Picture extends JLabel implements MouseListener
{
String myAnnotation;
String filePath;
Coordinate imageCoord;
private boolean wasDoubleClick;
private Timer timer;
EditAnnotation newEdit;
AnnotationVisual newVisual;
public Picture(String annotation, String filePath, Coordinate coord)
{
super(new ImageIcon(filePath));
this.addMouseListener(this);
myAnnotation=annotation;
this.filePath = filePath;
imageCoord = coord;
newEdit = new EditAnnotation(annotation);
newVisual = new AnnotationVisual();
}
public Picture(String filePath)
{
super(new ImageIcon(filePath));
this.addMouseListener(this);
this.filePath = filePath;
newEdit = new EditAnnotation();
newVisual = new AnnotationVisual();
}
public String getAnnotation()
{
return myAnnotation;
}
public AnnotationVisual getAnnotationVisual()
{
return newVisual;
}
public void setAnnotation(String annotation)
{
myAnnotation=annotation;
}
public Coordinate getCoordinate()
{
return imageCoord;
}
public void setCoordinate(Coordinate coord)
{
imageCoord = coord;
}
public Dimension getSize()
{
return new Dimension(super.getIcon().getIconWidth(), super.getIcon().getIconHeight());
}
public void mouseClicked(MouseEvent e)
{
final int scrLocX = (int)e.getLocationOnScreen().getX();
final int scrLocY = (int)e.getLocationOnScreen().getY();
if (e.getClickCount() == 2)
{
wasDoubleClick = true;
}
else if(e.getClickCount() == 1)
{
Integer timerinterval = (Integer) Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval");
timer = new Timer(timerinterval.intValue(), new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
if (wasDoubleClick)
{
GalleryScreen.getA().deleteAnnotation();
myAnnotation = newEdit.getAnnotation();
newEdit.show(myAnnotation);
wasDoubleClick = false;
}
else
{
GalleryScreen.getA().deleteAnnotation();
GalleryScreen.getA().showAnnotation(scrLocX, scrLocY , myAnnotation);
}
}
});
timer.setRepeats(false);
timer.start();
}
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
}
AnnotationVisual is the thing that supposed to pop up when single clicked
You're probably better off making the boolean private, and only allowing it to be changed through a setter method. The setter method, when called, should then repaint the component.
The point of listeners is to invert the logic. You don't constantly check if a value is changed. You notify the listener when you change the value.
So, instead of Foo.bar = 5, you invoke Foo.setBar(5), where in addition to the assignment, you call barListener.valueChanged(value)
As a sidenote - avoid storing state in static variables.
You don't set a listener on a field in Java, you set it on a property. While properties (according to the JavaBeans spec) can be fields, they're usually done as pairs of methods (one getter, one setter; the latter being not needed for read-only fields) as that lets you hook extra logic in to be called when the property is accessed. Such as firing a listener callback to say that the value has changed. (You could use a thread to monitor for that sort of thing, but that's really nasty and error-prone. Wasteful too.)
One thing to be aware of though: you don't know what thread the value will have been modified from. Take care when invoking back into Swing…