I never get "paint" written to my command line window when I use Eclipse and Run->cmd to run the program. It works fine if I run System.out.print() from paintComponent in another program. Someone who can help?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GUI extends JPanel implements KeyListener, ActionListener
{
private static final long serialVersionUID = 1L;
JFrame frmMain = new JFrame("Kodning");
JTextField text = new JTextField();
JPanel pan = new JPanel();
static char bokstav;
static int x=10, y=80;
boolean convert = false;
String s;
Timer t = new Timer(10, this);
public static void main(String[] args)
{
#SuppressWarnings("unused")
GUI g = new GUI();
}
public GUI()
{
frmMain.setSize(600, 120);
frmMain.setLayout(new GridLayout(2, 1));
frmMain.addWindowListener(hornStang());
frmMain.add(text);
frmMain.add(pan);
frmMain.setFocusable(true);
frmMain.setVisible(true);
frmMain.addKeyListener(this);
text.addKeyListener(this);
pan.addKeyListener(this);
t.start();
}
private static WindowAdapter hornStang()
{
return new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
};
}
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode()== KeyEvent.VK_ENTER)
{
System.out.println("dechifrera");
repaint();
deshiffrera(text.getText());
}
}
public void keyReleased(KeyEvent arg0){}
public void keyTyped(KeyEvent arg0){}
public void deshiffrera(String s)
{
s = this.s;
repaint();
}
#override
public void paintComponent(Graphics g)
{
System.out.println("paint");
for(int i=0;i<s.length();i++)
{
bokstav = s.charAt(i);
switch (bokstav)
{
case 'a':nere(g); hoger(g); prick(g, 0); break;
//en massa case
default:break;
}
x=x+12;
}
}
#Override
public void actionPerformed(ActionEvent e)
{
repaint();
}
}
The component must be added to a visible window/frame/component for it's paintComponent to be called.
GUI is only added as a KeyListener but is neither added to the JFrame, nor any other visible component in the code above. There is no reason for calling paintComponent since the component is not being displayed at all.
There are a number of issues with your code:
Your GUI panel is not in the frame (shouldn't it be added instead of pan?)
String s is uninitialized, which causes a NullPointerException
paint should be overridden instead of paintComponents
paint should not change the state of the component, because it can be called any time.
etc...
You probably miss the output of "System.out.println("paint");" ?
GUI-Apps under Windows cant write to the console (they dont have a console, because it would suck if every GUI-App would also open a black window).
There are two java-interpreters under windows: "javaw.exe" which is a GUI-App and silently discards any System.out-writes. And "java.exe" which is a console-app and allows writing to the console. Try to start your program with "java.exe"
I use this with AWT (not 100% sure whether it's working in Swing too...)
Graphics g = _yourcomponent_.getGraphics();
if (g != null) {
_yourcomponent_.paint(g);
// below the estimated code for Swing:
_yourcomponent_.paintComponent(g);
}
Related
I am very new to the Graphics portion of Java. I have created a frame and on it I have added a panel whose color has been set to Green. Now on clicking that panel I want to draw a circle using a test class's object called Mypanel. But it does not happen. Please guide !
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
class Mypanel extends JPanel
{
#Override
public void paintComponent(Graphics g)
{
g.drawOval(15, 15, 5, 5);
}
}
public class algo extends javax.swing.JFrame {
public algo() {
initComponents();
jPanel1.setBackground(Color.GREEN);
}
Mypanel p = new Mypanel() ;
private void jPanel1MouseClicked(java.awt.event.MouseEvent evt) {
p.repaint();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new algo().setVisible(true);
}
});
}
}
If I were to guess I would say that I am not supposed to use the repaint method, but I was told that this was to be used.
That code as supplied would not compile. For better help sooner, post a Minimal, Complete, and Verifiable example or Short, Self Contained, Correct Example.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Mypanel extends JPanel {
boolean clicked = false;
Mypanel() {
setBackground(Color.GREEN);
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
clicked = true;
repaint();
}
};
this.addMouseListener(mouseListener);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 100);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (clicked) {
g.drawOval(15, 15, 50, 50);
}
}
}
public class algo extends JFrame {
public algo() {
initComponents();
pack();
//jPanel1.setBackground(Color.GREEN); ?!?
}
protected final void initComponents() {
add(new Mypanel());
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new algo().setVisible(true);
}
});
}
}
There are a few things to correct in your example...
When you create the frame (i.e. in the constructor) you'll want to call super(). This is the first thing the constructor has to do. Then, you'll probably want to set an initial width/height, and set the background color of the frame green.
You need to add a mouse listener so that the mouseClicked method is actually called. Then have it add the 'MyPanel' object to the frame, and call repaint.
I think that's roughly what you're going for.
I'm working on a small personal project.
I have two JFrame windows, Viewer and Console.
Viewer only contains a subclass of JPanel, and it should respond to mouse input (clicking, mostly).
Console contains a button, a JTextField (the input), and a JTextArea (the output).
I need to make sure than when I press keys on my keyboard, the corresponding text appears in the Console JTextField, not only when the focus is held by the JTextField, but also when it's held any other component in my app.
In other words, I want the JTextField to accept input even right after I clicked on the Viewer frame.
Is this feasible?
In case that matters, I'm running win 8 x64 and I don't really care about portability (I'm the only one that will ever look at or use this code).
EDIT: here is an example of my code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class main {
public static Viewer v;
public static Console c;
public static void main(String[] args) {
v=new Viewer();
c=new Console();
}
static class Viewer extends JFrame {
public Viewer(){
setSize(600,600);
getContentPane().add(new myPanel());
addMouseListener(new mouse());
setVisible(true);
}
}
static class myPanel extends JPanel{
public void paintComponent(Graphics g){
g.setColor(new Color((int)(Math.random()*255),(int)(Math.random()*255),(int)(Math.random()*255)));
g.fillRect(0, 0, 600, 600);
}
}
static class Console extends JFrame {
public Console(){
setSize(600,200);
JTextField text=new JTextField();
getContentPane().add(text);
setVisible(true);
}
}
static class mouse implements MouseListener{
#Override
public void mouseClicked(MouseEvent arg0) {
v.repaint();
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
}
In this example, after I click on the big window to change its color, if I want to write stuff in the other window I have to click on it first.
Can I suggest the KeyboardFocusManager? I've found this to be the easiest way to achieve what I believe you are trying to achieve
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(
new KeyEventDispatcher() {
public void dispatchKeyEvent(KeyEvent ke) {
//TODO: fill this in
}
});
One inelegant solution is to create a KeyListener, which feeds typed characters to your console, although how exactly this happens depends on how you create your components. For the sake of this example I'll pretend to do it through a static method in your Console class (preferably you'd have access to an instance of Console):
public class ApplicationKeyListener implements KeyListener {
public ApplicationKeyListener() {
}
// Other necessary methods...
public void keyPressed(KeyEvent e) {
char pressed = e.getKeyChar();
Console.feedChar(pressed);
}
}
And then in Console, make sure your JTextField object is global and static, then set up the feedChar() method.
public class Console extends JFrame {
private static JTextField consoleTextField;
public Console() {
consoleTextField = new JTextField();
// ...
}
public static feedChar(char c) {
consoleTextField.setText(consoleTextField.getText() + c.toString());
}
}
Then finally you'd have to add this listener to all JFrames and their children.
public class Viewer extends JFrame {
public Viewer() {
ApplicationKeyListener kl = new ApplicationKeyListener();
addKeyListener(kl);
for (Component child : this.getComponents()) {
child.addKeyListener(kl);
}
}
}
The program makes a ball glide across from top left to bottom right and works. But if I were to shift the line
frame.getContentPane().add(ball);
from its current position to after the for loop, why doesn't the ball show up on the frame.
I agree that the ball should no longer move, because all the shifting done in the for loop happens even before I add the ball to the JFrame,but I don't understand why the ball doesn't show up on the screen when I ultimately add it to the frame.
Here's the code of the working program, if you shift the line mentioned above to after the for loop, the ball no longer shows up
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Animate
{
private JFrame frame;
private int x,y;
public static void main(String args[])
{
Animate ballRoll = new Animate();
ballRoll.go();
}
public void go()
{
frame = new JFrame();
frame.setSize(500,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyRoll ball = new MyRoll();
frame.getContentPane().add(ball);
for(x = 5;x<=350;x++)
{
y=x;
try
{
Thread.sleep(50);
}
catch(Exception e)
{
System.out.println("dsfsd");
}
ball.repaint();
}
}
class MyRoll extends JPanel
{
public void paintComponent(Graphics g)
{
g.setColor(Color.BLACK);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.ORANGE);
g.fillOval(x, y, 100, 100);
}
}
}
Some points to remember:
Don't use Thread.sleep() that sometime hangs the whole swing application instead try with Swing Timer that is most suitable for swing application.
Read more How to Use Swing Timers
Don't forget to call super.paintComponent() in overridden paintComponent() method.
Call frame.setVisible(true) in the end after adding all the components.
Use frame.pack() instead of frame.setSize(500,500) that fits the components as per component's preferred size.
Override getPreferredSize() to set the preferred size of the JPanel in case of custom painting.
Use SwingUtilities.invokeLater() or EventQueue.invokeLater() to make sure that EDT is initialized properly.
Read more
Why to use SwingUtilities.invokeLater in main method?
SwingUtilities.invokeLater
Should we use EventQueue.invokeLater for any GUI update in a Java desktop application?
Sample code: (change it as per your custom painting)
private Timer timer;
...
timer = new javax.swing.Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
y = ++x;
ball.repaint();
if (x > 350) {
timer.stop();
}
}
});
timer.setRepeats(true);
timer.start();
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Animate ballRoll = new Animate();
ballRoll.go();
}
});
}
class MyRoll extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
...
}
#Override
public Dimension getPreferredSize() {
return new Dimension(..., ...);
}
}
I created a graphic interface in java and 2 buttons.
My aim :
1) When I click on the first button, having a loop in which different tasks are processed (Button "Start"). Between each loop there is a stop of 10 seconds
2) When I click on the second button, the loop is processed immediately one last time but then stopped.
(I also would like to make a pop up showing that it has been stopped but that's not the main question, I think I can do it.)
I tried the following code, but first I think they are more simple ways to sort my problem. Plus I can compile but it doesn't work, the loop is not stopped, the window crashes:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
globalStop="Run";
while (globalStop.equals("Run")) {
System.out.println("GO");
// Other stuff
// For the break ?
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
Logger.getLogger(main.class.getName()).log(Level.SEVERE, null, ex);
}
}
System.out.println("done");
}
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
globalStop = "Stop";
System.out.println("Bouton2");
}
I hope I was clear enough, if that is not the case, please let me know and I will rephrase.
Thank you all in advance for your help.
I wondered how long it would take me to create a United States type traffic signal GUI. It took 75 minutes. I was able to create the GUI quickly because a lot of Swing is boilerplate. Once you create one GUI, you can copy some of the classes for your next GUI.
Here's an image of the traffic signal GUI.
When you press the Start button, the traffic signal will cycle from green to yellow to red. The traffic signal will cycle forever, until you press the Stop button.
When you press the Stop button, the traffic signal will turn red. It will stay red forever, until you press the Start button.
When you press the Start button while the traffic signal is cycling, the green to yellow to red cycle starts over.
Basically, the following steps show you how to create any Swing GUI. I didn't create the code in this order, but it makes sense to explain the code in a logical order. So, let's dig into the code.
This is the model class for the GUI. Every GUI needs to have it's own model, separate from the model of the application. For this GUI, the model is simple.
package com.ggl.traffic.signal.model;
import java.awt.Dimension;
public class TrafficSignalModel {
public static final int RED_LIGHT_TIME = 15;
public static final int YELLOW_LIGHT_TIME = 5;
public static final int GREEN_LIGHT_TIME = 10;
public static final Dimension LIGHT_SIZE = new Dimension(32, 32);
}
We set the signal light times in the model, as well as the size of the traffic lights.
For a more complicated GUI, we would keep track of the field values in the model.
Next, we have the main class of the traffic signal GUI.
package com.ggl.traffic.signal;
import javax.swing.SwingUtilities;
import com.ggl.traffic.signal.view.TrafficSignalFrame;
public class TrafficSignal implements Runnable {
#Override
public void run() {
new TrafficSignalFrame();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new TrafficSignal());
}
}
This class ensures that the traffic signal GUI is on the Swing event thread. That's all this class does. You can see how you can copy this class to start any GUI.
Next, we have the Frame class of the GUI.
package com.ggl.traffic.signal.view;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class TrafficSignalFrame {
protected ButtonPanel bPanel;
protected JFrame frame;
protected TrafficSignalPanel tsPanel;
public TrafficSignalFrame() {
createPartControl();
}
protected void createPartControl() {
tsPanel = new TrafficSignalPanel();
bPanel = new ButtonPanel();
bPanel.setTrafficSignalPanel(tsPanel);
frame = new JFrame();
frame.setTitle("Traffic Signal");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent event) {
exitProcedure();
}
});
frame.setLayout(new FlowLayout());
frame.add(bPanel.getPanel());
frame.add(tsPanel.getPanel());
frame.pack();
// frame.setBounds(100, 100, 400, 200);
frame.setVisible(true);
}
public void exitProcedure() {
frame.dispose();
System.exit(0);
}
public JFrame getFrame() {
return frame;
}
}
This class is boilerplate, except for the particular JPanels that will make up the GUI. If your JFrame has a JMenu, this would be the place to attach your JMenu to your JFrame.
Notice that I did not extend JFrame to make this class. The only time you extend a Swing component is when you're overriding one or more of the component's methods. If I need the actual JFrame, I call the getFrame() method. Using Swing components rather than extending Swing components keeps my methods separate from the Swing methods.
Next, we'll look at the traffic signal light panel. This panel makes up one of the 3 lights in the traffic signal.
package com.ggl.traffic.signal.view;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class TrafficSignalLightPanel extends JPanel {
private static final long serialVersionUID = 1L;
protected boolean lightOn;
protected Color lightColor;
protected Color darkColor;
public TrafficSignalLightPanel(Color lightColor) {
this.lightColor = lightColor;
this.darkColor = Color.WHITE;
this.lightOn = false;
}
public void setLightOn(boolean lightOn) {
this.lightOn = lightOn;
this.repaint();
}
#Override
public void paintComponent(Graphics g) {
if (lightOn) {
g.setColor(lightColor);
} else {
g.setColor(darkColor);
}
g.fillRect(0, 0, getWidth(), getHeight());
}
}
This class extends JPanel, because we want to override the paintComponent method. This is a simple class. All it does is paint the panel a color, or white.
Next, we'll look at the traffic signal panel. This panel creates 3 light panels and arranges them in a vertical row.
package com.ggl.traffic.signal.view;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.border.Border;
import com.ggl.traffic.signal.model.TrafficSignalModel;
public class TrafficSignalPanel {
protected JPanel panel;
protected TrafficSignalLightPanel redLight;
protected TrafficSignalLightPanel yellowLight;
protected TrafficSignalLightPanel greenLight;
public TrafficSignalPanel() {
createPartControl();
}
protected void createPartControl() {
Border border = BorderFactory.createLineBorder(Color.BLACK, 4);
redLight = new TrafficSignalLightPanel(Color.RED);
redLight.setBorder(border);
redLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);
yellowLight = new TrafficSignalLightPanel(Color.YELLOW);
yellowLight.setBorder(border);
yellowLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);
greenLight = new TrafficSignalLightPanel(Color.GREEN);
greenLight.setBorder(border);
greenLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);
panel = new JPanel();
panel.setLayout(new FlowLayout());
panel.setPreferredSize(
new Dimension(TrafficSignalModel.LIGHT_SIZE.width + 10,
TrafficSignalModel.LIGHT_SIZE.height * 3 + 25));
panel.add(redLight);
panel.add(yellowLight);
panel.add(greenLight);
}
public JPanel getPanel() {
return panel;
}
public TrafficSignalLightPanel getRedLight() {
return redLight;
}
public TrafficSignalLightPanel getYellowLight() {
return yellowLight;
}
public TrafficSignalLightPanel getGreenLight() {
return greenLight;
}
}
A fairly straightforward creation of a JPanel from 3 JPanels. I set the preferred size of the JPanel so the lights will be in a vertical row.
Next, we'll look at the button panel. You can pretty much copy this code into any GUI that has a button panel.
package com.ggl.traffic.signal.view;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
import com.ggl.traffic.signal.thread.TrafficSignalCycle;
public class ButtonPanel {
protected JButton startButton;
protected JButton stopButton;
protected JPanel panel;
protected TrafficSignalCycle thread;
protected TrafficSignalPanel tsPanel;
public ButtonPanel() {
this.thread = null;
createPartControl();
}
protected void createPartControl() {
panel = new JPanel();
panel.setLayout(new FlowLayout());
startButton = new JButton("Start");
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if (thread != null) {
thread.stopRunning();
}
tsPanel.getRedLight().setLightOn(false);
tsPanel.getYellowLight().setLightOn(false);
tsPanel.getGreenLight().setLightOn(false);
thread = new TrafficSignalCycle(tsPanel);
thread.start();
}
});
panel.add(startButton);
stopButton = new JButton("Stop");
stopButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if (thread != null) {
thread.stopRunning();
thread = null;
}
tsPanel.getRedLight().setLightOn(true);
tsPanel.getYellowLight().setLightOn(false);
tsPanel.getGreenLight().setLightOn(false);
}
});
panel.add(stopButton);
setButtonSizes(startButton, stopButton);
}
protected void setButtonSizes(JButton ... buttons) {
Dimension preferredSize = new Dimension();
for (JButton button : buttons) {
Dimension d = button.getPreferredSize();
preferredSize = setLarger(preferredSize, d);
}
for (JButton button : buttons) {
button.setPreferredSize(preferredSize);
}
}
protected Dimension setLarger(Dimension a, Dimension b) {
Dimension d = new Dimension();
d.height = Math.max(a.height, b.height);
d.width = Math.max(a.width, b.width);
return d;
}
public void setTrafficSignalPanel(TrafficSignalPanel tsPanel) {
this.tsPanel = tsPanel;
}
public JPanel getPanel() {
return panel;
}
}
The button actions were simple enough that I could keep them in the button panel. If you want, you can code separate action classes.
Finally, here's the code that runs the traffic light cycle. It's an extension of the Thread class, so it can be run in a separate thread from the GUI. It's always a good idea to do work in threads separate from the GUI thread.
package com.ggl.traffic.signal.thread;
import javax.swing.SwingUtilities;
import com.ggl.traffic.signal.model.TrafficSignalModel;
import com.ggl.traffic.signal.view.TrafficSignalLightPanel;
import com.ggl.traffic.signal.view.TrafficSignalPanel;
public class TrafficSignalCycle extends Thread {
protected boolean isRunning;
protected boolean isFinished;
protected TrafficSignalPanel tsPanel;
public TrafficSignalCycle(TrafficSignalPanel tsPanel) {
this.tsPanel = tsPanel;
this.isRunning = true;
this.isFinished = false;
}
#Override
public void run() {
while (isRunning) {
signalLightOn(tsPanel.getGreenLight(), TrafficSignalModel.GREEN_LIGHT_TIME);
signalLightOn(tsPanel.getYellowLight(), TrafficSignalModel.YELLOW_LIGHT_TIME);
signalLightOn(tsPanel.getRedLight(), TrafficSignalModel.RED_LIGHT_TIME);
}
this.isFinished = true;
}
protected void signalLightOn(TrafficSignalLightPanel light, int seconds) {
if (isRunning) {
setLightOn(light, true);
}
for (int i = 0; i < 1000 && isRunning; i++) {
try {
Thread.sleep(1L * seconds);
} catch (InterruptedException e) {
}
}
setLightOn(light, false);
}
protected void setLightOn(final TrafficSignalLightPanel light,
final boolean isLightOn) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
light.setLightOn(isLightOn);
}
});
}
public void stopRunning() {
this.isRunning = false;
while (!isFinished) {
try {
Thread.sleep(10L);
} catch (InterruptedException e) {
}
}
}
}
The method that actually changes the color of the signal light must execute in the Swing event thread. That's what the setLightOn method does by calling SwingUtilities.
The timing loop is a bit complicated because we want to be able to stop the thread in a few milliseconds. The isFinished boolean ensures that the thread is stopped completely, so that the lights can be set.
This is a fairly long answer, but I hope it's helpful to anyone creating a Swing GUI.
You shouldn't be looping within the UI thread, nor telling it to sleep. Fundamentally you should keep the UI thread as free as possible.
If you need something to occur on a regular basis in a Swing UI in the UI thread, use a Swing Timer.
It's unclear what you're doing in the "other stuff" however - it's possible that you should be doing that in a different thread entirely and using (say) an AtomicBoolean to indicate when you want to stop.
1. You should always keep the UI thread for UI work and Non-UI thread for Non-UI work.
2. In Java GUI, the main() is not Long lived, after assigning the construction of GUI to the Event Dispatcher Thread, the main() quits, and now its EDT's responsibility handle the GUI.
3. So when you click the buttons, and the work you are doing is doing some heavy process or its time consuming....then span a Separate thread.
4. You can use Thread or SwingWorker.
Example:
Button b = new Button("Click me");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
Thread t = new Thread(new Runnable(){
public void run(){
// Do the Heavy Processing work.....
}
});
t.start();
}
});
The easy but dirty way:
Multi-thread your program and have one thread do your loop and a second thread monitor your buttons. Have the button change your globalStop variable
The not so easy but cleaner way:
Make the button throw an interrupt to change the value. After the interrupt the for loop will continue to the end.
I had posted this in a wrong place (GameDev) and got no response there. So I'm posting it again here.
I'm making an applet game and it is rendering, the game loop is running, the animations are updating, but the keyboard input is not working. Here's an SSCCE.
public class Game extends JApplet implements Runnable {
public void init(){
// Initialize the game when called by browser
setFocusable(true);
requestFocus();
requestFocusInWindow(); // Always returning false
GInput.install(this); // Install the input manager for this class
new Thread(this).start();
}
public void run(){
startGameLoop();
}
}
And Here's the GInput class.
public class GInput implements KeyListener {
public static void install(Component c){
new GInput(c);
}
public GInput(Component c){
c.addKeyListener(this);
}
public void keyPressed(KeyEvent e){
System.out.println("A key has been pressed");
}
......
}
This is my GInput class. When run as an applet, it doesn't work and when I add the Game class to a frame, it works properly.
Thanks
Solved now. See my solution
One possible solution is to use the JApplet's contentPane, to set the focus on it rather than on the JApplet itself. But my preference is to use Key Bindings instead. You may need to use a Swing Timer for this to work:
My SSCCE:
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import javax.swing.*;
#SuppressWarnings("serial")
public class AppletKeyListen extends JApplet {
#Override
public void init() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
setFocusable(true);
int timerDelay = 100;
Timer myTimer = new Timer(timerDelay , new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
boolean focusObtained = requestFocusInWindow();
System.out.println("focusObtained for JApplet: " + focusObtained);
Container contentPane = getContentPane();
contentPane.setFocusable(true);
focusObtained = contentPane.requestFocusInWindow();
System.out.println("focusObtained for contentPane: " + focusObtained);
}
});
myTimer.setRepeats(false);
myTimer.start();
// boolean focusObtained = requestFocusInWindow();
// System.out.println("focusObtained: " + focusObtained);
//
// Container contentPane = getContentPane();
// contentPane.setFocusable(true);
//
// focusObtained = contentPane.requestFocusInWindow();
// System.out.println("focusObtained: " + focusObtained);
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
}
If you're running in a browser, you probably need to click on the applet to give it focus. For security reasons most browsers won't let an applet just grab the keyboard focus without the user clicking it.
So, I would add a mouse listener instead of doing the focus grabbing directly in init():
addMouseListener(new MouseAdapter() {
public void onMousePress(MouseEvent e) {
requestFocus();
}
});
Now that I have two options,
Use JWS
Don't make an applet mode
Now I had tried to make a new class called GApplet. It loads a game into a new JFrame which worked from the applet. Now I can access the fullscreen mode from web too. Here's a link to the class.
The GApplet class
And now it's working like the webstart and is actually an applet.