I have some labels that become visible when the letter a is pressed.
private void formKeyPressed(java.awt.event.KeyEvent evt) {
// TODO add your handling code here:
if(evt.getKeyCode()==KeyEvent.VK_A){
jLabel7.setVisible(true);
jLabel8.setVisible(true);
jLabel9.setVisible(true);
myBlink();
}
I have Label8 on a timer myBlink()
public void myBlink()
{
new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("begin");
jLabel8.setVisible(false);
jLabel8.setVisible(true);
System.out.println("Timer");
}
}).start();
}
I have placed printlns to see if timer begins and ends and when I press key "a" my output shows begin Timer multiple times but my label does not appear and disappear. What tweak does this code need? What am I missing? Thanks for the extra set of eyes.
This is probably because you call successively setVisible(false) and setVisible(true) which is done too fast to be seen, you should use a variable and modify its value any time the action of the Timer is called as next:
public void myBlink()
{
new Timer(1000, new ActionListener() {
boolean visible = true;
public void actionPerformed(ActionEvent e) {
jLabel8.setVisible(visible = !visible);
}
}).start();
}
Related
I am working on a swing project. There is a map, I have raster images of a given data for different times. Normally I change the time via a JSlider and it requests server for raster image. Then I add response image to map. There is a Play JButton, when pressed it will add those images one by one to raster layer of the map. It will be seen as an animation. In that JButton's actionPerfomed method I change the JSlider's value in a for loop.
My problem is when I press Play JButton, I can't see the data is played but I know code block works because I record each image(from server). I found out that it is becuse of JButton does not release Focus until its actionPerformed method ends. Because JButton looked like it was pressed until the end. So I only see the last image in the map.
First, I tried JButton.setFocusable(false) etc. but to no good.
Second, I tried using SwingWorker. I added it like this:
class PlayMvgmLayerWorker extends SwingWorker<Void, Void> {
public PlayMvgmLayerWorker(String title) {
super(title);
}
#Override
protected void done(Void aVoid) {
}
#Override
protected Void doInBackground() {
try{
BufferedImage[] image = new BufferedImage[24];
for(int i=0; i<24; i++) {
final int value = i - 48 + 24;
timeSlider.setValue( value );
Thread.sleep(10000l);
}
} catch(Exception ex) {
ex.printStackTrace();
}
return null;
}
}
private JButton animation = new JButton("");
animation.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PlayMvgmLayerWorker("title").execute();
}
});
private JSlider timeSlider = new JSlider();
timeSlider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
// time consuming processes ( server call, add image to map, etc)
}
});
I tried to simplify it.
It is much better than before, but I still can not see the data played properly. Sometimes data is played after JSlider ticks. Could it be because my time consuming process is in the second components(JSlider) stateChanged event? Should I use a second SwingWorker in JSlider's event too? Any suggestions about what can I do?
Moreover, what would be the best way to disable all components before playing data, and enable them after playing data?
Thank you very much in advance
If you have two activities Activity A and Activity B which have to be run simultaneously, you need to create a thread for the second activity - the first activity will already be run in its own thread (the main program).
The scheme is as follows:
Program A:
create new Thread: Activity B
run allother tasks for Activity A
In more specific terms the following program will run your simulation and update the tick of the slider:
public P() {
animation.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
doInBackgroundImp();
}
});
setSize(500, 500);
setLayout(new FlowLayout());
add(animation);
add(timeSlider);
setVisible(true);
}
protected void doInBackgroundImp() {
Thread th=new Thread() {
public void run() {
try{
for(int i=0; i<24; i++) {
final int value = i - 48 + 24;
timeSlider.setValue( i );
System.out.println(timeSlider.getValue()+" "+value);
Thread.sleep(1000);
}
} catch(Exception ex) {
ex.printStackTrace();
}
}
};
th.start();
}
private JButton animation = new JButton("");
private JSlider timeSlider = new JSlider();
}
I'm trying to stop the program for a second using Swing Timer.
Timer timer = new Timer(10000,
new ActionListener(public void actionPerformed(ActionEvent e) {}));
didn't work
public class Card extends JButton implements ActionListener {
int numberClick = 0;
public card() {
addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
numberClick++;
if(numberClick == 2) {
Timer timer = new Timer(10000, );
timer.start();
numberClick = 0;
}
}
}
You seem to lack basic understanding of how the Timer works. Please read How to Use Swing Timers. The concept is fairly simple.
The first argument in the Timer constructor is the delay. Seems you have that part down. The second argument is the ActionListener that listens for the "Timer Events" (actually ActionEvents). An event is fired each delayed time. The callback (actionPerformed) contains what should be performed after that delay (tick). So whatever you want to happen after that second, put it in the actionPerformed of the timer's ActionListener.
Also if you only want it t occur once, you should call timer.setRepeats(false);. Also note, you are using 10000, which is in milliseconds, so it's 10 seconds, not 1. You should change it to 1000
Example Flow
JButton button = new JButton("Press Me");
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
Timer timer = new Timer(1000, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Print after one second");
}
});
timer.setRepeats(false);
timer.start();
}
});
Press Button → Wait One Second → Print Statement
Is there a way to easily convert thread.sleep to javax.swing.timer?
The reason why I would need to do this, is to stop the user-interface from freezing when you press a button, so that you can implement a pause button.
Code Example:
btnStartTiming.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent arg0) {
try{
inputA = Double.parseDouble(txtEnterHowLong.getText()); //Changes double to string and receives input from user
}catch(NumberFormatException ex){
}
while (counter <= inputA){
txtCounter.setText(counter + "");
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
System.out.println(counter);
counter++;
}
}
});
Some tips:
Take a look to How to use Swing
Timers
trail and come back with concrete problems. Describe what are you trying to accomplish and your work so far, show your attempts to solve the problem and make an answerable question.
Don't use MouseListener
to listen when a button is pressed. Use ActionListener
instead. Take a look to How to Use Buttons, Check Boxes, and Radio
Buttons trail.
Put the java.swing.Timer in your constructor. You can use the button to .start() the timer.
Also instead of the while, you can add an if statement in the timer code check when to .stop()
Something like this
int delay = 1000;
Timer timer = new Timer(delay, null);
public Constructor(){
timer = new Timer(delay, new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (counter >= inputA) {
timer.stop();
} else {
// do something
}
}
});
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
}
I am trying to call a method repeatedly for as long as a button is pressed. But I get an infinite loop. Could anyone help me?
private void jButton6MousePressed(java.awt.event.MouseEvent evt) {
pressed = true;
while(pressed) {
car.accelerator();
}
}
private void jButton6MouseReleased(java.awt.event.MouseEvent evt) {
pressed = false;
}
Thanks.
You get an infinite loop because you have written an infinite loop, you need
JButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
car.accelerator();
}
});
If you want it to repeat the action while it's held own this is more difficult, here is an example of how you do that. In short you need to use threads.
I am pretty sure that once pressed is set to true, you never exit the while loop, so the fact that the button isn't being pressed never registers, the program is stuck. The only thing I can think of is using a timer to check periodically the state of the JButton. Alternatively, you can use multi-threading. That is, have PRESSED be a field in thread 1 that is set by the JButton (as you've done), and have the loop in a thread 2, checking on the status of PRESSED in thread 1.
Edit: Whoops, I see that bmorris591 has already suggested multithreading.
Swing thread enters an infinite loop.
You should run your loop in another thread:
private class BooleanHolder{
bool pressed;
};
final BooleanHolder pressed = new BooleanHolder();
private void jButton6MousePressed(java.awt.event.MouseEvent evt) {
presed.pressed=true;
Thread t = new Thread( new Runnable(){
public void run(){
while(pressed.pressed)
{
car.accelerator();
}
}
}
t.start();
}
private void jButton6MouseReleased(java.awt.event.MouseEvent evt) {
pressed.pressed=false;
}
Without knowing what car.accelerator(); does, it's impossible to make a accurate suggestion.
If car.accelerator() is interacting with the UI in any way, you need to be careful, you should never update the UI from any Thread other then the EDT.
Instead, you could use a rapid firing javax.swing.Timer
private Timer accelerateTimer;
//** ... **//
accelerateTimer = new Timer(15, new ActionListener() {
public void actionPerformed() {
car.accelerator();
}
});
accelerateTimer.setRepeats(true);
//** ... **//
public void mousePressed(MouseEvent me) {
accelerateTimer.restart();
}
public void mouseReleased(MouseEvent me) {
accelerateTimer.stop()
}
I'm making a level editor for my game. I have a property panel where I can modify the selected object its properties. I also have a Save button to write the level xml.
A field-edit is submitted(*) when the editor component lost the focus or Enter is pressed. This is working great, but the only problem is that when I have this sequence of actions:
Edit a field
Press the save button
Because, what happens is this:
I edit the field
I press the save button
The level is saved
The field lost the focus
The edit is submitted
As you can see, this is the wrong order. Of course I want the field to lose its focus, which causes the submit and then save the level.
Is there a trick, hack or workaround to make the field first lose the focus and then perform the action listener of the save button?
Thanks in advance.
(* submit = the edit to the field is also made in the object property)
EDIT: For the field I'm using a FocusAdapter with focusLost:
FocusAdapter focusAdapter = new FocusAdapter()
{
#Override
public void focusLost(FocusEvent e)
{
compProperties.setProperty(i, getColor());
record(); // For undo-redo mechanism
}
};
And for the button a simple ActionListener with actionPerformed`.
btnSave.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
// Save the level
}
});
Hmm ... can't reproduce: in the snippet below the lost is always notified before the actionPerfomed, independent on whether I click the button or use the mnemonic:
final JTextField field = new JTextField("some text to change");
FocusAdapter focus = new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
LOG.info("lost: " + field.getText());
}
};
field.addFocusListener(focus);
Action save = new AbstractAction("save") {
#Override
public void actionPerformed(ActionEvent e) {
LOG.info("save: " + field.getText());
}
};
save.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
JButton button = new JButton(save);
JComponent box = Box.createHorizontalBox();
box.add(field);
box.add(button);
On the other hand, focus is a tricky property to rely on, the ordering might be system-dependent (mine is win vista). Check how the snippet behave on yours.
If you see the same sequence as I do, the problem is somewhere else
if you get the save before the lost, try to wrap the the save action into invokeLater (which puts it at the end of the EventQueue, so it's executed after all pending events)
Action save = new AbstractAction("save") {
#Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
LOG.info("save: " + field.getText());
}
});
}
};
Normally, wrapping your save code into an SwingUtilities.invokeLater() should do the trick. As you already mentioned, this doesn't work? Try this:
private boolean editFocus = false;
FocusAdapter focusAdapter = new FocusAdapter()
{
#Override
public void focusGained(FocusEvent e){
editFocus = true;
}
#Override
public void focusLost(FocusEvent e){
compProperties.setProperty(i, getColor());
record(); // For undo-redo mechanism
editFocus = false;
if (saveRequested){
save();
}
}
};
and for your button:
private boolean saveRequested = false;
btnSave.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
if (editFocus){
saveRequested = true;
return;
} else {
save();
}
}
});
and then your save method:
private void save(){
// do your saving work
saveRequested = false;
}
This only works when your focusLost gets called after your button's action. If suddenly the order is correct, this code will get save() called twice.
But again, wrapping your save() code in your original approach should work, because the save code will execute after processing all events. That is after processing your button click and your focusLost events. Because your focusLost code executes immediately (it's not wrapped in an invokeLater()), the focusLost code should be executed always before your save code. This does not mean that the event order will be correct! But the code associated to the events will executed in the right order.