I am trying to display different images in 3 different jlables. There is a folder with images in them. I have display the images in the jlable and the image has to change after 60 seconds. No matter what I try I cannot do it. Can somebody help me.
The simple answer is, use a Swing Timer, it allows you to schedule a callback in the future, which is delivered within the context of the Event Dispatching Thread, making it safe to update the UI from, for example...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
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 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();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private File[] images;
private int imageIndex;
public TestPane() {
setLayout(new BorderLayout());
label = new JLabel();
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalAlignment(JLabel.CENTER);
add(label);
images = new File("...").listFiles();
imageIndex = -1;
nextImage();
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
nextImage();
}
});
timer.start();
}
protected void nextImage() {
if (images.length > 0) {
imageIndex++;
if (imageIndex >= images.length) {
imageIndex = 0;
}
try {
BufferedImage image = ImageIO.read(images[imageIndex]);
label.setIcon(new ImageIcon(image));
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
}
Have a look at How to use Swing Timers for more details.
If your images are large or are coming from a slow service (like over the internet), you might consider using a SwingWorker instead, this allows you to perform long running or blocking operations in the background (off the EDT), but which are easier to synchronise updates back to the EDT with. Have a look at Refresh Image in JLabel with Timer for more details
Related
I want to update the Jlabel text in every second as long as the loop is running. how could I do this? I want to do as this fromat.
JPanel jpnl=new JPanel();
jfrm.add(jpnl);
String[] fonts=GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
jlab = new JLabel("This is Label");
jpnl.add(jlab);
for(int i=0;i<fonts.length;i++){
System.out.println(fonts[i]);
jlab.setText(fonts[i]);
jlab.setFont(new Font(fonts[i],Font.PLAIN,30));
jlab.setForeground(Color.DARK_GRAY);
}
Swing's single threaded nature precludes using a loop or Thread.sleep in the way you seem to be trying. Doing so, will simply block the UI and prevent it from been painted/updated until the loop is completed.
Because Swing is not thread safe, you can't simply use another Thread and the above approaches to update the UI, without jumping through some hoops
The conical answer to your question is to use a Swing Timer, which triggers an update at a regular bases. Because these updates are triggered within the context of the Event Dispatching Thread, it makes it safe to use when you want to update the UI.
Take a closer look at How to use Swing Timers for more details
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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 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();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private String[] fonts;
private final JLabel jlab;
private int index = 0;
public TestPane() {
setLayout(new GridBagLayout());
fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
jlab = new JLabel("This is Label");
add(jlab);
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateFont();
index++;
if (index >= fonts.length) {
((Timer)e.getSource()).stop();
}
}
});
timer.setInitialDelay(0);
timer.start();
}
protected void updateFont() {
System.out.println(fonts[index]);
jlab.setText(fonts[index]);
jlab.setFont(new Font(fonts[index], Font.PLAIN, 30));
jlab.setForeground(Color.DARK_GRAY);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
I am trying to make a slideshow program. I want the next slide to appear when I click (There are only two slides not, but I will add more once the errors r sorted out).
The code compiles fine. But when i click, nothing happens.
What could possibly go wrong?
package project;
import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Frame extends JFrame{
Frame() {
setLayout(new BorderLayout());
ImageIcon slide = new ImageIcon("E:\\Books\\Computer\\Java\\Introduction to Java Programming\\exercise9e\\image\\slide0.jpg");
JLabel slidesLabel = new JLabel(slide);
add(slidesLabel,BorderLayout.CENTER);
slidesLabel.addMouseListener(new ClickListener());
}
public void nextSlide() {
ImageIcon slide = new ImageIcon("E:\\Books\\Computer\\Java\\Introduction to Java Programming\\exercise9e\\image\\slide1.jpg");
JLabel slidesLabel = new JLabel(slide);
add(slidesLabel,BorderLayout.CENTER);
System.out.println("x");
}
public static void main(String args[]) {
Frame frame = new Frame();
frame.setSize(800,600);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
}
public class ClickListener extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
nextSlide();
}
}
}
So, the "main" problem is BorderLayout will only manage a single component within any of the five available locations it manages.
Adding another component into the position tends to cause issues, where the component that was first added won't be displayed, or in your case, will remain and could interfere with the new component
"A" solution would be to re-use the same JLabel for each slide, simply supply a new value for the icon property (or in this example, the text property)
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Frame extends JFrame {
private JLabel slidesLabel = new JLabel("Apple");
public Frame() {
setLayout(new BorderLayout());
add(slidesLabel, BorderLayout.CENTER);
slidesLabel.addMouseListener(new ClickListener());
}
public void nextSlide() {
slidesLabel.setText("Banana");
}
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) {
ex.printStackTrace();
}
Frame frame = new Frame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ClickListener extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
nextSlide();
}
}
}
This approach would allow you to place each icon into an array and simply have a counter which determines which slide is current, so when you click for the next slide, you simply increment the counter, get the next value from the array and apply it to the label
A better (and more appropriate) solution would be to actually use a CardLayout, see How to Use CardLayout for more details
With icons...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Frame extends JFrame {
private JLabel slidesLabel = new JLabel();
private Icon[] icons;
private int currentSlide = -1;
public Frame() {
try {
// Personally, I'd use File#listFiles to list all the
// images in a directory, but that might be consider
// using our initiative...
icons = new Icon[]{
new ImageIcon(ImageIO.read(new File("..."))),
new ImageIcon(ImageIO.read(new File("..."))),
new ImageIcon(ImageIO.read(new File("...")))
};
slidesLabel.setVerticalAlignment(JLabel.CENTER);
slidesLabel.setHorizontalAlignment(JLabel.CENTER);
setLayout(new BorderLayout());
add(slidesLabel, BorderLayout.CENTER);
slidesLabel.addMouseListener(new ClickListener());
nextSlide();
} catch (IOException exp) {
exp.printStackTrace();
}
}
public void nextSlide() {
if (currentSlide < icons.length - 1) {
currentSlide++;
slidesLabel.setIcon(icons[currentSlide]);
}
}
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) {
ex.printStackTrace();
}
Frame frame = new Frame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ClickListener extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
nextSlide();
}
}
}
Why simply adding components to a BorderLayout is bad...
All I did was when nextSlide was called, create a new JLabel assign it the next icon and add it to the Frame (which is using a BorderLayout) and then re-sized the frame. Because the labels are transparent, they remain visible...
This is why you shouldn't simply add new components to a BorderLayout, but, in your case, simply update the properties of the existing JLabel to meet the changing needs.
I have a question about JLabels. I am trying to program an application that has a window (Set up with JFrame.) with many JLabels. Anyway, I was wondering if there was a way I could have the window show, then modify the JLabels' text. The problem I am having is that the window won't show up until the program reaches the end of the class. Is there a way I can get around that? Do I have to use a Thread? If so, how would I do that.
Thanks,
~Rane
Example:
public class Start extends JFrame{
private static final long serialVersionUID = 1L;
Random random = new Random();
JPanel panel = new JPanel();
JLabel label = new JLabel("Label");
panel.add(label);
add(panel);
while(true){
int rnd = random.nextInt(4);
label.setText("" + rnd);
}
} // I want to do that, but the window won't show until the loop ends. In this case, the loop
// never will end. How would I do something like this if not the same exact thing?
Let's start with the fact that Swing is not thread safe and that all interactions and modifications to the UI should be made within the contentext of the Event Dispatching Thread.
This also means that you never perform any long running or time consuming actions within the context of the Event Dispatching Thread, as this will prevent it from processing new event placed on the EventQueue, including, repaint requests.
See Concurrency in Swing for more details.
The simplest solution would be to use a javax.swing.Timer. This will allow you to schedule a regular call back which is guaranteed to be triggered within the context of the EDT, making it safe to update the UI
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
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 RandomLabels {
public static void main(String[] args) {
new RandomLabels();
}
public RandomLabels() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private Random rnd;
public TestPane() {
setLayout(new GridBagLayout());
label = new JLabel();
add(label);
rnd = new Random();
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(Integer.toString(rnd.nextInt()));
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
This also means you can control the Timer, starting it and stopping it when you want/need to with ease.
You could use a Thread, but the management requirements increase...You become responsible for managing the synchronisation of the updates to the UI, as well as having to implement functionality to actually stop the thread.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
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 RandomLabels {
public static void main(String[] args) {
new RandomLabels();
}
public RandomLabels() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private Random rnd;
public TestPane() {
setLayout(new GridBagLayout());
label = new JLabel();
add(label);
rnd = new Random();
Thread t = new Thread(new Randomizer());
t.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public class Randomizer implements Runnable {
#Override
public void run() {
while (true) {
try {
EventQueue.invokeAndWait(new Runnable() {
#Override
public void run() {
label.setText(Integer.toString(rnd.nextInt()));
}
});
} catch (InterruptedException | InvocationTargetException exp) {
exp.printStackTrace();
}
try {
Thread.sleep(40);
} catch (InterruptedException ex) {
}
}
}
}
}
}
Another (better than using a Thread) solution might be to use a SwingWorker. Which would allow you to perform long running/blocking/time consuming actions within the background, but provides functionality to easily send updates back to the UI automatically.
Take a closer look at How to use Swing Timers and Worker Threads and SwingWorker for more details
And finally, take a look at Initial Threads. You must ensure that you UI is started/constructed within the context of the EDT as well...
Call setVisibile in the constructor before you enter the while loop.
public class Start extends JFrame {
private static final long serialVersionUID = 1L;
public Start() {
Random random = new Random();
JPanel panel = new JPanel();
JLabel label = new JLabel("Label");
panel.add(label);
add(panel);
this.setVisible(true); //IMPORTANT PART
while(true){
int rnd = random.nextInt(4);
label.setText("" + rnd);
}
}
}
I have here a sample of progress bar:
import java.awt.BorderLayout;
import java.awt.Container;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.border.Border;
public class ProgressSample {
public static void main(String args[]) {
JFrame f = new JFrame("JProgressBar Sample");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container content = f.getContentPane();
JProgressBar progressBar = new JProgressBar();
progressBar.setValue(25);
progressBar.setStringPainted(true);
Border border = BorderFactory.createTitledBorder("Reading...");
progressBar.setBorder(border);
content.add(progressBar, BorderLayout.NORTH);
f.setSize(300, 100);
f.setVisible(true);
}
}
Now.. Is there a way to make the value run from 0 - 100% without a button triggering it. Like when I run that frame the Thread or Timer will automatically start. Is there a way to make it? Or I still need a button to trigger the timer/thread?
Simple answer is, yes.
You can update the progress bar at any time, so long as you do it from within the context of the Event Dispatching Thread. What you will require is some way to tell the JProgressBar what it's new value should be, but that will depend on what it is you are trying to achieve.
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class AutoProgress {
public static void main(String[] args) {
new AutoProgress();
}
private JProgressBar pb;
private int progress;
public AutoProgress() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
pb = new JProgressBar();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(pb);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
progress += 1;
if (progress >= 100) {
progress = 100;
((Timer)e.getSource()).stop();
}
pb.setValue(progress);
}
});
timer.start();
}
});
}
}
You might also like to have a look at JProgressBar#setIndeterminate.
You should also have a look at How to use Swing Timers
Im trying to figure out how i can add number notifications to my dock icon when a new message is received (I've got a small chat app)
This is what i mean:
Any ideas how to accomplish this?
Try taking a look at com.apple.eawt.Application (I'm having a hard time finding JavaDocs)
import com.apple.eawt.Application;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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 TestBadge {
public static void main(String[] args) {
new TestBadge();
}
public TestBadge() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private int count;
public TestPane() {
setLayout(new GridBagLayout());
add((label = new JLabel("0")));
Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
count++;
label.setText(Integer.toString(count));
Application.getApplication().setDockIconBadge(Integer.toString(count));
}
});
timer.start();
}
}
}
I should point out, that if you're running in an IDE capable of inspecting the installed classes, you should be able to get a list of the functionality provided by the Application class. I was using NetBeans and was able to find a listing of all the methods, problem is, some were documented and some weren't :P