Timing the display of JPanels - java

I have an annoying issue with my desktop app that I am just unable to figure out. I have isolated the problem into the following example. What I am trying to do....
I have a jframe and 5 jpanels, each panel has a different picture. When launched, the user will see panel A with a button all inside my jframe. When the button is pressed I would like panel A to dissappear and panel B,C,D and E become visible after one another but each panel to be displayed for different lengths of time...
B visible for 3 secs, B invisible, C visible for 5 seconds, C invisible... and so on.
I have tried javax.Timer, Thread.sleep(3000) and even for statements and none seem to achieve what I want. How would you guys achieve this and what is the method I need to be looking at?
Thanks in advance.

Using a Swing Timer sounds like a reasonable approach. When the user clicks the JButton, show B and start the Timer, which should have an interval of 3 seconds. When the Timer fires, show C and change the Timer's interval to 5 seconds. When it fires again, show C and set the interval for however long you want to show C.
If that isn't working, please post an MCVE showing what you've tried, and we'll go from there.

Here I have done an example which shows:
How to use a Swing timer
How to add images on JLabel without freezing the program
Swapping the pics with the help of an timing array
After timer starts its starts changing :
My SSCCE:
/**
*
* #author rohan
*/
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class TestSwapPics {
public TestSwapPics() {
initComponents();
}
private void initComponents() {
JFrame f =new JFrame();
Panel = new javax.swing.JPanel();
Panel.setLayout(new BorderLayout(5,5));
jLabel1 = new javax.swing.JLabel();
jButton1 = new javax.swing.JButton();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jLabel1.setIcon(new ImageIcon(getClass().getResource("/images.jpg")));
jButton1.setText("Start");
Panel.add(jLabel1, BorderLayout.NORTH);
Panel.add(jButton1, BorderLayout.SOUTH);
f.add(Panel);
f.pack();
f.setVisible(true);
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
startPicsChange();
}
private void startPicsChange() {
new Thread(new Runnable() {
#Override
public void run() {
Timer t = createAndStartTimer(timings[count],count);
while (t.isRunning()) {//wait for timer to be done
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
count++;
if (count == timings.length) {
JOptionPane.showMessageDialog(null, "Done");
} else {
startPicsChange();
}
}
});
}
}).start();
}
private Timer createAndStartTimer(int delay, final int count) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if(count==0)
jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/p3.PNG")));
if(count==1)
jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/p6.PNG")));
System.out.println("yes! reached here");
}
});
Timer t = new Timer(delay, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
System.out.println("here 2!!");
}
});
t.setRepeats(false);
t.start();
return t;
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestSwapPics();
}
});
}
private JButton jButton1;
private JLabel jLabel1;
private JPanel Panel;
private int[] timings = {2000, 1000, 4000,5000,2000};
private int count=0;
}

Related

How to refresh the Label one by one?

