In addition to the explanation about Swing thread issues in MadProgrammer's answer I would recommend separating the gui from its control by implementing the MVC Pattern.
This offers better encapsulation, better separation of responsibilities, and makes it easier to use threads for off-edt processing.
Have a model that holds all the information that the view (gui) needs:
/*
* The model contains the information for the view and information from the view
* The model is independent of the user interface.
* It notifies Listener on changes.
*/
class Model {
private Listener listener;
private int x = 0, y = 0;
synchronized int getX() {return x;}
synchronized void setX(int x) { this.x = x; }
synchronized int getY() {return y;}
synchronized void setY(int y) { this.y = y; }
void setListener(Listener listener){
this.listener = listener;
}
//notify listener when changed
void notifyListener(){
if(listener != null) {
listener.onChange();
}
}
}
In this case synchronization was added to allow the model to be used by threads.
Listener is defined by :
/*
* A simple interface used to link View and Model
*/
interface Listener {
void onChange();
}
View is just that. It implements Listener
so it can listen to Model
changes:
/*
* View is just that: a dumb as possible display
*/
public class View implements Listener{
private final JButton button;
private final MyDrawPanel panel;
private final Model model;
public View(Model model) {
this.model = model;
panel = new MyDrawPanel();
button = new JButton("Restart");
panel.add(button);
}
public void go(){
JFrame frame = new JFrame("Balloon Balls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.add(panel);
frame.setVisible(true);
}
class MyDrawPanel extends JPanel{
@Override
public void paintComponent(Graphics g){
super.paintComponent(g); //always call super
g.fillOval(model.getX(), model.getY(), 30, 30);
g.setColor(Color.BLACK);
}
}
@Override
public void onChange() {
panel.repaint();
}
void addActionListener(ActionListener listener){
button.addActionListener(listener);
}
}
Putting it all together: see the following mvce : it adds a controller that controls the model and view.
For convenience and simplicity, the following code can be copy-pasted into one file called View.java
, and run.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
/*
* View is just that: a dumb as possible display
*/
public class View implements Listener{
private final JButton button;
private final MyDrawPanel panel;
private final Model model;
public View(Model model) {
this.model = model;
panel = new MyDrawPanel();
button = new JButton("Restart");
panel.add(button);
}
public void go(){
JFrame frame = new JFrame("Balloon Balls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.add(panel);
frame.setVisible(true);
}
class MyDrawPanel extends JPanel{
@Override
public void paintComponent(Graphics g){
super.paintComponent(g); //always call super
g.fillOval(model.getX(), model.getY(), 30, 30);
g.setColor(Color.BLACK);
}
}
@Override
public void onChange() {
panel.repaint();
}
void addActionListener(ActionListener listener){
button.addActionListener(listener);
}
public static void main(String[]args){
new Controller();
}
}
/*
* A simple interface used to link View and Model
*/
interface Listener {
void onChange();
}
/*
* The model contains the information for the view and information from the view
* The model is independent of the user interface.
* It notifies Listener on changes.
*/
class Model {
private Listener listener;
private int x = 0, y = 0;
synchronized int getX() {return x;}
synchronized void setX(int x) { this.x = x; }
synchronized int getY() {return y;}
synchronized void setY(int y) { this.y = y; }
void setListener(Listener listener){
this.listener = listener;
}
//notify listener when changed
void notifyListener(){
if(listener != null) {
listener.onChange();
}
}
}
/*
* The controller "wires" the view and model, and does the processing.
*/
class Controller implements ActionListener{
private final Model model;
private final View view;
public Controller() {
model = new Model();
view = new View(model);
model.setListener(view);
view.addActionListener(this);
view.go();
}
@Override
public void actionPerformed (ActionEvent e){
new Thread(()->{
for(int i=0;i<130;i++){
model.setX(model.getX()+1);
model.setY(model.getY()+1);
model.notifyListener();
System.out.println(model.getX()+" - "+ model.getY());
try {
Thread.sleep(100);
} catch(Exception ex) { }
}
}).start();
}
}