Reputation: 23216
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?
Upvotes: 1
Views: 661
Reputation: 109815
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();
}
}
Upvotes: 1
Reputation: 109815
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();
}
});
}
}
Upvotes: 1
Reputation: 10762
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.
Upvotes: 4