I have a minimal app to show Java Swing GUI update problem, seems it's not painting the components properly, the errors look like this :
Here is my program :
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import java.util.concurrent.*;
public class Java_Test extends JPanel
{
static JFrame frame=new JFrame("Java Test");
static int W=800,H=260,Executor_Count=12;
JPanel Progress_Panel=new JPanel(),Center_Panel;
JButton Do_Test_Button=new JButton("Do Test");
Get_Time Timer=new Get_Time();
ThreadPoolExecutor executor;
Progress_Bar Progress_bar;
Timer Display_Timer=new Timer(1);
public Java_Test()
{
setLayout(new BorderLayout());
JPanel Top_Panel=new JPanel();
Top_Panel.setPreferredSize(new Dimension(W-6,60));
add("North",Top_Panel);
Do_Test_Button.setFont(new Font("Times New Roman",0,16));
Do_Test_Button.setBackground(new Color(118,198,250));
Do_Test_Button.setForeground(new Color(0,28,218));
Do_Test_Button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { Do_Test(); } });
Top_Panel.add(Do_Test_Button);
JPanel Center_Panel=new JPanel();
Center_Panel.setPreferredSize(new Dimension(W-2,170));
add("Center",Center_Panel);
Progress_Panel.setPreferredSize(new Dimension(W-2,160));
Center_Panel.add(Progress_Panel);
JLabel Progress_Label=new JLabel("Progress");
Progress_Label.setFont(new Font("Times New Roman",0,20));
Progress_Label.setBackground(new Color(253,253,253));
Progress_Label.setForeground(new Color(8,68,128));
Progress_Label.setPreferredSize(new Dimension(W-20,53));
Progress_Label.setHorizontalAlignment(SwingConstants.CENTER);
Progress_Panel.add(Progress_Label);
Progress_bar=new Progress_Bar(620,26);
Progress_Panel.add(Progress_bar);
JPanel Time_Used_Panel=new JPanel(new FlowLayout(FlowLayout.CENTER,6,26));
Time_Used_Panel.setPreferredSize(new Dimension(W-20,60));
Progress_Panel.add(Time_Used_Panel);
JLabel Time_Used_Label=new JLabel("Time : ");
Time_Used_Label.setFont(new Font("Times New Roman",0,14));
Time_Used_Label.setForeground(new Color(0,0,238));
Time_Used_Label.setHorizontalAlignment(SwingConstants.CENTER);
Time_Used_Panel.add(Time_Used_Label);
Display_Timer.setFont(new Font("Times New Roman",0,14));
Display_Timer.setForeground(new Color(0,0,238));
Display_Timer.setPreferredSize(new Dimension(50,17));
Time_Used_Panel.add(Display_Timer);
Clock clock=new Clock(0);
clock.setFont(new Font("Monospaced",Font.PLAIN,16));
clock.setBackground(new Color(0,110,220));
clock.setForeground(new Color(250,250,250));
clock.setOpaque(true);
clock.setPreferredSize(new Dimension(288,30));
clock.start();
JPanel Bottom_Panel=new JPanel();
Bottom_Panel.setPreferredSize(new Dimension(W-2,50));
Bottom_Panel.add(clock);
add("South",Bottom_Panel);
setPreferredSize(new Dimension(W,H));
}
void Do_Test()
{
String Info="",Result;
Out("Do_Test");
try
{
Display_Timer.start();
Timer.Start();
Output_Time("[ 1 ]");
Progress_bar.Set_Progress(1);
int Task_Count=222;
executor=new ThreadPoolExecutor(Executor_Count,Executor_Count*2,1,TimeUnit.SECONDS,new LinkedBlockingQueue());
ArrayList<Future<String>> futures=new ArrayList<>(Task_Count);
Test_Runner A_Runner;
try
{
for (int i=0;i<Task_Count;i++)
{
A_Runner=new Test_Runner();
futures.add(executor.submit(A_Runner));
}
executor.shutdown();
while (!executor.isTerminated()) { executor.awaitTermination(100,TimeUnit.MILLISECONDS); }
for (Future<String> future : futures)
{
Result=future.get();
if (Result!=null) Info+=Result;
}
}
catch (Exception e) { e.printStackTrace(); }
}
catch (Exception e) { e.printStackTrace(); }
Output_Time("[ 2 ]");
Progress_bar.Set_Progress(100);
Out("Done");
Display_Timer.stop();
}
String Output_Time(String Id)
{
Timer.End();
String Time_Duration=Id+" : Time = "+Timer.Get_Duration_Hour_Minute_Second();
Out(Time_Duration);
return Time_Duration;
}
private static void Out(String message) { System.out.println(message); }
static void Create_And_Show_GUI()
{
final Java_Test demo=new Java_Test();
frame.add(demo);
frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } });
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Create_And_Show_GUI(); } }); }
}
class Progress_Bar extends JPanel implements Runnable
{
int W,H,Last_Progress=-99,Progress,counter=0,Unit_Size=20;
static JProgressBar b=new JProgressBar();
boolean Started_B=false,Do_Step_B=false;
Thread Progress_Bar_Runner_Thread;
public Progress_Bar(int W,int H)
{
setPreferredSize(new Dimension(W,H));
b.setPreferredSize(new Dimension(W-20,H-8));
b.setStringPainted(true);
add(b);
start();
}
public void Set_Progress(int Progress)
{
if (Progress==1 || (this.Progress<Progress && Progress<=100))
{
this.Progress=Progress;
b.setValue(Progress);
b.paintImmediately(0,0,b.getWidth(),b.getHeight());
Out(" Progress = "+Progress+" %");
}
if (Progress==1) Started_B=true;
else if (Progress>=100)
{
Started_B=false;
Do_Step_B=false;
}
}
public void run()
{
try
{
while (Progress<=100)
{
if ((Progress==0 || Progress==50 || Progress==100 || Do_Step_B) && Last_Progress!=Progress)
{
b.setValue(Progress);
// revalidate();
b.paintImmediately(0,0,b.getWidth(),b.getHeight());
Last_Progress=Progress;
}
Thread.sleep(200); // Delay the thread
Do_Step_B=(Started_B && (counter++ % Unit_Size ==0));
if (Progress<100 && Do_Step_B) Progress++;
}
}
catch (Exception e) { e.printStackTrace(); }
}
public void start()
{
if (Progress_Bar_Runner_Thread==null)
{
Progress_Bar_Runner_Thread=new Thread(this);
Progress_Bar_Runner_Thread.setPriority(Thread.NORM_PRIORITY);
Progress_Bar_Runner_Thread.start();
}
}
public void stop() { if (Progress_Bar_Runner_Thread!=null) Progress_Bar_Runner_Thread=null; }
private static void Out(String message) { System.out.println(message); }
}
class Test_Runner implements Callable<String>
{
int Start_Index=0,End_Index=6999;
StringBuilder StrBdr=new StringBuilder();
public Test_Runner() { }
public String call() throws InterruptedException
{
try { for (int i=Start_Index;i<End_Index;i++) StrBdr.append("Test_Runner + Test_Runner + Test_Runner + Test_Runner + Test_Runner + Test_Runner"); }
catch (Exception e) {}
return StrBdr.toString();
}
}
class Timer extends JLabel implements Runnable
{
public static final long serialVersionUID=26362862L;
private Thread Timer_Thread;
String Time_Text="";
int updateInterval=1000,Format=0;
Get_Time Timer=new Get_Time();
public Timer()
{
setFont(new Font("Monospaced",Font.PLAIN,16));
setVerticalAlignment(SwingConstants.CENTER);
setHorizontalAlignment(SwingConstants.CENTER);
}
public Timer(int Format) { this.Format=Format; }
public void start()
{
Timer.Start();
if (Timer_Thread==null)
{
Timer_Thread=new Thread(this);
Timer_Thread.setPriority(Thread.NORM_PRIORITY);
Timer_Thread.start();
}
}
public void run()
{
Thread myThread=Thread.currentThread();
while (Timer_Thread==myThread)
{
switch (Format)
{
case 1 : Time_Text=Timer.Get_Duration_Hour_Minute_Second();break;
}
setText(Time_Text);
paintImmediately(0,0,getWidth(),getHeight());
revalidate();
try { Thread.sleep(updateInterval); }
catch (InterruptedException e) { }
}
}
public void stop() { if (Timer_Thread != null) Timer_Thread=null; }
}
class Clock extends JLabel implements Runnable
{
public static final long serialVersionUID=26362862L;
private Thread clockThread;
String Time_Text="";
int updateInterval=1000,Format=0;
Get_Time Timer=new Get_Time();
public Clock() { start(); }
public Clock(int Format)
{
this.Format=Format;
start();
}
public void start()
{
setVerticalAlignment(SwingConstants.CENTER);
setHorizontalAlignment(SwingConstants.CENTER);
if (clockThread==null)
{
clockThread=new Thread(this);
// clockThread.setPriority(Thread.NORM_PRIORITY);
clockThread.setPriority(Thread.MIN_PRIORITY);
clockThread.start();
}
}
public void run()
{
Thread myThread=Thread.currentThread();
while (clockThread==myThread)
{
switch (Format)
{
case 0 : Time_Text=" "+new java.util.Date().toString().substring(0,19)+" ";break;
}
setText(Time_Text);
paintImmediately(0,0,getWidth(),getHeight());
revalidate();
try { Thread.sleep(updateInterval); }
catch (InterruptedException e) { }
}
}
public void stop() { if (clockThread != null) clockThread=null; }
}
class Get_Time
{
private long start,end;
public int Hours,Minutes,Seconds,Total_Seconds;
String ST_Hours,ST_Minutes,ST_Seconds;
public Get_Time() { Reset(); }
public void Start() { start=System.currentTimeMillis(); }
public void End()
{
int half_second;
end=System.currentTimeMillis();
Total_Seconds=(int)(end-start)/1000;
half_second=(int)(end-start)%1000;
if (half_second>499) Total_Seconds++;
Hours=Total_Seconds/3600;
Minutes=(Total_Seconds%3600)/60;
Seconds=(Total_Seconds%3600)%60;
ST_Hours=new String((Hours>9)?""+Hours:"0"+Hours);
ST_Minutes=new String((Minutes>9)?""+Minutes:"0"+Minutes);
ST_Seconds=new String((Seconds>9)?""+Seconds:"0"+Seconds);
}
public String Get_Duration_Hour_Minute_Second()
{
End();
return ST_Hours+":"+ST_Minutes+":"+ST_Seconds;
}
public void Reset() { start=0;end=0; }
}
My computer has a 12 core 3.33 GH Intel Corei7 x 980 CPU, with 24 GB of RAM, running on Windows 7, Java Version = 1.8.0_212
The question is how to fix it ?
Enumeration of Swing threading and painting problems:
All of the following code is called from the Swing event dispatch thread (EDT), and so far so good....
void Do_Test() {
String Info = "", Result;
Out("Do_Test");
try {
Display_Timer.start();
Timer.Start();
Output_Time("[ 1 ]");
Progress_bar.Set_Progress(1); // OK to call this on the event thread
int Task_Count = 222;
executor = new ThreadPoolExecutor(Executor_Count, Executor_Count * 2, 1,
TimeUnit.SECONDS, new LinkedBlockingQueue());
ArrayList<Future<String>> futures = new ArrayList<>(Task_Count);
Test_Runner A_Runner;
try {
for (int i = 0; i < Task_Count; i++) {
A_Runner = new Test_Runner();
futures.add(executor.submit(A_Runner));
}
executor.shutdown();
OK, here we start seeing problems as you're calling a potentially long while loop on the EDT, potentially blocking it:
while (!executor.isTerminated()) {
executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
Same here, as the call to Future#get() is a blocking call:
for (Future<String> future : futures) {
Result = future.get();
if (Result != null)
Info += Result;
}
OK, now we start seeing some truly unusual Swing code where you make Swing calls, including setting a JProgressBar's value off the EDT and then try to force the EDT to do some painting via paintImmediately(....). Making calls off the EDT can result in intermittent and unpredictable errors, and can cause the painting to mess up, and the GUI to sometimes freeze:
class Progress_Bar extends JPanel implements Runnable {
// .....
public void Set_Progress(int Progress) {
if (Progress == 1 || (this.Progress < Progress && Progress <= 100)) {
this.Progress = Progress; // ****** being called off the EDT
b.paintImmediately(0, 0, b.getWidth(), b.getHeight()); // ***** Don't do this
Out(" Progress = " + Progress + " %");
}
// .....
}
public void run() {
try {
while (Progress <= 100) {
if ((Progress == 0 || Progress == 50 || Progress == 100 || Do_Step_B)
&& Last_Progress != Progress) {
// ***** called off the EDT -- don't do this
b.setValue(Progress);
// revalidate();
b.paintImmediately(0, 0, b.getWidth(), b.getHeight()); // **** and don't do this
Last_Progress = Progress;
}
Thread.sleep(200); // Delay the thread
Do_Step_B = (Started_B && (counter++ % Unit_Size == 0));
if (Progress < 100 && Do_Step_B)
Progress++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// .......
}
Similar strange goings-ons in this class, the Timer class, where you set a JLabel's text off of the EDT:
class Timer extends JLabel implements Runnable {
// ....
public void run() {
Thread myThread = Thread.currentThread();
while (Timer_Thread == myThread) {
switch (Format) {
case 1:
Time_Text = Timer.Get_Duration_Hour_Minute_Second();
break;
}
// again this is dangerous code **********
setText(Time_Text);
paintImmediately(0, 0, getWidth(), getHeight());
// ....
Same for the Clock class...
Solutions:
To repeatedly call code in a Swing GUI, use a javax.swing.Timer or "Swing Timer". For an example of use of this, please see my implementation of your MCVE, but using a Swing Timer for your threading code above.
The other code, the one that calls long-running tasks, should be done within a SwingWorker. This worker thread usually communicates with the Swing GUI in one of two ways (or both) -- either using a publish/process method pair, or (as in the example below), using a PropertyChangeListener attached to the Worker. Workers have several "bound" properties, fields that notify listeners of change, including the progress property, that can hold a value from 0 to 100, and the SwingWorker.StateValue, or "state" property:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
#SuppressWarnings("serial")
public class JavaTest2 extends JPanel {
private static final int W = 800;
private static final int H = 260;
private Action doTestAction = new DoTestAction("Do Test");
private JProgressBar progressBar = new JProgressBar(0, 100);
private MyClockPanel clockPanel = new MyClockPanel();
private MyTimerPanel timerPanel = new MyTimerPanel();
public JavaTest2() {
JPanel topPanel = new JPanel();
topPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 35, 5));
topPanel.add(new JButton(doTestAction));
progressBar.setStringPainted(true);
JPanel progressPanel = new JPanel(new GridBagLayout());
progressPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0;
progressPanel.add(progressBar, gbc);
JLabel progressLabel = new JLabel("Progress", SwingConstants.CENTER);
progressLabel.setFont(new Font("Times New Roman", 0, 20));
progressLabel.setForeground(new Color(8, 68, 128));
JPanel centralPanel = new JPanel(new BorderLayout(5, 5));
centralPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
centralPanel.add(progressLabel, BorderLayout.PAGE_START);
centralPanel.add(progressPanel);
JPanel clockWrapper = new JPanel();
clockWrapper.add(clockPanel);
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.PAGE_AXIS));
bottomPanel.add(timerPanel, BorderLayout.PAGE_START);
bottomPanel.add(clockWrapper, BorderLayout.PAGE_END);
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
add(topPanel, BorderLayout.PAGE_START);
add(bottomPanel, BorderLayout.PAGE_END);
add(centralPanel);
}
#Override
public Dimension getPreferredSize() {
Dimension superSize = super.getPreferredSize();
if (isPreferredSizeSet()) {
return superSize;
} else {
int w = Math.max(superSize.width, W);
int h = Math.max(superSize.height, H);
return new Dimension(w, h);
}
}
private class DoTestAction extends AbstractAction {
private MyWorker myWorker = null;
public DoTestAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
if (myWorker != null && myWorker.getState() == SwingWorker.StateValue.STARTED) {
return; // still running
}
timerPanel.start();
progressBar.setValue(0);
myWorker = new MyWorker();
myWorker.addPropertyChangeListener(new WorkerListener());
myWorker.execute();
setEnabled(false);
}
}
class WorkerListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
// if the worker is changing its progress bound property:
if (evt.getPropertyName().equals("progress")) {
int progress = (int) evt.getNewValue();
// just for safety's sake, limit progress to 100 and no more
progress = Math.min(progress, 100);
progressBar.setValue(progress);
} else if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
// else if worker is done
try {
// get the result to at least trap errors
String result = ((MyWorker) evt.getSource()).get();
// can display result in the GUI
timerPanel.stop();
} catch (Exception e) {
// worker's exception is available to the GUI if desired here
e.printStackTrace();
}
progressBar.setValue(100);
doTestAction.setEnabled(true);
}
}
}
private static class MyWorker extends SwingWorker<String, Void> {
private static final int EXECUTOR_COUNT = 12;
private static final int TASK_COUNT = 222;
#Override
protected String doInBackground() throws Exception {
ExecutorService executor = new ThreadPoolExecutor(EXECUTOR_COUNT, EXECUTOR_COUNT * 2, 1,
TimeUnit.SECONDS, new LinkedBlockingQueue<>());
List<Future<String>> futures = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < TASK_COUNT; i++) {
Callable<String> aRunner = new ARunner();
futures.add(executor.submit(aRunner));
}
executor.shutdown();
int index = 0;
for (Future<String> future : futures) {
String result = future.get();
sb.append(result);
sb.append(" ");
index++;
int progress = (100 * index) / TASK_COUNT;
progress = Math.min(progress, 100);
setProgress(progress);
}
return sb.toString();
}
}
private static class ARunner implements Callable<String> {
private static final long SLEEP_TIME = 800;
#Override
public String call() throws Exception {
TimeUnit.MILLISECONDS.sleep(SLEEP_TIME);
return "Foo";
}
}
private static void createAndShowGui() {
JavaTest2 mainPanel = new JavaTest2();
JFrame frame = new JFrame("Java Test 2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class MyClockPanel extends JPanel {
private static final Color FG = new Color(250, 250, 250);
private static final Color BG = new Color(0, 110, 220);
private static final int TIMER_DELAY = 200;
private static final Font FONT = new Font("Monospaced", Font.PLAIN, 16);
private JLabel clockLabel = new JLabel("", SwingConstants.CENTER);
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E MMM dd yyyy kk:mm:ss");
public MyClockPanel() {
setBackground(BG);
clockLabel.setForeground(FG);
clockLabel.setFont(FONT);
displayDateTime();
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(5, 50, 5, 50));
add(clockLabel);
new javax.swing.Timer(TIMER_DELAY, e -> {
displayDateTime();
}).start();
}
private void displayDateTime() {
LocalDateTime dateTime = LocalDateTime.now();
String text = dateTime.format(formatter);
clockLabel.setText(text);
}
}
#SuppressWarnings("serial")
class MyTimerPanel extends JPanel {
private static final Color FG = new Color(0, 0, 238);
private static final Font FONT = new Font("Times New Roman", 0, 14);
private static final int TIMER_DELAY = 40;
private static final String FORMAT_TXT = "Elapsed Time: %02d:%02d:%02d";
private JLabel timerLabel = new JLabel("", SwingConstants.CENTER);
private LocalDateTime startTime = null;
private Timer timer = null;
public MyTimerPanel() {
timerLabel.setForeground(FG);
timerLabel.setFont(FONT);
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(5, 50, 5, 50));
timerLabel.setText(String.format(FORMAT_TXT, 0, 0, 0));
add(timerLabel);
}
public void start() {
stop();
startTime = LocalDateTime.now();
timer = new Timer(TIMER_DELAY, e -> incrementTime());
timer.start();
}
public void stop() {
if (timer != null && timer.isRunning()) {
timer.stop();
}
}
private void incrementTime() {
LocalDateTime currentTime = LocalDateTime.now();
long hours = ChronoUnit.HOURS.between(startTime, currentTime);
long minutes = ChronoUnit.MINUTES.between(startTime, currentTime) % 60;
long seconds = ChronoUnit.SECONDS.between(startTime, currentTime) % 60;
String text = String.format(FORMAT_TXT, hours, minutes, seconds);
timerLabel.setText(text);
}
}
Related
I would like to implement a frame where you can press a button to make a call, and then either the respondent, the manager or the director would respond to the call, and it would output on the screen who answered the call.
If an employee is not available, I would like to show on the console. So for example if respondent and manager are not available, I would like the output to look like this:
Respondent busy. Forwarding to manager.
Manager busy. Forwarding to Director.
Director answered the call.
With my implementations these messages are shown, but they are all showed instantly. I would like to set some waiting time between the messages, like:
shows message: "Manager is busy. Forwarding to director"
waits 2 seconds
shows message: "Director answered the call"
CallHandler.java:
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public class CallHandler implements Runnable{
private ArrayList<Employee> employees = new ArrayList<>();
private MyFrame view;
public CallHandler(){
employees.add(new Respondent());
employees.add(new Manager());
employees.add(new Director());
view = new MyFrame(this);
}
public void dispatchCall() throws InterruptedException {
boolean dialing = true;
while(dialing) {
for (Employee employee : getAvailableEmployees()) {
Status status = employee.call();
if (status == Status.ANSWERED) {
(new Thread(employee)).start();
sendMessage(employee.getClass().getName() + " has answered the call.");
dialing = false;
break;
}
sendMessage(employee.getClass().getName() + " is busy. You will be redirected to the next available employee.");
}
if (dialing)
sendMessage("No one is available at the moment. Please wait...");
}
}
private void sendMessage(String msg) throws InterruptedException {
view.addMessage(msg);
}
private ArrayList<Employee> getAvailableEmployees(){
return employees.stream().filter(Employee::isFree).collect(Collectors.toCollection(ArrayList::new));
}
public static void main(String args[]){
CallHandler handler = new CallHandler();
}
#Override
public void run() {
try{
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
} catch (InterruptedException e) {
System.out.println("Call got interrupted");
}
}
}
MyFrame.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
public class MyFrame extends JFrame implements Runnable {
private static final int WIDTH = 800;
private static final int HEIGHT = 500;
private static final int PANEL_WIDTH = 600;
private static final int PANEL_HEIGHT = 290;
private final JPanel panel = new JPanel();
private final ArrayList<JLabel> labels = new ArrayList<>();
private int rowIndex = 0;
private static final int ROWS = 17;
private CallHandler handler = null;
private Timer timer;
MyFrame(CallHandler callHandler) {
handler = callHandler;
this.setLayout(null);
setVisible(true);
setSize(WIDTH, HEIGHT);
add(panel);
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.setBackground(Color.GREEN);
panel.setBounds(100, 50, PANEL_WIDTH, PANEL_HEIGHT);
JButton btn = new JButton("Make call");
btn.setBounds(350, 370, 100, 80);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
try {
handler.dispatchCall();
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
});
this.add(btn);
initiateRows();
}
public void addMessage(String msg) throws InterruptedException {
if (rowIndex < ROWS) {
getLabel(rowIndex).setText(msg);
rowIndex++;
} else {
System.out.println("removing first component");
removeComponent(0);
addRow(msg);
}
panel.updateUI();
// Thread.sleep(1000);
}
private void initiateRows() {
for (int i = 0; i < ROWS; i++) {
addRow("");
}
}
private void addRow(String msg) {
JLabel label = new JLabel(msg);
label.setAlignmentX(Component.CENTER_ALIGNMENT);
labels.add(label);
panel.add(label);
}
private JLabel getLabel(int i) {
return (JLabel) panel.getComponent(i);
}
private void removeComponent(int i) {
panel.remove(i);
}
public static void main(String args[]) {
MyFrame f = new MyFrame(new CallHandler());
}
#Override
public void run() {
try{
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
} catch (InterruptedException e) {
System.out.println("Call got interrupted");
}
}
}
I'm trying to make a counter that counts every 1/100 of a second until the program is closed, but it appears to be a little slower than that. Here's what I have:
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.FlowLayout;
import javax.swing.*;
public class counterTest extends JFrame
{
JLabel label;
long counter = 0;
String counterStr;
public counterTest()
{
super("counter");
ActionListener listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
counter++;
counterStr = Long.toString(counter);
label.setText(counterStr);
}
};
label = new JLabel();
setLayout(new FlowLayout());
setSize(100,100);
setResizable(false);
setVisible(true);
add(label);
javax.swing.Timer timer = new javax.swing.Timer(10, listener);
timer.setInitialDelay(0);
timer.start();
}
public static void main(String args[])
{
counterTest c = new counterTest();
c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
The "10" in the line where I declare "timer" should cause "listener" to run every 1/100 second, correct? It seems a bit slower... what is wrong here?
EDIT: Full code posted.
A Swing Timer is not built to be 100% accurate, and 1/100 may be pushing it's limits. Consider using the Swing Timer but displaying absolute time differences, calculated by getting the time.
e.g.,
private class MyTimerListener implements ActionListener {
private long startTime = 0;
public void reset() {
startTime = System.currentTimeMillis();
}
#Override
public void actionPerformed(ActionEvent e) {
long time = System.currentTimeMillis();
long delta = (time - startTime) / 10L;
label.setText(String.valueOf(delta));
}
}
OK, my mini-program:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyTimer2 extends JPanel implements GuiTimer {
private static final String TIME_FORMAT = "%03d:%03d";
private static final int EXTRA_WIDTH = 50;
private JLabel timerLabel = new JLabel();
private TimerControl timerControl = new TimerControl(this);
public MyTimer2() {
JPanel topPanel = new JPanel();
topPanel.add(new JLabel("Time:"));
topPanel.add(timerLabel);
JPanel centerPanel = new JPanel();
centerPanel.add(new JButton(timerControl.getStartAction()));
centerPanel.add(new JButton(timerControl.getStopAction()));
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(centerPanel, BorderLayout.CENTER);
setDeltaTime(0);
}
#Override
public void setDeltaTime(int delta) {
int secs = (int) delta / 1000;
int mSecs = (int) delta % 1000;
timerLabel.setText(String.format(TIME_FORMAT, secs, mSecs));
}
#Override
public Dimension getPreferredSize() {
Dimension superSz = super.getPreferredSize();
if (isPreferredSizeSet()) {
return superSz;
}
int prefW = superSz.width + EXTRA_WIDTH;
int prefH = superSz.height;
return new Dimension(prefW, prefH);
}
private static void createAndShowGui() {
MyTimer2 mainPanel = new MyTimer2();
JFrame frame = new JFrame("MyTimer2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
interface GuiTimer {
public abstract void setDeltaTime(int delta);
}
#SuppressWarnings("serial")
class TimerControl {
private static final int TIMER_DELAY = 10;
private long startTime = 0;
private long pauseTime = 0;
private Timer timer;
private GuiTimer gui;
private StartAction startAction = new StartAction();
private StopAction stopAction = new StopAction();
public TimerControl(GuiTimer gui) {
this.gui = gui;
}
public Action getStopAction() {
return stopAction;
}
public Action getStartAction() {
return startAction;
}
enum State {
START("Start", KeyEvent.VK_S),
PAUSE("Pause", KeyEvent.VK_P);
private String text;
private int mnemonic;
private State(String text, int mnemonic) {
this.text = text;
this.mnemonic = mnemonic;
}
public String getText() {
return text;
}
public int getMnemonic() {
return mnemonic;
}
};
private class StartAction extends AbstractAction {
private State state;
public StartAction() {
setState(State.START);
}
public final void setState(State state) {
this.state = state;
putValue(NAME, state.getText());
putValue(MNEMONIC_KEY, state.getMnemonic());
}
#Override
public void actionPerformed(ActionEvent e) {
if (state == State.START) {
if (timer != null && timer.isRunning()) {
return; // the timer's already running
}
setState(State.PAUSE);
if (startTime <= 0) {
startTime = System.currentTimeMillis();
timer = new Timer(TIMER_DELAY, new TimerListener());
} else {
startTime += System.currentTimeMillis() - pauseTime;
}
timer.start();
} else if (state == State.PAUSE) {
setState(State.START);
pauseTime = System.currentTimeMillis();
timer.stop();
}
}
}
private class StopAction extends AbstractAction {
public StopAction() {
super("Stop");
int mnemonic = KeyEvent.VK_T;
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
if (timer == null) {
return;
}
timer.stop();
startAction.setState(State.START);
startTime = 0;
}
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
long time = System.currentTimeMillis();
long delta = time - startTime;
gui.setDeltaTime((int) delta);
}
}
}
So far I have this:
The logic is that:
a.)I will press the keybutton 'S' then the game will start
b.)The JTextArea will show the conversation of the users(note: I didn't disable it for debugging purposes)
c.)The JTextField will be the field the user will type text.
I have these working code:
package game;
//import
public class Game extends JFrame {
public static final String SERVER_IP = "localhost";
public static final int WIDTH = 1200;
public static final int HEIGHT = 800;
public static final int SCALE = 1;
private final int FPS = 60;
private final long targetTime = 1000 / FPS;
private BufferedImage backBuffer;
public KeyboardInput input;
private Stage stage;
public String username = "";
public GameClient client;
public static Game game;
public static String message = "";
private Tank tank;
private javax.swing.JButton jButton1;
private javax.swing.JPanel jPanel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextArea jTextArea1;
private javax.swing.JTextField jTextField1;
public Game() throws HeadlessException {
setSize(1000, 1000);
addWindowListener(new WinListener());
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setResizable(false);
setVisible(true);
//setUndecorated(false);
addKeyListener(input);
setVisible(true);
}
public void init() {
this.game = this;
input = new KeyboardInput();
//this.setSize(WIDTH, HEIGHT);
//this.setLocationRelativeTo(null);
//this.setResizable(false);
Dimension expectedDimension = new Dimension(900, 50);
Dimension expectedDimension2 = new Dimension(100, 50);
jButton1 = new JButton("jButton1");
jTextArea1 = new JTextArea(6,6);
jTextArea1.setBounds(0,200,200,200);
jTextArea1.setBackground(Color.BLUE);
//jTextArea1.setFocusable(false);
jTextField1 = new JTextField("jTextField1");
JPanel panel2 = new JPanel();
panel2.setLayout(new FlowLayout());
panel2.add(jTextField1);
panel2.add(jButton1);
panel2.setBackground(Color.BLACK); // for debug only
panel2.setPreferredSize(expectedDimension);
panel2.setMaximumSize(expectedDimension);
panel2.setMinimumSize(expectedDimension);
jPanel1 = new JPanel();
jPanel1.setLayout(new BoxLayout(jPanel1, BoxLayout.Y_AXIS));
jPanel1.add(jTextArea1);
jPanel1.add(panel2);
jPanel1.setBackground(Color.RED); // for debug only
jPanel1.setPreferredSize(expectedDimension2);
jPanel1.setMaximumSize(expectedDimension2);
jPanel1.setMinimumSize(expectedDimension2);
jScrollPane1 = new JScrollPane(jPanel1,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
setContentPane(jScrollPane1);
System.out.println("init");
revalidate();
client = new GameClient(SERVER_IP, this);
backBuffer = new BufferedImage(800* SCALE,600 * SCALE, BufferedImage.TYPE_INT_RGB);
}
public Stage getStage() {
return stage;
}
public class WinListener extends WindowAdapter {
#Override
public void windowClosing(WindowEvent e) {
disconnect();
System.exit(0);
}
}
public void disconnect() {
Packet01Disconnect p = new Packet01Disconnect(username);
p.writeData(client);
client.closeSocket();
System.exit(0);
}
private Font font = new Font("Munro Small", Font.PLAIN, 96);
private Font font2 = new Font("Munro Small", Font.PLAIN, 50);
private Font fontError = new Font("Munro Small", Font.PLAIN, 25);
private int op = 0;
public void updateMenu() {
if (input.up.isPressed()) {
if (op == 1) {
op = 0;
} else {
op++;
}
input.up.toggle(false);
} else if (input.down.isPressed()) {
if (op == 0) {
op = 1;
} else {
op--;
}
input.down.toggle(false);
} else if (input.enter.isPressed() && op == 0) {
runningMenu = false;
input.enter.toggle(false);
} else if (input.enter.isPressed() && op == 1) {
System.exit(0);
}
}
public void drawMenu() {
Graphics g = getGraphics();
Graphics bbg = backBuffer.getGraphics();
bbg.setFont(font);
bbg.setColor(Color.white);
bbg.drawString("Sample", 189, 180);
bbg.setFont(font2);
if (op == 0) {
bbg.setColor(Color.red);
bbg.drawString("Start", 327, 378);
bbg.setColor(Color.white);
bbg.drawString("Quit", 342, 425);
} else if (op == 1) {
bbg.setColor(Color.white);
bbg.drawString("Start", 327, 378);
bbg.setColor(Color.red);
bbg.drawString("Quit", 342, 425);
}
g.drawImage(backBuffer, 0, 0, this);
}
public void draw() {
Graphics g = getGraphics();
Graphics bbg = backBuffer.getGraphics();
bbg.setColor(Color.black);
bbg.fillRect(0, 0, WIDTH, HEIGHT);
stage.drawStage(bbg, this);
for (Tank t : stage.getPlayers()) {
t.draw(bbg, SCALE, this);
}
g.drawImage(backBuffer, 0, 0, this);
}
public void update() {
tank.update(stage);
stage.update();
}
private long time = 0;
public void updateLogin() {
if (username.length() < 8) {
if (input.letter.isPressed()) {
username += (char) input.letter.getKeyCode();
input.letter.toggle(false);
}
}
if (input.erase.isPressed() && username.length() > 0) {
username = username.substring(0, username.length() - 1);
input.erase.toggle(false);
}
if (input.enter.isPressed() && username.length() > 0) {
input.enter.toggle(false);
time = System.currentTimeMillis();
Packet00Login packet = new Packet00Login(username, 0, 0, 0);
packet.writeData(client);
}
if (message.equalsIgnoreCase("connect server success")) {
time = 0;
runningLogin = false;
return;
}
if (message.equalsIgnoreCase("Username already exists")) {
drawLogin();
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
message = "";
username = "";
time = 0;
}
if (message.equalsIgnoreCase("Server full")) {
drawLogin();
try {
Thread.sleep(2000);
} catch (Exception e) {
}
System.exit(0);
}
if (time != 0 && message.equals("") && (System.currentTimeMillis() - time) >= 5000) {
message = "cannot connect to the server";
drawLogin();
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
}
message = "";
time = 0;
}
}
public void drawLogin() {
Graphics g = getGraphics();
Graphics bbg = backBuffer.getGraphics();
bbg.setColor(Color.black);
bbg.fillRect(0, 0, 800, 600);
bbg.setColor(Color.red);
bbg.setFont(fontError);
bbg.drawString(message, 100, 100);
bbg.setFont(font2);
bbg.setColor(Color.white);
bbg.drawString("Username", 284, 254);
bbg.setColor(Color.red);
bbg.drawString(username, 284, 304);
g.drawImage(backBuffer, 0, 0, this);
}
public static String waitPlayers = "Waiting for others players";
public String auxWaitPlayers = waitPlayers;
public static int quantPlayers = 0;
public class StringWait extends Thread {
public void run() {
while (true) {
try {
waitPlayers = "waiting for others players";
Thread.sleep(1000);
waitPlayers = "waiting for others players.";
Thread.sleep(1000);
waitPlayers = "waiting for others players..";
Thread.sleep(1000);
waitPlayers = "waiting for others players...";
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
}
}
}
public void updateWaitPlayers() {
if (quantPlayers == 1) {
runningWaitPlayer = false;
}
}
public void drawWaitPlayers() {
Graphics g = getGraphics();
Graphics bbg = backBuffer.getGraphics();
bbg.setColor(Color.black);
bbg.fillRect(0, 0, 800, 600);
bbg.setColor(Color.white);
bbg.setFont(fontError);
bbg.drawString(waitPlayers, 100, 100);
g.drawImage(backBuffer, 0, 0, this);
}
public boolean runningMenu = true, runningLogin = true, runningWaitPlayer = true, runningGame = true;
public int op2 = 0;
public void start() {
long start;
long elapsed;
long wait;
init();
while (true) {
runningGame = true;
runningMenu = true;
runningWaitPlayer = true;
runningLogin = true;
switch (op2) {
//..
}
}
public void setGameState(boolean state) {
//...
}
public static void main(String[] args) throws InterruptedException {
Game g = new Game();
Thread.sleep(1000);
g.start();
}
}
And these is my objective interface:
I hope someone will help me with my problem.
Set the "main" containers layout manager to BorderLayout
On to this, add the GameInterface in the BorderLayout.CENTER position
Create another ("interaction") container and set it's layout manager to BorderLayout, add this to the "main" container's BorderLayout.SOUTH position
Wrap the JTextArea in a JScrollPane and add it to the BorderLayout.CENTER position of your "interaction" container
Create another container ("message"), this could use a GridBagLayout. On to this add the JTextField (with GridBagConstraints#weightx set to 0 and GridBagConstraints#weightx set to 1) and add the button to the next cell (GridBagConstraints#gridx set to 1 and GridBagConstraints#weightx set to 0)
For more details, see:
Laying Out Components Within a Container
How to Use Borders
How to Use GridBagLayout
Note:
Graphics g = getGraphics(); is NOT how custom painting should be done. Instead, override the paintComponent of a component like JPanel and perform your custom painting there!
For more details see
Painting in AWT and Swing
Performing Custom Painting
Example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JPanel master = new JPanel(new BorderLayout());
master.setBackground(Color.BLUE);
JPanel gameInterface = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
};
gameInterface.setBackground(Color.MAGENTA);
master.add(gameInterface);
JPanel interactions = new JPanel(new BorderLayout());
interactions.add(new JScrollPane(new JTextArea(5, 20)));
JTextField field = new JTextField(15);
JButton btn = new JButton("Button");
JPanel message = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
message.add(field, gbc);
gbc.gridx = 1;
gbc.weightx = 0;
message.add(btn, gbc);
interactions.add(message, BorderLayout.SOUTH);
master.add(interactions, BorderLayout.SOUTH);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(master);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
I'm trying to learn SwingWorker and I have this simple code running and working, but I can't figure out how to prompt a JOptionPane.showMessageDialog when the task is complete. I've to tried to put
if (isDone())
JOptionPane.showMessageDialog(null, "Task Complete");
in different locations, but can't get anything to work. I've read that I may have to put it in the invokeLater() for it to run in the EDT, I'm not really sure how to accomplish that.
I tried to have my SwingWorker as a class member of my panel, but I can't instantiate it because it gets intantiaated in a listener. So I get a null pointer trying to put if (task.isDone()) in my invokeLater().
What is the proper way to accomplish this task?
I have an SSCCE here (all you do is enter a number and it prints my name that many times to a text area).
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.*;
public class RepeatNameSwingWorker extends JPanel {
private JProgressBar jpb = new JProgressBar(0, 100);
private JTextArea jtaNames = new JTextArea(20, 50);
private final JTextField jtfTimes = new JTextField(8);
private final JButton jbtExecute = new JButton("Execute");
private final JLabel jlblNumTimes = new JLabel("Enter number of times: ");
public RepeatNameSwingWorker(){
jpb.setStringPainted(true);
jtaNames.setLineWrap(true);
jtaNames.setWrapStyleWord(true);
JPanel panel = new JPanel();
panel.add(jlblNumTimes);
panel.add(jtfTimes);
panel.add(jbtExecute);
setLayout(new BorderLayout());
add(jpb, BorderLayout.NORTH);
add(new JScrollPane(jtaNames), BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
jbtExecute.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
RepeatNames task = new RepeatNames(
Integer.parseInt(jtfTimes.getText()), jtaNames);
task.addPropertyChangeListener(new PropertyChangeListener(){
#Override
public void propertyChange(PropertyChangeEvent e) {
if ("progress".equals(e.getPropertyName())) {
jpb.setValue((Integer)e.getNewValue());
}
}
});
task.execute();
if (task.isDone())
JOptionPane.showMessageDialog(null, "Task Complete");
}
});
}
static class RepeatNames extends SwingWorker<String, String>{
int times;
JTextArea result;
public RepeatNames(int times, JTextArea result) {
this.times = times;
this.result = result;
}
#Override
protected String doInBackground(){
publishNames(times);
return null;
}
#Override
protected void done() {
try {
result.append(get().toString()); // Display in text field
} catch (Exception ex) {
result.append(ex.getMessage());
}
}
#Override
protected void process(List<String> list) {
for (int i = 0; i < list.size(); i++) {
result.append(list.get(i) + " ");
}
}
private void publishNames(int n) {
int count = 0;
int number = 2;
while (count <= n) {
if (isPrime(number)) {
count++;
setProgress(100 * count / n);
publish("Paul");
}
number++;
}
}
private static boolean isPrime(int number) {
for (int divisor = 2; divisor <= number / 2; divisor++) {
if (number % divisor == 0) {
return false;
}
}
return true;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new RepeatNameSwingWorker());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
}
});
}
}
, but I can't figure out how to prompt a JOptionPane.showMessageDialog when the task is complete
Show the JOptionPane in the done() method.
This is the complete code :
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.Thread;
class jProgressBar {
JProgressBar pb;
JButton start;
int i;
jProgressBar() {
buildGUI();
hookUpEvents();
}
public void buildGUI() {
JFrame fr=new JFrame("Progress Bar");
JPanel p=new JPanel();
p.setLayout(new FlowLayout(FlowLayout.CENTER));
JPanel barPanel=new JPanel();
barPanel.setLayout(new GridLayout(2,0,50,50));
pb=new JProgressBar(0,10);
start=new JButton("Start Demo");
fr.add(p);
barPanel.add(start);
barPanel.add(pb);
p.add(barPanel);
fr.setSize(500,500);
fr.setVisible(true);
}
public void hookUpEvents() {
start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
try {
Runnable r=new Runnable() {
public void run() {
action(ae); // LINE 39
}
};
Thread th=new Thread(r);
th.start();
} catch(Exception exc) {
System.out.println(exc);
}
}
});
}
public void action(ActionEvent ae) {
start.setVisible(false);
try {
Runnable rp=new Runnable() {
public void run() {
i++;
pb.setValue(i);
try {
Thread.sleep(2000);
} catch(Exception exc) {
System.out.println(exc);
}
if(i==5) {
pb.setString("Half Done!");
}
else if(i==10) {
pb.setString("Completed!");
}
}
};
Thread th=new Thread(rp);
th.start();
} catch(Exception exc) {
System.out.println(exc);
}
}
public static void main(String args[]) {
new jProgressBar();
}
}
This is the error produced on cmd:
d:\UnderTest>javac jProgressBar.java
jProgressBar.java:39: local variable ae is accessed from within inner class; needs to be declared fina
l
action(ae);
^
1 error
What is this error and how can I solve this error?
Declare the variable ae as final:
public void actionPerformed(final ActionEvent ae) {
This means that it cannot be assigned a new value, which should be fine according to your current code.
a very nice example for SwingWorker
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
public class SwingWorkerExample extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private final JButton startButton, stopButton;
private JScrollPane scrollPane = new JScrollPane();
private JList listBox = null;
private DefaultListModel listModel = new DefaultListModel();
private final JProgressBar progressBar;
private mySwingWorker swingWorker;
public SwingWorkerExample() {
super("SwingWorkerExample");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setLayout(new GridLayout(2, 2));
startButton = makeButton("Start");
stopButton = makeButton("Stop");
stopButton.setEnabled(false);
progressBar = makeProgressBar(0, 99);
listBox = new JList(listModel);
scrollPane.setViewportView(listBox);
getContentPane().add(scrollPane);
//Display the window.
pack();
setVisible(true);
}
//Class SwingWorker<T,V> T - the result type returned by this SwingWorker's doInBackground
//and get methods V - the type used for carrying out intermediate results by this SwingWorker's
//publish and process methods
private class mySwingWorker extends javax.swing.SwingWorker<ArrayList<Integer>, Integer> {
//The first template argument, in this case, ArrayList<Integer>, is what s returned by doInBackground(),
//and by get(). The second template argument, in this case, Integer, is what is published with the
//publish method. It is also the data type which is stored by the java.util.List that is the parameter
//for the process method, which recieves the information published by the publish method.
#Override
protected ArrayList<Integer> doInBackground() {
//Returns items of the type given as the first template argument to the SwingWorker class.
if (javax.swing.SwingUtilities.isEventDispatchThread()) {
System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() returned true.");
}
Integer tmpValue = new Integer(1);
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) { //find every 100th prime, just to make it slower
tmpValue = FindNextPrime(tmpValue.intValue());
//isCancelled() returns true if the cancel() method is invoked on this class. That is the proper way
//to stop this thread. See the actionPerformed method.
if (isCancelled()) {
System.out.println("SwingWorker - isCancelled");
return list;
}
}
//Successive calls to publish are coalesced into a java.util.List, which is what is received by process,
//which in this case, isused to update the JProgressBar. Thus, the values passed to publish range from
//1 to 100.
publish(new Integer(i));
list.add(tmpValue);
}
return list;
}//Note, always use java.util.List here, or it will use the wrong list.
#Override
protected void process(java.util.List<Integer> progressList) {
//This method is processing a java.util.List of items given as successive arguments to the publish method.
//Note that these calls are coalesced into a java.util.List. This list holds items of the type given as the
//second template parameter type to SwingWorker. Note that the get method below has nothing to do with the
//SwingWorker get method; it is the List's get method. This would be a good place to update a progress bar.
if (!javax.swing.SwingUtilities.isEventDispatchThread()) {
System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false.");
}
Integer percentComplete = progressList.get(progressList.size() - 1);
progressBar.setValue(percentComplete.intValue());
}
#Override
protected void done() {
System.out.println("doInBackground is complete");
if (!javax.swing.SwingUtilities.isEventDispatchThread()) {
System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false.");
}
try {
//Here, the SwingWorker's get method returns an item of the same type as specified as the first type parameter
//given to the SwingWorker class.
ArrayList<Integer> results = get();
for (Integer i : results) {
listModel.addElement(i.toString());
}
} catch (Exception e) {
System.out.println("Caught an exception: " + e);
}
startButton();
}
boolean IsPrime(int num) { //Checks whether a number is prime
int i;
for (i = 2; i <= num / 2; i++) {
if (num % i == 0) {
return false;
}
}
return true;
}
protected Integer FindNextPrime(int num) { //Returns next prime number from passed arg.
do {
if (num % 2 == 0) {
num++;
} else {
num += 2;
}
} while (!IsPrime(num));
return new Integer(num);
}
}
private JButton makeButton(String caption) {
JButton b = new JButton(caption);
b.setActionCommand(caption);
b.addActionListener(this);
getContentPane().add(b);
return b;
}
private JProgressBar makeProgressBar(int min, int max) {
JProgressBar progressBar1 = new JProgressBar();
progressBar1.setMinimum(min);
progressBar1.setMaximum(max);
progressBar1.setStringPainted(true);
progressBar1.setBorderPainted(true);
getContentPane().add(progressBar1);
return progressBar1;
}
private void startButton() {
startButton.setEnabled(true);
stopButton.setEnabled(false);
System.out.println("SwingWorker - Done");
}
#Override
public void actionPerformed(ActionEvent e) {
if ("Start" == null ? e.getActionCommand() == null : "Start".equals(e.getActionCommand())) {
startButton.setEnabled(false);
stopButton.setEnabled(true);
// Note that it creates a new instance of the SwingWorker-derived class. Never reuse an old one.
(swingWorker = new mySwingWorker()).execute(); // new instance
} else if ("Stop" == null ? e.getActionCommand() == null : "Stop".equals(e.getActionCommand())) {
startButton.setEnabled(true);
stopButton.setEnabled(false);
swingWorker.cancel(true); // causes isCancelled to return true in doInBackground
swingWorker = null;
}
}
public static void main(String[] args) {
// Notice that it kicks it off on the event-dispatching thread, not the main thread.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
SwingWorkerExample swingWorkerExample = new SwingWorkerExample();
}
});
}
}
There are some counterproductive issues present.
Swing is single-thread based, and all actions must be done on the EDT. For that reason, your JProgressBar doesn't update correctly. See also Concurrency in Swing.
Don't use Thread.sleep(int) in Swing, and certainly not in an action listener.
By using Runnable, it is possible to update JProgressBar; but as mentioned, the method must be run from invokeLater().
For that, SwingWorker would be better, as shown below and here.
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class TestProgressBar {
private static void createAndShowUI() {
JFrame frame = new JFrame("TestProgressBar");
frame.getContentPane().add(new TestPBGui().getMainPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowUI();
}
});
}
private TestProgressBar() {
}
}
class TestPBGui {
private JPanel mainPanel = new JPanel();
public TestPBGui() {
JButton yourAttempt = new JButton("Your attempt to show Progress Bar");
JButton myAttempt = new JButton("My attempt to show Progress Bar");
yourAttempt.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
yourAttemptActionPerformed();
}
});
myAttempt.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
myAttemptActionPerformed();
}
});
mainPanel.add(yourAttempt);
mainPanel.add(myAttempt);
}
private void yourAttemptActionPerformed() {
Window thisWin = SwingUtilities.getWindowAncestor(mainPanel);
JDialog progressDialog = new JDialog(thisWin, "Uploading...");
JPanel contentPane = new JPanel();
contentPane.setPreferredSize(new Dimension(300, 100));
JProgressBar bar = new JProgressBar(0, 100);
bar.setIndeterminate(true);
contentPane.add(bar);
progressDialog.setContentPane(contentPane);
progressDialog.pack();
progressDialog.setLocationRelativeTo(null);
Task task = new Task("Your attempt");
task.execute();
progressDialog.setVisible(true);
while (!task.isDone()) {
}
progressDialog.dispose();
}
private void myAttemptActionPerformed() {
Window thisWin = SwingUtilities.getWindowAncestor(mainPanel);
final JDialog progressDialog = new JDialog(thisWin, "Uploading...");
JPanel contentPane = new JPanel();
contentPane.setPreferredSize(new Dimension(300, 100));
final JProgressBar bar = new JProgressBar(0, 100);
bar.setIndeterminate(true);
contentPane.add(bar);
progressDialog.setContentPane(contentPane);
progressDialog.pack();
progressDialog.setLocationRelativeTo(null);
final Task task = new Task("My attempt");
task.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equalsIgnoreCase("progress")) {
int progress = task.getProgress();
if (progress == 0) {
bar.setIndeterminate(true);
} else {
bar.setIndeterminate(false);
bar.setValue(progress);
progressDialog.dispose();
}
}
}
});
task.execute();
progressDialog.setVisible(true);
}
public JPanel getMainPanel() {
return mainPanel;
}
}
class Task extends SwingWorker<Void, Void> {
private static final long SLEEP_TIME = 4000;
private String text;
public Task(String text) {
this.text = text;
}
#Override
public Void doInBackground() {
setProgress(0);
try {
Thread.sleep(SLEEP_TIME);// imitate a long-running task
} catch (InterruptedException e) {
}
setProgress(100);
return null;
}
#Override
public void done() {
System.out.println(text + " is done");
Toolkit.getDefaultToolkit().beep();
}
}