Good day!
I wanted use a standart Swing Timer with Full Screen Exclusive Mode. To this effect I applied a SwingWorker to control the event when graphic mode should be set. All following steps are executed in run method. run() is called from main.
1)First of all, I create my SwingWorker object and override two its methods(doInBackground and done). Init is important method because it should set all needfull graphic setting to current JFrame object and bind my key listener objet(called screen_list) with it:
...
worker = new SwingWorker<Window, Void>()
{
public Window doInBackground()
{
init();
return gdev.getFullScreenWindow();
}
public void done()
{
try {
disp = get();
}
catch (InterruptedException ignore) {}
catch (java.util.concurrent.ExecutionException e) {
String why = null;
Throwable cause = e.getCause();
if (cause != null) {
why = cause.getMessage();
} else {
why = e.getMessage();
}
System.err.println("Error retrieving file: " + why);
}
}
};
...
2)then I create my screenlistener that implements an ActionListener and a Key Listener, it is bound with disp as KeyListener in init() method:
private void init()
{
...
try
{
disp = gdev.getFullScreenWindow();
if(disp != null)
{
gdev.setDisplayMode(use_dm);
disp.createBufferStrategy(2);
disp.setFocusTraversalKeysEnabled(false);
disp.addKeyListener((KeyListener)screen_list);
}
}
catch(IllegalArgumentException ex)
{
}
...
}
3)I create and initialize my Swing Timer and start it;
4)And finally i call execute method:
public void run(int pause, int delay)
{
...
try
{
screen_list = new ScreenListener();
tm = new Timer(delay, screen_list);
tm.setInitialDelay(pause);
tm.setRepeats(true);
tm.start();
worker.execute();
}
catch(Exception e)
{}
...
}
Class ScreenListener as i have written implements a KeyListener and an ActionListener. In ActionPerfomed method i check out did worker do its job(init method), if yes, i get ref to current display mode and draw something:
class ScreenListener implements ActionListener, KeyListener
{
public void actionPerformed(ActionEvent e)
{
if(!worker.isDone())
{
return;
}
else
{
//gdev - GraphicsDevice type
disp = gdev.getFullScreenWindow();
if (disp != null)
{
...
draw(gr);
...
}
}
}
...
}
Why aren't events from keyboard processed?
I don't make all of those Swing calls from init. Init should set all needfull graphic setting to current JFrame object and bind key listener with it.
OK, I see you've changed your code some:
private void init()
{
...
try
{
disp = gdev.getFullScreenWindow();
if(disp != null)
{
gdev.setDisplayMode(use_dm);
disp.createBufferStrategy(2);
disp.setFocusTraversalKeysEnabled(false);
disp.addKeyListener((KeyListener)screen_list);
}
}
catch(IllegalArgumentException ex)
{
}
...
}
but you're still making Swing calls in init when you get the JFrame, set its display mode and buffer strategy, it's focus traversal business, and add a key listener. Why are these calls being made in a background thread as they wouldn't be expected to interfere with Swing processing (so no need to be done on the background), and are actually "Swing calls" since you're changing the state of Swing objects with them. doInBackground is for running long-running or cpu-intensive processes that if run on the EDT would freeze the GUI and make it unresponsive. The code you've shown does not do this. The danger of doing Swing calls in a background thread is that while it will work 95% of the time, it will fail at unexpected times causing your app to crash and burn, usually at the most inopportune time.
Also, why the empty catch block? I'd at least put in an ex.printStackTrace() in there so as not to fly blind.
2)then I create my screenlistener that implements an ActionListener and a Key Listener, it is bound with disp as KeyListener in init() method:
So am I right in stating that you're adding a KeyListener to a JFrame? I doubt that this will work since KeyListeners only respond if the bound component has the focus, something a JFrame would rarely do or want to do. Perhaps you wish to use the more versatile key bindings as this will allow greater flexibility with regards to focus and responsiveness.
3)then I create and initialize my Swing Timer and start it;
OK
4)And finally i call execute method. –
public void run(int pause, int delay)
{
...
try
{
screen_list = new ScreenListener();
tm = new Timer(delay, screen_list);
tm.setInitialDelay(pause);
tm.setRepeats(true);
tm.start();
worker.execute();
}
catch(Exception e)
{}
...
}
Again you've got an empty catch block.
And can you tell us more about your specific problems? We see bits and pieces of unrelated code with a vague description of sort of what it does, but don't really have a full idea of anything yet. Can you give us a more detailed description of your program and its problems? Are you trying to create the SSCCE as recommended by Andrew? If you could create and post this, we'd be much better able to test and modify your program and help you to a solution. Best of luck
I used a SwingWorker capabilities because full screen mode as yet had not set by the time timer already started.
Ок. I passed up using a SwingWorker. Instead of this I added a simple condition:
class ScreenListener implements ActionListener, KeyListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("ScreenListener: actionPerformed");
disp = gdev.getFullScreenWindow();
if(disp == null)
{
return;
}
else
{
//draw something
...
}
}
}
So now my run method looks like this
public void run(int pause, int delay)
{
screen_list = new ScreenListener();
init();
try
{
tm = new Timer(delay, (ActionListener)screen_list);
tm.setInitialDelay(pause);
tm.setRepeats(true);
tm.start();
}
catch(Exception ex)
{
ex.printStackTrace();
}
finally
{
if(!tm.isRunning())
{
...
}
}
}
And I make a focusable my disp:
private void init()
{
JFrame frame = new JFrame();
...
disp = new Window(frame);
DisplayMode[] dms = gdev.getDisplayModes();
DisplayMode use_dm = null;
if(gdev.isFullScreenSupported())
{
disp.setBackground(Color.CYAN);
disp.setForeground(Color.WHITE);
gdev.setFullScreenWindow(disp);
}
use_dm = getMatchMode(dms, def_dm);
try
{
disp = gdev.getFullScreenWindow();
if(disp != null)
{
...
disp.setFocusable(true);
disp.addKeyListener((KeyListener)screen_list);
...
}
}
catch(IllegalArgumentException ex)
{
ex.printStackTrace();
}
}
but I can't still catch my keyboard events. KeyTyped, KeyPressed, KeyReleased aren't still called so it is my problem in that programm.
My first aim was make a simple animation with full screen mode. At first i used a simple thread method - sleep - as for main thread. Then I added a swing timer for the same purpose but as you look I got a problem: I can't make to work my KeyListener.
I decided my problem:
1)Now FullScreen class extends from JFrame:
public class SimpleFullScreen extends JFrame
{
...
private synchronized void init()
{
Window disp = null;
//customize my display
setFocusable(true);
setResizable(false);
setIgnoreRepaint(true);
setUndecorated(true);
setBackground(Color.CYAN);
setForeground(Color.WHITE);
addKeyListener((KeyListener)screen_list);
DisplayMode[] dms = gdev.getDisplayModes();
DisplayMode use_dm = null;
if(gdev.isFullScreenSupported())
gdev.setFullScreenWindow(this);
use_dm = getMatchMode(dms, def_dm);
try
{
disp = gdev.getFullScreenWindow();
if(disp != null)
{
gdev.setDisplayMode(use_dm);
createBufferStrategy(2);
}
}
catch(IllegalArgumentException ex)
{
ex.printStackTrace();
}
}
...
}
2)Add loop in the run method, it checks out is timer running:
public void run(int pause, int delay)
{
Window disp = null;
screen_list = new ScreenListener();
init();
try
{
//Initialize and start timer
...
while(tm.isRunning())
{
System.out.println("Run: timer running");
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
finally
{
try
{
if(!tm.isRunning())
{
disp = gdev.getFullScreenWindow();
disp.setVisible(false);
disp.dispose();
gdev.setFullScreenWindow(null);
System.exit(0);
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
3) init, actionPerfomed and KeyPressed became the synchronized methods.
So ActionListener and KeyListener works good.
Thanks for responses!
Related
I'm having trouble with this code I am using to create a roulette wheel. The goal is to spin the wheel when I click the "SPIN!" button. I have done this by creating a for loop that should change the status of the wheel from true to false, which changes the orientation. This, when done fast enough, should create the illusion of movement.
THE PROBLEM I AM HAVING: is that my wheel is only repainting after the whole for loop is done, despite my placement of the repaint(). So, it only spins one tick.
Here is some sample code of my ActionListener:
public class spinListener implements ActionListener
{
RouletteWheel wheel;
int countEnd = (int)(Math.random()+25*2);
public spinListener(RouletteWheel w)
{
wheel = w;
}
public void actionPerformed(ActionEvent e)
{
for (int i = 0; i <countEnd; i++)
{
try
{
Thread.sleep(100);
if (wheel.getStatus() == true)
{
wheel.setStatus(false);
repaint();
}
if (wheel.getStatus() == false)
{
wheel.setStatus(true);
repaint();
}
}
catch (InterruptedException ex)
{
Logger.getLogger(WheelBuilder.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
UPDATE: I figured out the problem. Here are the changes I made for anyone having a similar problem.
public class spinListener implements ActionListener
{
Timer tm = new Timer(100, this);
int count = 0;
public void actionPerformed(ActionEvent e)
{
tm.start();
changeWheel();
}
public void changeWheel()
{
int countEnd = (int)(Math.random()+20*2);
if (count < countEnd)
{
wheel.setStatus(!wheel.getStatus());
repaint();
count++;
}
}
}
Swing is a single threaded environment, anything that blocks the Event Dispatching Thread, will prevent it from been able to process new events, including, paint events.
The use of Thread.sleep within the actionPerformed method is blocking the EDT, preventing it from processing new events, including paint events, until the actionPerformed method is exited.
You should use a javax.swing.Timer instead.
Take a look at Concurrency in Swing and How to Use Swing Timers for more details
I have 2 classes defined below:
public class TextsManager extends Thread {
LinkedList<String> lstOfPendingStr = new LinkedList<String>();
boolean stopLoop = false;
JTextArea txtArea;
public void run()
{
while (!stopLoop)
{
while (!lstOfPendingStr.isEmpty())
{
String tmp = lstOfPendingStr.getFirst();
this.txtArea.append(tmp);
lstOfPendingStr.removeFirst();
}
try {
Thread.sleep(0); // note: I had to force this code
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void AddNewStr(String newStr)
{
this.lstOfPendingStr.add(newStr);
}
}
And
public class ClientApp {
private JFrame frame;
private JTextField textField;
private JTextArea textArea;
static private TextsManager txtManager;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClientApp window = new ClientApp();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ClientApp() {
initialize();
/*
* Client app
*/
txtManager = new TextsManager(textArea);
txtManager.start();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
textArea = new JTextArea();
textField = new JTextField();
textField.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER)
{
txtManager.AddNewStr(textField.getText() + "\n");
textField.setText("");
}
}
});
}
}
The program will read User Input from textField, pass it into TextsManager.lstOfPendingStr. Then, on each loop inside TextsManager.run(), it will check for existed members in lstOfPendingStr and output them via txtArea.
The problem is that if I removed the code Thread.sleep(0) inside run(), the run() then apparently stopped working. Despite lstOfPendingStr had been successfully updated with new elements, codes inside the loop while(!lstOfPendingStr.isEmpty()) would not ever to be called.
I put hard codes such as System.out.println or Thread.sleep(0) (as in the provided code) inside the while(!stopLoop), then it worked fine.
Although, I managed to solve the problem by forcing the thread to sleep for a few miliseconds, I want to know the reason behind this issue.
I appreciate your wisdom.
Regard :)
You have a couple of problems.
You are calling methods on lstOfPendingStr from two threads, but initialized it with LinkedList, which is not thread-safe. You should use a thread safe class, LinkedBlockingQueue seems the best options as far as I understood from your code.
Inside the thread you are calling JTextArea#append(). As all AWT/Swing methods, you can not call them from arbitrary threads, but only from the AWT thread. Wrap the call inside an invokeLater block.
The fact that sleep appears to make your code work is just a sign of the concurrency problems.
I have a jList called todoList
When the user click on an item in the list, it stays selected. But I would like the currently selected item in the list to deselect "by itself" after 400 milliseconds when the mouse exits the jList.
This must only run if there is something already selected in the list.
I am using Netbeans IDE and this is what is have tried so far:
private void todoListMouseExited(java.awt.event.MouseEvent evt) {
if (!todoList.isSelectionEmpty()) {
Thread thread = new Thread();
try {
thread.wait(400L);
todoList.clearSelection();
} catch (InterruptedException ex) {
System.out.println(ex);
}
}
}
and
private void todoListMouseExited(java.awt.event.MouseEvent evt) {
if (!todoList.isSelectionEmpty()) {
Thread thread= Thread.currentThread();
try {
thread.wait(400L);
todoList.clearSelection();
} catch (InterruptedException ex) {
System.out.println(ex);
}
}
}
These both just make everything stop working.
My though process was that i need to create a new Thread that will wait for 400 milliseconds and then run the clearSelection() method of the jList. This would happen every time the mouse exits the list and run only if there is something in the list that is already selected.
I hope I am explaining my problem thoroughly enough.
The problem is that you are blocking the AWT-Event-Thread.
The solution is to use a swing timer:
private void todoListMouseExited(java.awt.event.MouseEvent evt)
{
if (!todoList.isSelectionEmpty()) {
new Timer(400, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
todoList.clearSelection();
}
}).start();
}
}
The problem is that Object#wait is waiting(rather than sleeping) to be notified but this is not happening. Instead the timeout causing an InterruptedException bypassing the call to clearSelection.
Don't use raw Threads in Swing applications. Instead use a Swing Timer which was designed to interact with Swing components.
if (!todoList.isSelectionEmpty()) {
Timer timer = new Timer(400, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
todoList.clearSelection();
}
});
timer.setRepeats(false);
timer.start();
}
I need to execute/display a series of events from a Arraylist to a JTextArea, however, each Event gets execute with different time. Following is a quick example of my goal:
public void start(ActionEvent e)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
jTextArea.append("Test" + "\n");
try
{
Thread.sleep(3000);
} catch (InterruptedException e1)
{
e1.printStackTrace();
}
jTextArea.append("Test1" + "\n");
}
});
}
So right now, "Test" and "Test1" display on JTextArea after whole execution is completed.
How do I make "Test" display first, then 3 secs later, display "Test1"
Thank u all in advance
invokeLater schedules the runnable to run on the Event Dispatch Thread. You shouldn't sleep within it or you will starve the dispatch thread. Try using a separate worker thread instead:
Thread worker = new Thread(new Runnable(){
public void run(){
jTextArea.append("Test" + "\n");
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
jTextArea.append("Test1" + "\n");
}
});
worker.start();
If your tasks are time/cpu intensive, then yes, definitely use a background thread to do this such as a SwingWorker object or a Runnable run in a Thread. If however what you need to do is to stagger the display of something and all you are looking for is the Swing equivalent of Thread.sleep(3000), then your best option is to use a Swing Timer. There is an excellent tutorial on how to use these which you can find here: http://download.oracle.com/javase/tutorial/uiswing/misc/timer.html
For example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Fu extends JPanel {
private static final int TIMER_DELAY = 600;
protected static final int MAX_COUNT = 20;
private JTextArea jTextArea = new JTextArea(10, 10);
private JButton startBtn = new JButton("Start");
private Timer timer;
public Fu() {
startBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
startAction(e);
}
});
add(new JScrollPane(jTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
add(startBtn);
}
private void startAction(ActionEvent e) {
if (timer != null && timer.isRunning()) {
// prevent multiple instances of timer from running at same time
return;
}
timer = new Timer(TIMER_DELAY, new ActionListener() {
private int count = 0;
public void actionPerformed(ActionEvent e) {
if (count < MAX_COUNT) {
count++;
jTextArea.append("Test " + count + "\n");
} else {
jTextArea.append("Done! \n");
timer.stop();
timer = null;
}
}
});
timer.setInitialDelay(0);
timer.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Foo");
frame.getContentPane().add(new Fu());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
As pointed out, this is a bad idea, as you will block the event thread.
However, understanding the reason for this is important as well. As you seem to know, all code that affects the state of Swing components needs to happen in the event handling thread (which is the reason why invokeLater and friends should always be used).
What is a bit less better known is that paining code also executes in the event handling thread. When your call to Thread.sleep is executing, it's not only blocking the event thread, it's also blocking any painting of components. This is why the full update appears to happen in one go -- the JTextArea is updated but it can't be repainted until your run method returns.
Lots of info available here: http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
First of all here are some code snippets:
public void startThread() {
this.animationThread = new Thread(this);
this.animationThread.start();
try {
this.animationThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void run() {
pirateMainAnimation.animate();
}
public void animate() {
for (int j = 0; j < 9; j++) {
try {
Thread.sleep(250);
} catch (InterruptedException e) {
break;
}
PirateAnimationPanel.getInstance().setCurrent(j);
PirateAnimationPanel.getInstance().repaint();
}
}
I'm trying to animate some images. The thing is that I want the main thread to wait for the animation thread to finish and then to continue. I searched around, read a little bit and decided to use the join() method. It perfectly waits for the thread to finish but I doesn't animate correctly. The repaint() method gets called 2 times instead of nine. I think maybe the problem is because I used singletons. Here is the singleton implementation.
import java.awt.Graphics;
import java.awt.MediaTracker;
import javax.swing.JPanel;
import uk.ac.aber.dcs.piratehangman.animation.PirateMainAnimation;
import uk.ac.aber.dcs.piratehangman.utilityclasses.AnimationThread;
#SuppressWarnings("serial")
public class PirateAnimationPanel extends JPanel {
private int current;
private MediaTracker mTracker;
private PirateMainAnimation pirateMainAnimation;
private AnimationThread animationThread;
private PirateAnimationPanel() {
this.current = 0;
this.pirateMainAnimation = new PirateMainAnimation();
mTracker = new MediaTracker(this);
this.animationThread = new AnimationThread();
setMediaTracker();
repaint();
}
private void setMediaTracker() {
for (int i = 0; i < 9; i++) {
mTracker.addImage(
this.pirateMainAnimation.getImagesForAnimation()[i],
this.pirateMainAnimation.getImagesForAnimationID()[i]);
try {
mTracker.waitForID(this.pirateMainAnimation
.getImagesForAnimationID()[i]);
} catch (InterruptedException e) {
System.out.println("Error loading image: " + i);
}
}
}
public void playAnimation() {
this.animationThread.startThread();
}
public void paintComponent(Graphics g) {
super.paintComponents(g);
System.out.println("called");
g.drawImage(this.pirateMainAnimation.getImagesForAnimation()[current],
0, 0, this);
}
private static class PirateAnimationPanelHolder {
private static final PirateAnimationPanel pirateAnimationPanel =
new PirateAnimationPanel();
};
public static PirateAnimationPanel getInstance() {
return PirateAnimationPanelHolder.pirateAnimationPanel;
}
public void setCurrent(int current) {
this.current = current;
}
public int getCurrent() {
return current;
}
}
I think you mean that the paintComponent() methods only gets called twice. Also I think you should be able to remove the call to super.paintComponents() if you fill the component to the background color.
The repaint() method only marks the component as dirty and requests a re-render on the next paint.
I would have expected the Swing thread to be able to repaint within the 250ms but I'm not sure what other work is being done/rendered. You might want to put a call to MediaTracker.waitForAll() before the animation.
While the static singleton is not adding much I don't think it is causing a problem (in this case).
Update:
So the problem is that the join() is on the Swing event Thread which is blocking the repainting of the component. I suggested a call like the following to show the "new game dialog after the last animation:
SwingUtilities.invokeLater(new Runnable() {
public void run() { showDialog(); }
})
"Note that events being posted to the EventQueue can be coalesced," which may explain the disparity. Also, be certain to build your GUI on event dispatch thread. See A More Complex Image Icon Example for a more detailed discussion.
Addendum: Improving Perceived Performance When Loading Image Icons has a nice SwingWorker example that may simplify the off-loading.