public class Test {
public static void main(String[] args) {
Methode met = new Methode();
JFrame f = new JFrame("Label Example");
JLabel l1;
JButton btn;
l1 = new JLabel("Start", SwingConstants.CENTER);
btn = new JButton("Bestätigen");
JPanel panel = new JPanel();
String comboBoxListe[] = { "1", "2", "3", "4", "5" }; // 1=300 2=250 3=200 4=150 5=100
JComboBox bundeslandAuswahl = new JComboBox(comboBoxListe);
panel.add(bundeslandAuswahl);
l1.setBounds(0, 0, 1800, 800);
l1.setFont(new Font("Serif", Font.PLAIN, 100));
btn.setBounds(800, 0, 100, 50);
panel.setBounds(900, 0, 100, 100);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
met.ausgabe(l1);
}
});
f.add(btn);
f.add(l1);
f.add(panel);
f.setLayout(null);
f.setVisible(true);
f.setLocationRelativeTo(null);
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
Timer t = new Timer();
}}
class Methode {
void ausgabe(JLabel l1) {
String temp = "";
String[] arr2 = { "Hallo", "World", "!" };
for (int i = 0; i < arr2.length; i++) {
temp = arr2[i];
l1.setText(temp);
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
The Label only shows the last element of the Array, if i use the ActionListener. Without the btn.addActionListener its work. How can i solve it with a button? I want that when I click the method ausgabe, that the code displays step by step the individual array fields in the label.
Thanks
That happens because you have to repaint the component(l1). When you use graphic user interface in a single thread program, your program will run into a while block within the GUI code. So when the part of the code that is being executed is not yours then the program is working in GUI staff like repainting the window or graphic components.
You have to call a method in l1 to repaint it. That's the reason because the last text is the only showed. In that way a GUI code will be executed and your interface will be updated. If you do that your problem will be solved. Use the java documentation to find this method.
Start by looking at Concurrency in Swing for the reasons why this approach won't work and then How to Use Swing Timers for the solution.
The "core" issue is, Swing is single threaded and not thread safe. This means that when you call ausgabe from the ActionListener, you're trying to run a long running/blocking process within the context of the Event Dispatching Thread.
But, until the method exists, the EDT can not process any new paint or other events, so nothing gets updated until it's completed.
Because Swing is also not thread safe, it's not advisable to use a Thread to try and fix the issue. Instead, you should make use of a Swing Timer, which acts like a pseudo loop, but which is called back within the EDT, making it safe to use to update the UI.
For example...
import java.awt.Dimension;
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.SwingUtilities;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private Timer timer;
private String text = "Hello World";
public TestPane() {
setLayout(new GridBagLayout());
label = new JLabel("");
add(label);
}
#Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
timer = null;
}
timer = new Timer(1000, new ActionListener() {
private int counter;
#Override
public void actionPerformed(ActionEvent evt) {
if (counter >= text.length()) {
timer.stop();
timer = null;
}
label.setText(text.substring(0, counter));
counter += 1;
}
});
timer.setInitialDelay(1000);
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
if (timer != null) {
timer.stop();
timer = null;
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
You should also take the time to learn how to use the various layout managers, it will save you a lot of time, hassle and head scratching.
See Laying Out Components Within a Container for more details.
One trick I might have used, would have been to use two labels. One with the full text set on it and with it's text color set the same color as the background of the panel, the second used to animate the update. Each positioned so that they will overlap each other.
This way, you provide enough information to the layout manager to make determinations about how much space the component needs.
In the above example, I just cheated and overrode getPreferredSize, again, this could have just calculated the final size of the label, but that's becoming complicated.

JLabel that changes text from one thing to another

I'm working on this program and I ran into another issue. I have a Jframe with a JLabel that I wish for it to change text from one thing to another. However, when I try to do that it doesnt show me the text changing, rather the last text I set it to.
How do I get my JLabel to cycle through text SLOWLY?
I'm trying a wait method to make the program go slowly so I can see if I can make it cycle through, but that doesnt seem to be working.
it would be helpful if someone could edit my code or make their own example of how to do this, THANKS!
public class CreditGraphics {
public String cardNum;
public JFrame frame;
public JPanel panel;
public JLabel label;
public JTextField text;
public CreditGraphics() {
synchronized(this){
try {
frame = new JFrame("HI");
panel = new JPanel();
label = new JLabel();
text = new JTextField(16);
panel.add(label);
panel.add(text);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(new Dimension(500, 500));
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
wait(4000);
label.setText("Hi");
wait(4000);
frame.revalidate();
frame.repaint();
label.setText("Hello");
frame.revalidate();
frame.repaint();
text.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cardNum = text.getText();
}
});
}
catch(InterruptedException e) {
e.printStackTrace();
}}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new CreditGraphics();
}
});
}
public void checkCard(){
}
}
As suggested by #trashgod use Swing Timer that is more suitable for swing application to perform a task once, after a delay or to perform a task repeatedly.
sample code:
private Timer timer;
...
label.setText("Hi");
// delay of 4 seconds
timer=new Timer(4000,new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
label.setText("Hello");
// timer.stop(); // stop the timer if repeated mode is on
}
});
timer.setRepeats(false); // you can turn-on it if needed
timer.start();
Note:
There is no need to call frame.repaint() and frame.revalidate() in this case.
Override getPreferredSize() to set the preferred size of the JPanel in case of custom painting.
sample code:
JPanel panel = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(..., ...);
}
};
read more...
Do not use Thread.sleep() or wait() as it will freeze your Swing application.
Instead you should use a javax.swing.Timer
See the Java tutorial How to Use Swing Timers and Lesson: Concurrency in Swing for more information and examples.

Make a swing thread that show a "Please Wait" JDialog

