I'm having some trouble with this piece of code.
I'm starting a timer with a random number, and I want to update a JLabel with the countdown, every second. But I haven't figured out how to do so, since the only listener the timer triggers is at the end of it (that I know).
here's the code:
int i = getTimer(maxWait);
te1 = new Timer(i, this);
label.setText(i+"");
te1.start();
...
public int getTimer(int max){
Random generator = new Random();
int i = generator.nextInt(max);
return i*1000;
}
...
public void actionPerformed(ActionEvent ev){
if(ev.getSource() == te1){
label.setText(i+"");
te1.stop();
}
}
I don't really understand your question why you are using the Random, but here are some observations:
I want to update a JLabel with the countdown, every second.
Then you need to set the Timer to fire every second. So the parameter to the Timer is 1000, not some random number.
Also, in your actionPerformed() method you stop the Timer the first time it fires. If you are doing a count down of some kind then you would only stop the Timer when the time reaches 0.
Here is a simple example of using a Timer. It just updates the time every second:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class TimerTime extends JPanel implements ActionListener
{
private JLabel timeLabel;
public TimerTime()
{
timeLabel = new JLabel( new Date().toString() );
add( timeLabel );
Timer timer = new Timer(1000, this);
timer.setInitialDelay(1);
timer.start();
}
#Override
public void actionPerformed(ActionEvent e)
{
//System.out.println(e.getSource());
timeLabel.setText( new Date().toString() );
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("TimerTime");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new TimerTime() );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
If you need more help then update your question with a proper SSCCE demonstrating the problem. All questions should have a proper SSCCE, not just a few random lines of code so we can understand the context of the code.
Related
I am having trouble with a game I am making. I am attempting to fade a JLabel, using a Timer. So far, I have been able to set a JLabel as transparent, and I have been able to make a working Timer. But somehow, I am not able to combine them together. I can set a JLabel's background to
l.setBackground(new Color(0, 0, 0, 200));
(l being desired JLabel to change the color of, of course)
and it works. The JLabel is transparent.
But I have not been able to put it into a timer so that the color fades:
public static void fadeLabel(JLabel lab, int steps, int target) {
class fade extends Thread {
public void run() {
Timer t = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(lab.getBackground().getAlpha());
lab.setBackground(new
Color(lab.getBackground().getRed(),
lab.getBackground().getGreen(),
lab.getBackground().getBlue(),
lab.getBackground().getAlpha() + steps));
if (lab.getBackground().getAlpha() == target) {
((Timer) e.getSource()).stop();
}
}
});
t.start();
}
}
new Thread(new fade()).start();
}
instead, it just goes from black to transparent. Nothing in-between. It also throws the error java.lang.IllegalArgumentException: Color parameter outside of expected range: Alpha. However, I have set the step to -5 (the rate at which it fades, in this case, fades out, as it is negative) and if you remember, I set the alpha to something that is divisible by five, and I also have a checker for when it gets to the target, so that it stops the Timer when it reaches that target. Is there something I'm doing wrong? Is the JLabel's background updating too fast for it to show? Any help would be great.
Edit:
I have fixed the java.lang.IllegalArgumentException: Color parameter outside of expected range: Alpha. But the other one about the JLabel not fading is still there, and I don't know how to fix it.
I'm not directly answering your question. A wrote some code, take a look :D
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class MyWindow extends JFrame {
public MyWindow() {
initComponents();
}
private void initComponents() {
setTitle( "Fading labels :)" );
setDefaultCloseOperation( EXIT_ON_CLOSE );
setSize( 400, 400 );
setLocationRelativeTo( null );
setLayout( new FlowLayout() );
for ( int i = 0; i < 550; i++ ) {
JLabel lab = new JLabel( "x" );
add( lab );
fadeLabel( lab, 10, i / 10, 0 );
}
}
private void fadeLabel( JLabel label, int steps, int skipSteps, int targetAlpha ) {
new LabelFader( label, steps, skipSteps, targetAlpha ).start();
}
private class LabelFader {
JLabel label;
int steps;
int skipSteps;
int targetAlpha;
LabelFader( JLabel label, int steps, int skipSteps, int targetAlpha ) {
this.label = label;
this.steps = steps;
this.skipSteps = skipSteps;
this.targetAlpha = targetAlpha;
}
void start() {
Color startColor = label.getForeground();
int startAlpha = startColor.getAlpha();
int deltaAlpha = startAlpha - targetAlpha;
if ( deltaAlpha > 0 ) {
int alphaStep = deltaAlpha / steps;
new Thread( new Runnable() {
#Override
public void run() {
int totalIterations = steps + skipSteps;
int currIteration = 1;
for ( int i = 0; i < totalIterations; i++ ) {
if ( skipSteps == 0 ) {
int newAlpha = startAlpha - alphaStep * currIteration++;
newAlpha = newAlpha < 0 ? 0 : newAlpha;
Color newColor = new Color(
startColor.getRed(),
startColor.getGreen(),
startColor.getGreen(),
newAlpha );
label.setForeground( newColor );
} else {
skipSteps--;
}
try {
Thread.sleep( 100 );
} catch ( InterruptedException exc ) {
exc.printStackTrace();
}
}
Color newColor = new Color(
startColor.getRed(),
startColor.getGreen(),
startColor.getGreen(),
targetAlpha );
label.setForeground( newColor );
}
}).start();
}
}
}
public static void main( String[] args ) {
EventQueue.invokeLater( new Runnable() {
public void run() {
new MyWindow().setVisible( true );
}
});
}
}
A JLabel is not opaque by default, so its background is not painted. Therefore changing its background color will have no effect - unless you explicitly made your JLabel opaque by calling the relevant method, as in
lab.setOpaque(true)
Forgive me but I couldn't find that in the code you posted.
Also, code that affects the GUI needs to be performed on the Event Dispatch Thread (EDT). Your code that changes the JLabel background color is being run in a separate thread. The result of running that code is therefore unpredictable. The javax.swing.Timer class does not need to be run in a separate thread. Simply create a Timer object and then call its start method. So just take your code that creates the Timer out of the Thread and put it in with the rest of your GUI code.
If you would like a small, demo program showing how to fade the text of a JLabel, let me know.
EDIT: Here is the demo that fades the JLabel text. A JFrame containing a JLabel and a JButton. Click the JButton and the JLabel text fades until it disappears.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.WindowConstants;
/**
* Tests <i>Swing</i> {#link javax.swing.Timer timer}.
*/
public class LabelDim {
private int alpha;
private JButton button;
private JFrame frame;
private JLabel label;
private Timer timer;
public LabelDim() {
timer = new Timer(200, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (alpha > 0) {
alpha -= 15;
label.setForeground(new Color(0, 0, 0, alpha));
}
else {
timer.stop();
}
}
});
}
private void showGui() {
frame = new JFrame("Dim");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
label = new JLabel("I am a fading label.", SwingConstants.CENTER);
alpha = label.getForeground().getAlpha();
label.setFont(label.getFont().deriveFont(24.0f));
label.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
frame.add(label, BorderLayout.CENTER);
button = new JButton("Start");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
JPanel panel = new JPanel();
panel.add(button);
frame.add(panel, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
LabelDim instance = new LabelDim();
EventQueue.invokeLater(() -> instance.showGui());
}
}
SECOND EDIT:
Here's some sample code that works. It'll throw the Exception you've already fixed but this is just for demonstration purposes only :).
Play with the label.setOpaque and its background and tell what happens with yourself. If you need to draw the label background set that flag to true.
If you do not need to draw its background do not modify label's background and do not set it to be opaque and modifying the foreground color should be more than enough! :)
package main;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main extends JFrame {
JLabel label;
JButton btn;
public Main() {
setSize(500,500);
setLayout(new FlowLayout());
label = new JLabel("My super awesome label");
//label.setOpaque(true); //Play with these lines and see what happens
//label.setBackground(Color.BLUE); //Play with these lines and see what happens
btn = new JButton("Click me");
btn.setSize(200,200);
btn.addActionListener(
(ActionEvent evt) -> {
Timer t = new Timer(100,
(ActionEvent event) -> {
Color foreground = label.getForeground();
Color background = label.getBackground();
foreground = decreaseAlpha(foreground);
background = decreaseAlpha(background);
label.setForeground(foreground);
label.setBackground(background);
}
);
t.start();
}
);
this.add(label);
this.add(btn);
}
public static void main(String[] args) {
(new Main()).setVisible(true);
}
public Color decreaseAlpha(Color original) {
return new Color(original.getRed(), original.getGreen(), original.getBlue(), original.getAlpha() - 10);
}
}
EDIT:
You might be changing your background of your label instead of its foreground...
Timer t = new Timer(10, (ActionEvent evt)-> {
l.setForeground(new
Color(l.getForeground().getRed(),
l.getForeground().getGreen(),
l.getForeground().getBlue(),
l.getForeground().getAlpha() - 1)
);
l.invalidate();
});
t.start();
Being l your label
Also, don't do this:
new Thread(new fade()).start();
Your fade class is already a Thread because it's extending it. Use (new fade()).start(); instead
I am trying to make a thread that reads the screen and displays it in a frame, this code is meant to run at 5fps, so far it reads the screen, but I am having trouble making the JFrame display the updating Image each "frame" or 200 mili-seconds. when I use repaint(); or revalidate();
public static void startScreenRecorder()
{
Thread screenThread = new Thread()
{
public synchronized void run()
{
long time;
long lastFrameTime = 0;
JFrame frame = new JFrame("Screen capture");
ImagePanel panel = new ImagePanel(captureScreen());
frame.add(panel);
frame.setSize(300, 400);
frame.setVisible(true);
while (true)
{
time = System.currentTimeMillis();
while (time - lastFrameTime < 190)
{
try {
Thread.sleep(10);
} catch (Exception e) {
}
time = System.currentTimeMillis();
}
lastFrameTime = time;
panel = new ImagePanel(captureScreen());
panel.revalidate();
panel.repaint();
frame.revalidate();
frame.repaint();
}
}
};
screenThread.start();
}
Don't use Thread.sleep() to attempt to control animation.
Animation should be done by using a Swing Timer. When you use a Timer the GUI is automatically updated on the EDT.
panel = new ImagePanel(captureScreen());
The above code doesn't do anything. It just creates a panel in memory. Nowhere to you actually add the panel to the GUI. Changing the reference of a variable does not update the GUI.
Instead you should probably add a JLabel to the frame (when you initially create the frame). Then when you have a new Image you just do:
label.setIcon( new ImageIcon( your screen capture ) );
I wouldn't be surprised if your code shows no images at all since it ignores Swing threading rules:
All Swing code needs to be called on the Swing event dispatch thread (EDT) only.
All other long-running code needs to be called in a background thread. I assume that this means captureScreen().
You should never call Thread.sleep(...) on the Swing event thread unless you want to put your entire application to sleep.
Better perhaps to use a Swing Timer.
You create new ImagePanels but do nothing with them -- you never add them to the GUI for instance, except for the first JPanel. Note that if you change the object a variable refers to, here the panel variable, this will have absolutely no effect on instances of the object used elsewhere, there the JPanel displayed in the GUI.
Rather than create new JPanels, why not instead create ImageIcons with your images and swap a visualized JLabel's Icon with setIcon(...)?
Since you have a lot of background stuff going on, consider using a SwingWorker<Void, Icon> to do your work, and have it publish ImageIcons that are then displayed in the GUI's JLabel. If you did this, then you probably wouldn't use a Swing Timer since the timing would be done in the SwingWorker's background thread.
For example:
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
#SuppressWarnings("serial")
public class SwingWorkerEg extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private JLabel displayedLabel = new JLabel();
public SwingWorkerEg() {
setLayout(new BorderLayout());
add(displayedLabel);
try {
MySwingWorker mySwingWorker = new MySwingWorker();
mySwingWorker.execute();
} catch (AWTException e) {
e.printStackTrace();
}
}
public void setLabelIcon(Icon icon) {
displayedLabel.setIcon(icon);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private class MySwingWorker extends SwingWorker<Void, Icon> {
private final Rectangle SCREEN_RECT = new Rectangle(0, 0, PREF_W,
PREF_H);
private Robot robot = null;
public MySwingWorker() throws AWTException {
robot = new Robot();
}
#Override
protected Void doInBackground() throws Exception {
Timer utilTimer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
BufferedImage capturedImage = captureScreen();
publish(new ImageIcon(capturedImage));
}
};
long delay = 200;
utilTimer.scheduleAtFixedRate(task, delay, delay);
return null;
}
#Override
protected void process(List<Icon> chunks) {
for (Icon icon : chunks) {
setLabelIcon(icon);
}
}
private BufferedImage captureScreen() {
BufferedImage img = robot.createScreenCapture(SCREEN_RECT);
return img;
}
}
private static void createAndShowGui() {
SwingWorkerEg mainPanel = new SwingWorkerEg();
JFrame frame = new JFrame("SwingWorker Eg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Which would display...
I'm a Java beginner and I'm trying to build a simple stopwatch program that displays the time on a swing GUI. Making the stopwatch is easy, however I cannot find a way to make the GUI update every second and display the current time on the stopwatch. How can I do this?
Something along these lines should do it:
import java.awt.EventQueue;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JLabel;
/** #see https://stackoverflow.com/a/11058263/230513 */
public class Clock {
private Timer timer = new Timer();
private JLabel timeLabel = new JLabel(" ", JLabel.CENTER);
public Clock() {
JFrame f = new JFrame("Seconds");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(timeLabel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
timer.schedule(new UpdateUITask(), 0, 1000);
}
private class UpdateUITask extends TimerTask {
int nSeconds = 0;
#Override
public void run() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
timeLabel.setText(String.valueOf(nSeconds++));
}
});
}
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
final Clock clock = new Clock();
}
});
}
}
The timeLabel will always display the number of seconds the timer has been running.
You will need to correctly format it to display "hh:mm:ss"; one approach is shown here.
Create a container and add the label to it so that you can display it as part of the GUI.
Compare the result to this alternate using javax.swing.Timer.
I would like to create a JButton that changes its text periodically after the first click. I'm not really familiar with Swing library. What would be a good starting point? May I update its text without an action?
Thank you.
for all periodical events in Swing I only suggest javax.swing.Timer
output by using Timer should be, for example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.Timer;
public class CrazyButtonTimer {
private JFrame frame = new JFrame(" Crazy Button Timer");
private JButton b = new JButton("Crazy Colored Button");
private Random random;
public CrazyButtonTimer() {
b.setPreferredSize(new Dimension(250, 35));
frame.getContentPane().add(b);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
javax.swing.Timer timer = new Timer(500, new TimerListener());
timer.setInitialDelay(250);
timer.start();
}
private class TimerListener implements ActionListener {
private TimerListener() {
}
#Override
public void actionPerformed(final ActionEvent e) {
Color c = b.getForeground();
if (c == Color.red) {
b.setForeground(Color.blue);
} else {
b.setForeground(Color.red);
}
}
}
public static void main(final String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
CrazyButtonTimer crazyButtonTimer = new CrazyButtonTimer();
}
});
}
}
If you to change it on every fixed amount of time then you can use Swing Timer or Thread to do this. But for this you have to listen at least one action so that you can initialize and start it.
You can also use TimerTask class from java.util like follow:
java.util.TimerTask timerTask = new java.util.TimerTask() {
#Override
public void run() {
//change button text here using button.setText("newText"); method
}
};
java.util.Timer myTimer = new java.util.Timer();
myTimer.schedule(timerTask, 3 * 1000, 3* 1000); // This will start timer task after 3 seconds and repeat it on every 3 seconds.
I suggest you to create a timer (here you can find some doc)
Timer timer = new Timer(100,this);
Your class has to extend action listener ed implements the following method which allow you to change the text of your JButton(I called it ``button).
public void actionPerformed(ActionEvent e) {
if(e.getSource.equals(timer)){
button.setText("newText");
}
}
Luca
All the other answers fail to mention how to update non-periodically. If you need it to update irregularly, you can make a method in your GUI class called something like: updateButton(); and just call that every time you want it to change your text.
public void updateButton(String newText)
{
Button.setText(newText);
}
Just thought I'd add this in case someone wanted to set it irregularly.
If you want to change it periodically (e.g. every 5th second) you could create a new Thread which sets the text of the button to the desired value and repaints it (if necessary).
I have a frame which starts a swingTimer to perform a periodic task. The problem is when I close the frame, the task still continues. I want the swingTimer to stop if the close button is pressed.
I have tried specifying EXIT_ON_CLOSE and DISPOSE_ON_CLOSE but these do not work. Does someone know what I should do?
Thanks
Swing Timer has a stop method. You can always call that if the "frame" (JFrame??) ends via a WindowListener.
Also, per my tests, the Timer should stop on its own if the EDT stops. For example:
import java.awt.event.*;
import javax.swing.*;
public class StopTimer extends JPanel {
private static final float FONT_SIZE = 32;
private Timer myTimer;
private JLabel timerLabel = new JLabel("000");
private int count = 0;
public StopTimer() {
timerLabel.setFont(timerLabel.getFont().deriveFont(FONT_SIZE));
add(timerLabel);
int timerDelay = 1000;
myTimer = new Timer(timerDelay , new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
count++;
timerLabel.setText(String.format("%03d", count));
System.out.println("count: " + count);
}
});
myTimer.start();
}
private static void createAndShowUI() {
JFrame frame = new JFrame("StopTimer");
frame.getContentPane().add(new StopTimer());
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() {
public void run() {
createAndShowUI();
}
});
}
}
If this doesn't help you, then do what I've done: post a small compilable and runnable program that demonstrates your problem.