Swing is NOT thread safe, this means you should never update or modify the UI in anyway other then from the context of the event dispatching thread. See Concurrency in Swing for more details.
For animation in Swing, consider using a javax.swing.Timer
.
The following example uses to javax.swing.Timer
s instead of the Thread
s. Normally, I prefer to use a single Timer
, as it reduces the overhead on the EDT, but I wanted to create a demonstration which mimicked your own example...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RunDrones {
public static int counterNum = 0;
final JFrame display;
private final JLabel counterStatus;
private final JLabel dialStatus;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
new RunDrones();
}
});
}
public RunDrones() {
display = new JFrame();
display.setLayout(new GridLayout());
dialStatus = new JLabel("OOPS");
dialStatus.setFont(new Font("Verdana", 1, 86));
display.add(dialStatus);
counterStatus = new JLabel("OOPS");
counterStatus.setFont(new Font("Verdana", 1, 86));
display.add(counterStatus);
display.setSize(800, 400);
display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
display.setLocationRelativeTo(null);
display.setVisible(true);
Timer dialTimer = new Timer(50, new Dial());
dialTimer.setInitialDelay(0);
Timer counterTimer = new Timer(50, new Counter());
dialTimer.start();
counterTimer.start();
}
public static int getRandom() {
int firstNum = new Random().nextInt(5) + 1;
int secondNum = new Random().nextInt(5) + 1;
return firstNum - secondNum;
}
public class Dial implements ActionListener {
private int dialNum;
@Override
public void actionPerformed(ActionEvent e) {
if (dialNum >= -85 && dialNum <= 60) {
dialNum += getRandom();
dialStatus.setText(Integer.toString(dialNum));
} else {
((Timer) e.getSource()).stop();
dialStatus.setText("ALARM!!");
}
}
}
public class Counter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (counterNum >= -75 && counterNum <= 75) {
counterNum += getRandom();
counterStatus.setText(Integer.toString(counterNum));
} else {
((Timer) e.getSource()).stop();
counterStatus.setText("ALARM!!");
}
}
}
}
You may also want to have a read through Performing Custom Painting
Updated with rough example
This is a rough example with painting of a rectangle...Now, personally, if I was doing this, I would establish some kind of range model and generate a percentage value from it which could be used to display the rectangle over a given viewable area, it would just make life easier, but that's me...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RunDrones {
public static int counterNum = 0;
final JFrame display;
private final JLabel counterStatus;
private RectanglePane dialPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
new RunDrones();
}
});
}
public RunDrones() {
display = new JFrame();
display.setLayout(new GridLayout());
dialPane = new RectanglePane();
display.add(dialPane);
counterStatus = new JLabel("OOPS");
counterStatus.setFont(new Font("Verdana", 1, 86));
display.add(counterStatus);
display.setSize(800, 400);
display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
display.setLocationRelativeTo(null);
display.setVisible(true);
Timer dialTimer = new Timer(50, new Dial(dialPane));
dialTimer.setInitialDelay(0);
Timer counterTimer = new Timer(50, new Counter());
dialTimer.start();
counterTimer.start();
}
public static int getRandom() {
int firstNum = new Random().nextInt(5) + 1;
int secondNum = new Random().nextInt(5) + 1;
return firstNum - secondNum;
}
public class RectanglePane extends JPanel {
private int yPos;
public void setYPos(int yPos) {
this.yPos = yPos;
repaint();
}
public int getYPos() {
return yPos;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
int y = (getHeight() / 2) - 85;
int x = (getWidth() - 20) / 2;
g.drawRect(x, y, 40, 165);
// y = (getHeight() + getYPos()) / 2;
y = (getHeight() / 2) + getYPos();
g.setColor(getForeground());
g.fillRect(x, y, 40, 40);
}
}
public class Dial implements ActionListener {
private int dialNum;
private RectanglePane pane;
public Dial(RectanglePane pane) {
this.pane = pane;
pane.setForeground(Color.GREEN);
}
@Override
public void actionPerformed(ActionEvent e) {
if (dialNum >= -85 && dialNum <= 60) {
dialNum += getRandom();
pane.setYPos(dialNum);
} else {
pane.setForeground(Color.RED);
((Timer) e.getSource()).stop();
}
}
}
public class Counter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (counterNum >= -75 && counterNum <= 75) {
counterNum += getRandom();
counterStatus.setText(Integer.toString(counterNum));
} else {
((Timer) e.getSource()).stop();
counterStatus.setText("ALARM!!");
}
}
}
}
Example of a simple RangeModel
public class RangeModel {
private final int min;
private final int max;
private int value;
public RangeModel(int min, int max) {
this.min = min;
this.max = max;
}
public int getMin() {
return min;
}
public int getMax() {
return max;
}
public int getValue() {
return value;
}
public void setValue(int newValue) {
newValue = Math.max(getMin(), newValue);
newValue = Math.min(getMax(), newValue);
this.value = newValue;
}
public double getProgress() {
int range = getMax() - getMin();
int normalValue = getValue() - getMin();
return (double)normalValue / ((double)range - 1);
}
}
And an example use...
RangelModel model = new RangelModel(-85, 85);
for (int i = model.getMin(); i < model.getMax(); i++) {
model.setValue(i);
System.out.println(NumberFormat.getPercentInstance().format(model.getProgress()));
}