The problem is this:
I've a swing application running, at a certain point a dialog requires to insert username and password and to press "ok".
I would like that when the user press "ok" the swing application does in this order:
Open a "Please wait" JDialog
Make some operation(eventually displaying some other JDialog or JOptionPane)
When it finishes with the operation close the "please wait" JDialog
This is the code that I wrote in the okButtonActionPerformed():
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {
//This class simply extends a JDialog and contains an image and a jlabel (Please wait)
final WaitDialog waitDialog = new WaitDialog(new javax.swing.JFrame(), false);
waitDialog.setVisible(true);
... //Do some operation (eventually show other JDialogs or JOptionPanes)
waitDialog.dispose()
}
This code obviously doesn't works because when I call the waitDialog in the same thread it blocks all till I don't close it.
So I tried to run it in a different thread:
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {
//This class simply extends a JDialog and contains an image and a jlabel (Please wait)
final WaitDialog waitDialog = new WaitDialog(new javax.swing.JFrame(), false);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
waitDialog.setVisible(true);
}
});
... //Do some operation (eventually show other JDialogs or JOptionPanes)
waitDialog.dispose()
}
But also this doesn't work because the waitDialog is not displayed immediately but only after that the operation completed their work (when they show a joption pane "You are logged in as...")
I also tried to use invokeAndWait instead of invokeLater but in this case it throws an exception:
Exception in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread
How can I do?
Consider using a SwingWorker to do your background work, and then closing the dialog either in the SwingWorker's done() method or (my preference) in a PropertyChangeListener that is added to the SwingWorker.
e.g.,
import java.awt.BorderLayout;
import java.awt.Dialog.ModalityType;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class PleaseWaitEg {
public static void main(String[] args) {
JButton showWaitBtn = new JButton(new ShowWaitAction("Show Wait Dialog"));
JPanel panel = new JPanel();
panel.add(showWaitBtn);
JFrame frame = new JFrame("Frame");
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class ShowWaitAction extends AbstractAction {
protected static final long SLEEP_TIME = 3 * 1000;
public ShowWaitAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent evt) {
SwingWorker<Void, Void> mySwingWorker = new SwingWorker<Void, Void>(){
#Override
protected Void doInBackground() throws Exception {
// mimic some long-running process here...
Thread.sleep(SLEEP_TIME);
return null;
}
};
Window win = SwingUtilities.getWindowAncestor((AbstractButton)evt.getSource());
final JDialog dialog = new JDialog(win, "Dialog", ModalityType.APPLICATION_MODAL);
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("state")) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
dialog.dispose();
}
}
}
});
mySwingWorker.execute();
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
JPanel panel = new JPanel(new BorderLayout());
panel.add(progressBar, BorderLayout.CENTER);
panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START);
dialog.add(panel);
dialog.pack();
dialog.setLocationRelativeTo(win);
dialog.setVisible(true);
}
}
Notes:
A key concept is to set everything up, add the PropertyChangeListener, get the SwingWorker running, all before displaying the modal dialog, because once the modal dialog is shown, all code flow from the calling code is frozen (as you've found out).
Why do I prefer the PropertyChangeListener to using the done method (as Elias demonstrates in his decent answer here, which I've up-voted) -- using the listener provides more separation of concerns, looser coupling. This way the SwingWorker has to know nothing of the GUI code that is using it.
public void okButtonActionPerformed(ActionEvent e) {
final JDialog loading = new JDialog(parentComponent);
JPanel p1 = new JPanel(new BorderLayout());
p1.add(new JLabel("Please wait..."), BorderLayout.CENTER);
loading.setUndecorated(true);
loading.getContentPane().add(p1);
loading.pack();
loading.setLocationRelativeTo(parentComponent);
loading.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
loading.setModal(true);
SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {
#Override
protected String doInBackground() throws InterruptedException
/** Execute some operation */
}
#Override
protected void done() {
loading.dispose();
}
};
worker.execute();
loading.setVisible(true);
try {
worker.get();
} catch (Exception e1) {
e1.printStackTrace();
}
}
A variation of the above answer
It's an easy and replicable way to do...
//This code goes inside your button action
DialogWait wait = new DialogWait();
SwingWorker<Void, Void> mySwingWorker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
//Here you put your long-running process...
wait.close();
return null;
}
};
mySwingWorker.execute();
wait.makeWait("Test", evt);
//end
//Create this class on your project
class DialogWait {
private JDialog dialog;
public void makeWait(String msg, ActionEvent evt) {
Window win = SwingUtilities.getWindowAncestor((AbstractButton) evt.getSource());
dialog = new JDialog(win, msg, Dialog.ModalityType.APPLICATION_MODAL);
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
JPanel panel = new JPanel(new BorderLayout());
panel.add(progressBar, BorderLayout.CENTER);
panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START);
dialog.add(panel);
dialog.pack();
dialog.setLocationRelativeTo(win);
dialog.setVisible(true);
}
public void close() {
dialog.dispose();
}
}

