What I have to do is make a little GUI that when a button
is clicked it starts to progress bars with threads
; however, we have to do it with an inner class that implements the Runnable interface
, and within the constructor for that class, it takes a String
and a JProgressBar
object. Then within the main constructor, we are supposed to instantiate 2 JProgressBar
objects and then create 2 of the Inner class objects using those JProgressBars
.
Only my second JProgressBar
is updating with the thread
. I know that my second JProgressBar
is basically overriding my first one, but I don't know how I would go to fix that, because if I try to change the inner class's constructor to setting 2 JProgressBar
attributes equal to the parameter of the constructor, then the first JProgessBar
just completely disappears from the GUI. Here is my code, thank you to anyone that can help.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Lab4Part2 {
//Attributes
private JFrame frame;
private String progress;
private JProgressBar jpb;
//Constructor
public Lab4Part2() {
//Create frame with specified grid layout
frame = new JFrame();
GridLayout grid = new GridLayout(0,1);
frame.setLayout(grid);
JButton jbClick = new JButton("Let's start this show");
frame.add(jbClick);
//Add in JProgressBars and create Inner Class objects with them
JProgressBar jpb1 = new JProgressBar();
JProgressBar jpb2 = new JProgressBar();
InnerProgress bar1 = new InnerProgress("Progress 1: ", jpb1);
InnerProgress bar2 = new InnerProgress("Progress 2: ", jpb2);
//Anonymous class for the button's action listener
jbClick.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
Thread t1 = new Thread(bar1);
Thread t2 = new Thread(bar2);
t1.start();
t2.start();
}
});
//Adds inner object to frame
frame.add(bar1);
frame.add(bar2);
//Packing and stuff
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
//Inner class
class InnerProgress extends JPanel implements Runnable {
//Constructor
public InnerProgress(String pbar, JProgressBar jpBar) {
jpBar.setMinimum(0);
jpBar.setMaximum(80);
jpBar.setStringPainted(true);
progress = pbar;
jpb = jpBar;
JLabel label = new JLabel(pbar);
add(label);
add(jpBar);
}
//Thread action
public void run() {
System.out.println("We are running " + Thread.currentThread().getName());
for(int i = 1; i<= 80; i++) {
try {
Thread.currentThread().sleep((long)Math.random()*100);
jpb.setValue(i);
}
catch(InterruptedException ie) {
System.out.println(ie);
}
}
System.out.println("Thread name: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
}
//Main
public static void main(String[] args) {
new Lab4Part2();
}
}
Your inner class should maintain it's own reference to the JProgressBar
that it is passed.
Simply take...
private JProgressBar jpb;
and move it inside your inner class
class InnerProgress extends JPanel implements Runnable {
private JProgressBar jpb;
//...
So, two other issues
One, (long)Math.random()*100
is causing the result of Math.random()
to be case to an int
, which will result in it becoming 0
.
You need to cast the result of the operation, for example...
Thread.currentThread().sleep((int)(Math.random() * 500));
(nb: I made it 500 for my testing)
Two, Swing is not thread safe. This means you should not be updating the UI from outside of the context of the Event Dispatching Thread.
So, instead of...
jpb.setValue(i);
I wrote a new method which will update the UI accordingly
protected void updateProgress(int value) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
InnerProgress.this.jpb.setValue(value);
}
});
}
(Long and short, this over comes issues with referencing variables from inside an anonymous context)
Then simply called it using updateProgress(i);
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
public class Lab4Part2 {
//Attributes
private JFrame frame;
private String progress;
//Constructor
public Lab4Part2() {
//Create frame with specified grid layout
frame = new JFrame();
GridLayout grid = new GridLayout(0, 1);
frame.setLayout(grid);
JButton jbClick = new JButton("Let's start this show");
frame.add(jbClick);
//Add in JProgressBars and create Inner Class objects with them
JProgressBar jpb1 = new JProgressBar();
JProgressBar jpb2 = new JProgressBar();
InnerProgress bar1 = new InnerProgress("Progress 1: ", jpb1);
InnerProgress bar2 = new InnerProgress("Progress 2: ", jpb2);
//Anonymous class for the button's action listener
jbClick.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
Thread t1 = new Thread(bar1);
Thread t2 = new Thread(bar2);
t1.start();
t2.start();
}
});
//Adds inner object to frame
frame.add(bar1);
frame.add(bar2);
//Packing and stuff
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
//Inner class
class InnerProgress extends JPanel implements Runnable {
private JProgressBar jpb;
//Constructor
public InnerProgress(String pbar, JProgressBar jpBar) {
jpBar.setMinimum(0);
jpBar.setMaximum(80);
jpBar.setStringPainted(true);
progress = pbar;
jpb = jpBar;
JLabel label = new JLabel(pbar);
add(label);
add(jpBar);
}
//Thread action
public void run() {
System.out.println("We are running " + Thread.currentThread().getName());
for (int i = 1; i <= 80; i++) {
try {
Thread.currentThread().sleep((int)(Math.random() * 500));
updateProgress(i);
} catch (InterruptedException ie) {
System.out.println(ie);
}
}
System.out.println("Thread name: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
protected void updateProgress(int value) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
InnerProgress.this.jpb.setValue(value);
}
});
}
}
//Main
public static void main(String[] args) {
new Lab4Part2();
}
}