Reputation: 13
I am trying to update an ImageIcon on a JLabel which sits on a JLayeredPane, but there is a lot of latency between when the setting thread sends the proper state to the JLabel object and when the GUI displays the ImageIcon of the proper state. The following code is an example of the issue, look for the difference in time between the print of the button being on/off and when the displayed icon gets lighter/darker. The setting thread:
new Thread(new Runnable() { // setting thread
@Override
public void run() {
// TODO Auto-generated method stub
try {
while(true) {
System.out.println("testButton on"); // print that the button is on
testButton.updateState(1); // set button state to on
Thread.sleep(70 + random.nextInt(500)); //sleep between 70 and 570 milliseconds
System.out.println("testButton off");// print that the button is off
testButton.updateState(0); // set button state to off
Thread.sleep(70 + random.nextInt(500)); // sleep between 70 and 570 milliseconds
}
} catch(Exception e) {
e.printStackTrace();
}
}
}).start();
The button object:
class Button extends JLabel {
ImageIcon released;
ImageIcon pressed;
String text;
public Button(int x, int y, String text) {
released = new ImageIcon("src/components/images/button.png");
pressed = new ImageIcon("src/components/images/buttonDown.png");
setBounds(x,y, 100, 100);
this.text = text;
setIcon(released);
}
public void updateState(int data) {
if (data == 1) {
setIcon(pressed);
}
else {
setIcon(released);
}
}
}
The ImageIcons are only 325 bytes, so what might be causing the latency? I looked up about the Event Dispatcher Thread and many people say it should be instantaneous for an image to get painted.
End goal: Have many button objects on screen with the setting thread calling them to update based on randomly occurring actions. The displayed icon for a specific button object should change immediately as it is set in the function. The setting thread will not be constantly looping, instead loop once for every action sent (it is twice here just to show the issue).
Any suggestions or things to try I will test as soon as I can.
Edit: In the end the thread that gets the information will call to a device driver in Linux where it will wait for a response and only when it gets a response will it need to update the window. From what I know timer is used to update something at regular intervals, but I am likely wrong.
Upvotes: 1
Views: 137
Reputation: 18792
As explained in the comments running long processes on the The Event Dispatch Thread blocks it, so it does not respond to changes.
Also you are not suppose to update Swing components from other (not EDT) threads.
You need to use Swing tools like SwingWorker or Timer.
The following mcve demonstrates a simple slide-show using Timer:
import java.awt.BorderLayout;
import java.io.IOException;
import java.net.URL;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
public class ChangeButtonIcon extends JPanel{
private final URL[] urls = {
new URL("https://findicons.com/files/icons/345/summer/128/cake.png"),
new URL("http://icons.iconarchive.com/icons/atyourservice/service-categories/128/Sweets-icon.png"),
new URL("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS_FkBgG3_ux0kCbfG8mcRHvdk1dYbZYsm2SFMS01YvA6B_zfH_kg"),
};
private int iconNumber = 0;
private final JButton button;
private boolean stop = true;
private final Random random;
private static final int MIN_DELAY = 70, DELAY = 500;
private Timer timer;
public ChangeButtonIcon() throws IOException {
random = new Random();
button = new JButton();
button.setIcon(new ImageIcon(urls[iconNumber]));
button.setHorizontalTextPosition(SwingConstants.CENTER);
button.addActionListener(e -> startStopSlideShow());
add(button);
}
private void startStopSlideShow(){
stop = ! stop;
if(stop){
timer.stop();
return;
}
timer = new Timer( MIN_DELAY+ random.nextInt(DELAY), (e)->swapIcon());
timer.start();
}
private void swapIcon() {
iconNumber = iconNumber >= urls.length -1 ? 0 : iconNumber+1;
button.setIcon(new ImageIcon(urls[iconNumber]));
}
public static void main(String[] args) throws IOException{
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.add(new ChangeButtonIcon());
window.add(new JLabel("Click image to start / stop"), BorderLayout.PAGE_END);
window.pack();
window.setVisible(true);
}
}
Upvotes: 2