Need advice on creating a closing button for a small Java program

I am a novice as already stated and looking to create a button to close the program out. I am not talking about making sure the typical window close (Red X) terminates the program. I wish to make an additional button within my frame that when clicked will terminate the program as well.
You can add an ActionListener to your button which, upon action being performed, exits from the JVM.
yourButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
If you have set up the main application frame's (JFrame) defaultCloseOperation to JFrame.EXIT_ON_CLOSE then simply calling the frame's dispose method will terminate the program.
JButton closeButton = JButton("Close");
closeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
yourReferenceToTheMainFrame.dispose();
}
});
If not, then you will need to add to the actionPerformed method a call to System.exit(0);
import java.awt.GridLayout;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class GoodbyeWorld {
GoodbyeWorld() {
final JFrame f = new JFrame("Close Me!");
// If there are no non-daemon threads running,
// disposing of this frame will end the JRE.
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// If there ARE non-daemon threads running,
// they should be shut down gracefully. :)
JButton b = new JButton("Close!");
JPanel p = new JPanel(new GridLayout());
p.setBorder(new EmptyBorder(10,40,10,40));
p.add(b);
f.setContentPane(p);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
ActionListener closeListener = new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
f.setVisible(false);
f.dispose();
}
};
b.addActionListener(closeListener);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new GoodbyeWorld();
}
};
SwingUtilities.invokeLater(r);
}
}
If you are extending the org.jdesktop.application.Application class (Netbeans would do that) you could invoke exit() in your app class, so:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
yourApp.exit();
}
});

problem with getting JFrame bounds inside a timer in Netbeans

I want to animate a JFrame to become half-size when i press a button in my programme. I think the easiest way is putting the current bounds of JFrame into a timer and decrease bounds 1 by 1 when the timer running.But when I declare a new timer in netbeans IDE it will looks like this.
Timer t = new Timer(5,new ActionListener() {
public void actionPerformed(ActionEvent e) {
//inside this I want to get my Jframe's bounds like this
// int width = this.getWidth();---------here,"this" means the Jframe
}
}
});
But the problem is in here "this" not refering to JFrame.And also I cant even create a new object of my JFrame.Because it will give me another window.Can anyone help me solve this problem ?.
Try
int width = Foo.this.getWidth();
where Foo subclasses JFrame.
I want to animate a JFrame to become half-size when i press a button in my programme
So when you click the button you have access to the button. Then you can use:
SwingUtilities.windowForComponent( theButton );
to get a reference to the frame.
So now when you create the ActionListener for the Timer you can pass in the Window as an argument for the ActionListener.
Edit:
The suggestion by mre is simple and straight forward and easy to use in many cases (and probably the better solution in this case).
My suggestion is a little more complicated but it was introducing you to the SwingUtilities method which will eventually allow you to write more reusable code that could potentially be used by any frame or dialog you might create.
A simple example would be something like:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AnimationSSCCE extends JPanel
{
public AnimationSSCCE()
{
JButton button = new JButton("Start Animation");
button.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JButton button = (JButton)e.getSource();
WindowAnimation wa = new WindowAnimation(
SwingUtilities.windowForComponent(button) );
}
});
add( button );
}
class WindowAnimation implements ActionListener
{
private Window window;
private Timer timer;
public WindowAnimation(Window window)
{
this.window = window;
timer = new Timer(20, this);
timer.start();
}
#Override
public void actionPerformed(ActionEvent e)
{
window.setSize(window.getWidth() - 5, window.getHeight() - 5);
// System.out.println( window.getBounds() );
}
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("AnimationSSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new AnimationSSCCE() );
frame.setSize(500, 400);
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Of course you would want to stop the timer when the winow reaches a certain minimum size. I'll leave that code up to you.

Categories