I am trying to show a label from another class. However when I add it to the frame it will not show. I have tried drawing it from the counter class itself by passing in the Frame which I would assume is not good practice (ignoring the fact it didn't work). As well as what is in the code below. Can anybody help me and explain why my solution will not show the created label? As you can most likely tell i'm very new to using JPanel.
CookieChaser Class
public class CookieChaser extends JPanel {
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Cookie Chaser");
CookieChaser game = new CookieChaser();
frame.add(game);
frame.setSize(1000, 1000);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
counter Score = new counter(frame);
cookie Cookie = new cookie();
JLabel item = counter.getLabel();
frame.add(item);
frame.setVisible(true);
while (true) {
game.repaint();
Thread.sleep(10);
}
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
}
Counter Class
public class counter {
int count;
static JLabel text;
public counter(JFrame frame){
count = 0;
text = new JLabel(String.valueOf(count));
text.setLocation(0,0);
text.setSize(50,50);
}
public static JLabel getLabel(){
return text;
}
I modified your code to create the following Swing GUI.
Whenever I create a Swing game or application, I use the model / view / controller pattern. This means I create a GUI model. The GUI model contains all of the fields that my GUI needs. Next, I create a GUI view which reads the values from the GUI model. Finally, I create one or more GUI controllers, which update the GUI model and refresh / repaint the GUI view.
I made the following changes to your code:
I created a GUI model. I created the Counter class. All the Counter class does is hold a counter value.
I created a GUI view, which uses the GUI model. I created the JFrame, JPanel, and JLabel all in the view class. You may use more than one class to create the view. Since this view was simple, I created everything in one class.
All Swing applications must start with a call to the SwingUtilities invokeLater method. The invokeLater method puts the creation and updating of the Swing components on the Event Dispatch thread. Oracle and I insist that all Swing applications start this way.
I created a separate Animation runnable so that you can see the JLabel updates. I increment the counter once a second.
The repaint method in the Animation class calls the SwingUtilities invokeLater method to ensure that the JLabel update is done on the Event Dispatch thread. The animation loop runs in a separate thread to keep the GUI responsive.
Here's the revised code.
package com.ggl.testing;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CookieChaser implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new CookieChaser());
}
private JLabel counterLabel;
#Override
public void run() {
Counter counter = new Counter();
JFrame frame = new JFrame("Cookie Chaser");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
counterLabel = new JLabel(" ");
mainPanel.add(counterLabel);
frame.add(mainPanel);
frame.setSize(300, 200);
frame.setVisible(true);
new Thread(new Animation(this, counter)).start();
}
public void setCounterLabel(String text) {
counterLabel.setText(text);
}
public class Counter {
private int counter;
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
public void incrementCounter() {
this.counter++;
}
}
public class Animation implements Runnable {
private Counter counter;
private CookieChaser cookieChaser;
public Animation(CookieChaser cookieChaser, Counter counter) {
this.cookieChaser = cookieChaser;
this.counter = counter;
}
#Override
public void run() {
while (true) {
counter.incrementCounter();
repaint();
sleep(1000L);
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
cookieChaser.setCounterLabel(Integer.toString(counter
.getCounter()));
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
}
}
}
}
Related
I'm trying to create a game loop in java which has a frame cap which can be set while the game is running. The problem im having is I have a render() function and a update() function. Just setting frame cap for both render() and update() means that the speed of the game logic will change when you change the frame cap. Any idea how to have a frame cap which can be set in game while not affecting the speed of the game logic (update())?
As stated in the comments: You can create two threads one of which is responsible for updating and the other is responsible for rendering.
Try to think of an architecture that suits your needs and your game. You can try something like:
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Runner extends JPanel {
private static final long serialVersionUID = -5029528072437981456L;
private JFrame window;
private Renderer renderer;
public Runner() {
setPreferredSize(new Dimension(WindowData.WIDTH, WindowData.HEIGHT));
setFocusable(true);
requestFocus();
window = new JFrame(WindowData.TITLE);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.add(this);
window.setResizable(false);
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
renderer = new Renderer(getGraphics());
renderer.start();
}
public static void main(String[] args) {
new Runner();
}
}
import java.awt.Graphics;
public class Renderer implements Runnable {
private Graphics context;
private Thread thread;
private boolean running;
public Renderer(Graphics context) {
this.context = context;
thread = new Thread(this, "Renderer");
running = false;
}
public void start() {
if (running)
return;
running = true;
thread.start();
}
public void render() {
context.clearRect(0, 0, WindowData.WIDTH, WindowData.HEIGHT);
context.fillRect(50, 50, 100, 100);
}
public void run() {
while (running) {
render();
// ** DO YOUR TIME CONTROL HERE ** \\
}
}
}
This code will actually lead to serious performance issues because you are not in control over the rendering ( repaint() ) time.
But this is just a demo to show you how you can use different threads. Do your own architecture.
I'm trying to make my own version of Snake for learning purposes. Everything seems to work fine except that if I want my frame to be repainted, I have to resize my window manually. Here is my code:
package snake;
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PlayGame extends JPanel implements Runnable{
public boolean animate = false;
public final int FRAME_DELAY = 750;
PickupBall b = new PickupBall();
Snake bob = new Snake();
public synchronized void start() {
animate = true;
}
public synchronized void stop() {
animate = false;
}
private synchronized boolean animationEnabled() {
return animate;
}
#Override
public void run(){
while(true){
if (animationEnabled()){
repaint();
}
try {
Thread.sleep(FRAME_DELAY);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
b.draw(g);
bob.draw(g);
}
public static void main(String[] args) {
JFrame jfr = new JFrame("Snake");
jfr.setSize(640,640);
jfr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jfr.setResizable(true);
PlayGame p = new PlayGame();
jfr.setContentPane(p);
p.setBackground(Color.WHITE);
p.start();
new Thread(p).start();
jfr.setVisible(true);
}
}
Why isn't repaint() triggered without altering the frame size? I get the correlation but it makes no sense to me why it needs such a trigger when it's in a while(true) loop anyway.
What am I missing here?
Edit 1:
Removed thread object
Replaced t.start() with p.start()
Edit 2:
Added new Thread(p).start(); and now it works! Thanks.
Edit 3:
Removed revalidate();
You are executing repaint() in the worker thread and not in the event dispatch tread (EDT) which is the only one actually drawing onto the screen.
You have to en queue the call to repaint() in the EDT using SwingUtilities static methods invokeLater() or invokeAndWait().
Added new Thread(p).start();
Still have no idea how or why this is different to
Thread t = new Thread(p);
t.start();
But it worked.
I'm trying to work with the Java paint
utility and it's been a bit of a hassle.
I'm trying to do something which I assume is quite basic.
I'm drawing a square Graphic to a JPanel and then trying
to move it using repaint
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
public class testGui {
static gui gc_gui;
static int gv_x;
static int gv_y;
public static void main(String[] args) {
gc_gui = new gui();
gv_x = 50;
gv_y = 50;
gc_gui.cv_frame.setVisible(true);
}
public static class gui {
JFrame cv_frame;
content cv_content;
public gui() {
cv_frame = new JFrame();
cv_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cv_frame.setTitle("Test GUI");
cv_frame.setSize(600, 400);
cv_frame.setLayout(new FlowLayout());
cv_content = new content();
cv_content.setBackground(Color.Black);
cv_content.setPreferredSize(new Dimension(500, 300));
cv_frame.add(cv_content);
gv_x = 0;
gv_y = 0;
cv_content.update();
}
}
public static class content extends JPanel {
public void paint(Graphics graphic) {
super.paint(graphic);
draw(graphic);
}
public void update() {
super.repaint();
}
public void draw(Graphics graphic) {
Graphics2D graphic2D = (Graphics2D) graphic;
graphic2D.setPaint(Color.Red);
graphic2D.fillRect(gv_x, gv_y, 100, 100);
}
}
}
I don't know why the call to the update function isn't doing
anything though.
It draws the square at 50x and 50y, the sets it to 0x and 0y
immediately and then when I call repaint I expected it to
be moved to it's new coordinates although it's still at
50x and 50y.
Why is this?
Your solution is to use KeyBindings.
https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
and also.
You need to create a Swing Timer, Thread, or Loop , that manages the frames to be painted. and such
Here is a link for Swing Timers as they are pretty easy to implement:
https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
A lot of programs I see also have this ( AKA. working with threads.):
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
I need to run a background thread in my Java GUI that only runs when I click a button and pauses when I click that button again. I am not exactly sure how to set this up, but I have placed a thread in my constructor and the while loop within is set to go through when I set a specific boolean to TRUE. One button switches from setting this boolean TRUE or FALSE.
Everything else I have in this GUI works fine. When I tried debugging the thread, it actually works as I step through the thread but nothing when I try running the GUI completely. The GUI is rather large so I'm gonna put up a portion of the constructor and the action listener of the button. The rest of the code is unnecessary since it works just fine. I need to know what I am doing wrong here:
public BasketballGUI() {
// certain labels and buttons
Thread runningSim = new Thread() {
public void run() {
while(simRun) {
// do stuff here
}
}
};
runningSim.start();
}
// other GUI stuff
// actionListener that should run the thread.
class SimButtonListener implements ActionListener {
public void actionPerformed(ActionEvent arg0) {
if(!simRun) {
simRun = true;
sim.setText("Pause Simulator");
}
else if(simRun) {
simRun = false;
sim.setText("Run Simulator");
}
// other stuff in this actionListener
}
}
Establish a Swing based Timer with an ActionListener that will be called repeatedly.
In the actionPerformed(ActionEvent) method call repaint().
Start the timer (Timer.start()) when the user clicks Start
Stop the timer (Timer.stop()) when the user clicks Stop
If you cannot get it working from that description, I suggest you post an SSCCE of your best attempt.
I thought I had one 'lying around'.. Try this working SSCCE which uses images created in this SSCCE.
I could see this background thread useful for a Java GUI when handling button events to affect something like a text area or progress bar.
For the sake of argument, I will build you a tiny GUI that affects a Text Area. I hope this helps you.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.*;
public class TestClass extends JPanel {
super("TestClass - Title");
private AtomicBoolean paused;
private JTextArea jta;
private JButton btn;
private Thread thread;
public TestClass() {
paused = new AtomicBoolean(false);
jta = new JTextArea(100, 100);
btn = new JButton();
initialize();
}
public void initialize() {
jta.setLineWrap(true);
jta.setWrapStyleWord(true);
add(new JScrollPane(jta));
btn.setPreferredSize(new Dimension(100, 100));
btn.setText("Pause");
btn.addActionListener(new ButtonListener());
add(btn);
Runnable runnable = new Runnable() {
#Override
public void run() {
while(true) {
for(int i = 0; i < Integer.MAX_VALUE; i++) {
if(paused.get()) {
synchronized(thread) {
try {
thread.wait();
} catch(InterruptedException e) {
}
}
}
}
jta.append(Integer.toString(i) + ", ");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
};
thread = new Thread(runnable);
thread.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 30);
}
class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
if(!paused.get()) {
btn.setText("Start");
paused.set(true);
} else {
btn.setText("Pause");
paused.set(false);
synchronized(thread) {
thread.notify();
}
}
}
}
}
Main class to call everything.
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class MainClass {
public static void main(final String[] arg) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestClass());
frame.pack();
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
});
}
}
I did not test this code to see if it works exactly, Its main goal is to break you through your coders block and use my components to fix your issue. Hope this helped. Need anything else Email me at DesignatedSoftware#gmail.com
I'm making my first Applet. I have a JPanel which creates a Swing GUI and performs CPU intensive tasks (repainting a Component 60Hz). My Applet displays this JPanel on event dispatching thread. here is an abstraction of the problem. Normally I would launch the applet from an html document instead of having a main method. This program puts about a 40% load on my CPU.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
public class TestApplet extends JApplet {
TestPanel tp;
public void init() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
System.err.println("createGUI didn't complete successfully");
}
}
private void createGUI() {
//Create and set up the content pane.
tp = new TestPanel();
tp.setOpaque(true);
setContentPane(tp);
}
public static void main(String[] args) {
JFrame f = new JFrame("Fish Tank");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JApplet ap = new TestApplet();
ap.init();
f.add("Center", ap);
f.pack();
f.setVisible(true);
}
}
class TestPanel extends JPanel{
public TestTank tt = new TestTank();
public TestPanel() {add(tt);}
public void stop() {tt.stop();}
public void start() {tt.start();}
}
class TestTank extends Component implements ActionListener{
private javax.swing.Timer timer;
TestTank(){
timer = new javax.swing.Timer(17, this);
timer.setCoalesce(true);
timer.start();
}
public Dimension getPreferredSize(){
return new Dimension(900, 700);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Dimension size = getSize();
g2.setPaint(new GradientPaint(0,0,Color.RED,900, 0,Color.WHITE));
g2.fill(new Rectangle2D.Float(0,0,size.width,size.height));
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public void stop(){timer.stop();}
public void start(){timer.start();}
}
My question: How do I suspend and resume execution of the JPanel (FishTankPanel) when the user switches tabs or minimizes the browser? I want the Applet to stop using the CPU when the user can't see what it is doing. I need to capture browser events in order to execute tp.stop() in the applet. I have tried to execute them with window event listeners in the JPanel, and by overriding the start() and stop() methods in the Applet. I have been unsuccessful. Any suggestions or solutions would be appreciated.
I would do as Dave said and use the JApplet override start and stop methods to call your GUI methods. For instance, see changes in code:
public class TestApplet extends JApplet {
TestPanel tp;
public void init() {
// ... no change
}
private void createGUI() {
// ... no change
}
#Override
public void stop() {
if (tp != null) {
tp.stop();
}
}
#Override
public void start() {
if (tp != null) {
tp.start();
}
}
}
class TestTank extends Component implements ActionListener {
private javax.swing.Timer timer;
// ... no change
public void stop() {
timer.stop();
System.out.println("stop");
}
public void start() {
timer.start();
System.out.println("start");
}
}
It seems you might need to leverage some JS for this. E.G. use the JS shown in this answer to explicitly call the applet start() & stop() methods on focus & blur detection respectively.
The solution for my problem was to use javascript to implement the Page Visibility API. I then called the appropriate Java methods from within the javascript script.