I have an array of strings which I'm trying to display (one by one) as a slideshow in a Java Swing component. I am also trying to add a delay time between the iterations.
I attempted to do this by using a JTextArea, with an action listener added to it. Here is the code I have right now:
private class myActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// A BUNCH OF TEXT PROCESSING
//NOTE: myInfo.getContents() returns an ArrayList<myType>.
Iterator<myType> iterator = myInfo.getContents().iterator();
int i = 0;
while (iterator.hasNext()) {
myTextArea.setText(iterator.next().toString());
// to add time betweeen iterations i wanted to use the thread
// delay method.
}
}
}
My code is not working because JTextArea doesn't have an action listener.
UPDATE
NOTE: Many replies stated that I should use an ActionListener for the JTextArea; However, Eclipse is not showing me that JTextArea has a method called addActionListener.
I'm kind of stuck here, which Java Swing component do you think would be the most suitable in this scenario?
The text in my array may be long, so a one lined label would not be a good choice.
What other alternatives or approaches do I have?
Thank you very much, any help and suggestions are appreciated.
This is basic example is based on the suggestion posted by #Robin
public class TestDisplayString {
public static void main(String[] args) {
new TestDisplayString();
}
public TestDisplayString() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextArea textArea;
private List<String> content;
private Iterator<String> iterator;
public TestPane() {
readText();
setLayout(new BorderLayout());
textArea = new JTextArea(10, 40);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
add(new JScrollPane(textArea));
iterator = content.iterator();
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (iterator.hasNext()) {
textArea.setText(iterator.next());
} else {
((Timer)e.getSource()).stop();
}
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
protected void readText() {
content = new ArrayList<>(25);
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/Text.txt")));
String text = null;
while ((text = reader.readLine()) != null) {
if (text.trim().length() > 0) {
content.add(text);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (Exception e) {
}
}
}
}
}
This is the contents of the "Text.txt" file.
How to Use Swing Timers
A Swing timer (an instance of javax.swing.Timer) fires one or more
action events after a specified delay. Don't confuse Swing timers with
the general-purpose timer facility that was added to the java.util
package in release 1.3. This page describes only Swing timers.
In general, we recommend using Swing timers rather than
general-purpose timers for GUI-related tasks because Swing timers all
share the same, pre-existing timer thread and the GUI-related task
automatically executes on the event-dispatch thread. However, you
might use a general-purpose timer if you don't plan on touching the
GUI from the timer, or need to perform lengthy processing.
You can use Swing timers in two ways:
To perform a task once, after a delay.
For example, the tool tip manager uses Swing timers to determine when to show a tool tip and when to hide it.
To perform a task repeatedly.
For example, you might perform animation or update a component that displays progress toward a goal.
Swing timers are very easy to use. When you create the timer, you
specify an action listener to be notified when the timer "goes off".
The actionPerformed method in this listener should contain the code
for whatever task you need to be performed. When you create the timer,
you also specify the number of milliseconds between timer firings. If
you want the timer to go off only once, you can invoke
setRepeats(false) on the timer. To start the timer, call its start
method. To suspend it, call stop.
Note that the Swing timer's task is performed in the event dispatch
thread. This means that the task can safely manipulate components, but
it also means that the task should execute quickly. If the task might
take a while to execute, then consider using a SwingWorker instead of
or in addition to the timer. See Concurrency in Swing for instructions
about using the SwingWorker class and information on using Swing
components in multi-threaded programs.
Let's look at an example of using a timer to periodically update a
component. The TumbleItem applet uses a timer to update its display at
regular intervals. (To see this applet running, go to How to Make
Applets. This applet begins by creating and starting a timer:
timer = new Timer(speed, this); timer.setInitialDelay(pause);
timer.start();
The speed and pause variables represent applet parameters; as
configured on the other page, these are 100 and 1900 respectively, so
that the first timer event will occur in approximately 1.9 seconds,
and recur every 0.1 seconds. By specifying this as the second argument
to the Timer constructor, TumbleItem specifies that it is the action
listener for timer events.
After starting the timer, TumbleItem begins loading a series of images
in a background thread. Meanwhile, the timer events begin to occur,
causing the actionPerformed method to execute:
public void actionPerformed(ActionEvent e) {
//If still loading, can't animate.
if (!worker.isDone()) {
return;
}
loopslot++;
if (loopslot >= nimgs) {
loopslot = 0;
off += offset;
if (off < 0) {
off = width - maxWidth;
} else if (off + maxWidth > width) {
off = 0;
}
}
animator.repaint();
if (loopslot == nimgs - 1) {
timer.restart();
} }
Until the images are loaded, worker.isDone returns false, so timer
events are effectively ignored. The first part of the event handling
code simply sets values that are employed in the animation control's
paintComponent method: loopslot (the index of the next graphic in the
animation) and off (the horizontal offset of the next graphic).
Eventually, loopslot will reach the end of the image array and start
over. When this happens, the code at the end of actionPerformed
restarts the timer. Doing this causes a short delay before the
animation sequence begins again.
Use your ActionListener in combination with a javax.Swing.Timer. The ActionListener assigned to the Timer will be called on regular intervals with the specified delay.
See the timer tutorial for more information
Related
In the code below I am running my method which adds text to a JTextArea, then i wait 4 seconds and add more text. However it simply waits four seconds then puts all the text down at once. How can I make it so it adds the first text, waits then adds the second block of text?
public static void configuresettings() {
GUI.add("To Begin, Go to www.opionsxo.com");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
GUI.add("Welcome to Configure Settings!");
}
I figured it out, if anyone is interested in how. Then view the code below...
public static void configuresettings() {
GUI.add("To Begin, Go to www.opionsxo.com");
ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
GUI.add("Welcome to Configure Settings!");
}
};
Timer timer = new Timer( 4000, actionListener );
timer.setRepeats(false);
timer.start();
}
You shouldn't use Thread.sleep with swing, or it will block your application. For instance, if you resize your window or move a window in front of your application, it won't repaint as needed, because you stopped it. Instead, you should be using Swing Timers.
Your problem is probably because you should revalidate() your JTextArea before sleep or something, but as said, you should change the implementation of it.
Read more here: http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
Don't do anything within the context of the Event Dispatching Thread that might cause it to stop, like calling Thread.sleep.
Instead, trying using a javax.swing.Timer or SwingWorker
Take a look at:
Concurrency in Swing
Worker Threads and SwingWorker
How to Use Swing Timers
For more details
For example:
java for-loop in GUI TextArea
Displaying contents of String array in Swing component as iterations using Time delay. JAVA
I cant run JTextArea multiple times?
How can I smoothly move the JButton if action performed (eg the button is pushed). Here's my example but it doesnt work propely:
public void actionPerformed(ActionEvent event) {
for(int i = 0; i<50; i++){
ww.button.setLocation(ww.button.getLocation().x+1, ww.button.getLocation().y);//ww is a JFrame child
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
After action performed I get a delay in 20*50 ms and button location set in previous loc + 50px, without intermediate locations.
Always try to avoid Thread while working with Swing. In your code it will make main EDT thread to sleep and that's why you are not able to view intermediate locations. Try this with using Swing Timer and you will get it working as intended. Take a look here: How to Use Swing Timers and also try using Swing workers.
Basically, I have this game where once guesses the correct answer it starts a new game with a new word. I want to display Correct! but after three seconds, change it to a empty string. How do I do that?
My attempt:
if (anagram.isCorrect(userInput.getText()))
{
anagram = new Anagram();
answer.setText("CORRECT!");
word.setText(anagram.getRandomScrambledWord());
this.repaint();
try
{
Thread.currentThread().sleep(3000);
}
catch (Exception e)
{
}
answer.setText("");
} else
{
answer.setForeground(Color.pink);
answer.setText("INCORRECT!");
}
Edit:
My solution:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt)
{
// TODO add your handling code here:
if (anagram.isCorrect(userInput.getText()))
{
answer.setText("CORRECT!");
ActionListener taskPerformer = new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
anagram = new Anagram();
word.setText(anagram.getRandomScrambledWord());
answer.setText("");
userInput.setText("");
}
};
Timer timer = new Timer(3000, taskPerformer);
timer.setRepeats(false);
timer.start();
} else
{
answer.setForeground(Color.pink);
answer.setText("INCORRECT!");
}
}
I am not sure, but I hope that I am following MadProgrammer's advice and not blocking the event itself, but the new thread. I will look up Java Timer also.
Swing is an event driven environment. While you block the Event Dispatching Thread, no new events can be processed.
You should never block the EDT with any time consuming process (such as I/O, loops or Thread#sleep for example).
You might like to have a read through The Event Dispatch Thread for more information.
Instead, you should use a javax.swing.Timer. It will trigger a ActionListener after a given delay.
The benefit of which is that the actionPerformed method is executed with the context of the Event Dispatching Thread.
Check out this or this or this or this for an examples
it works after 3 seconds..
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
statusbar.setText("Status");
}
};
Timer timer = new Timer(3000, taskPerformer);
timer.setRepeats(false);
timer.start();
if these piece of code is in the event handlers, then you are holding up the UI thread, and it is not going to work as UI update will only happens after you finished your work in the event handlers.
You should create another thread do the work of "sleep 3 second, and change the text field, and trigger repaint". Using Timer or similar utilities is the easiest way to achieve what I am describing.
I have an array of strings which I'm trying to display (one by one) as a slideshow in a Java Swing component. I am also trying to add a delay time between the iterations.
I attempted to do this by using a JTextArea, with an action listener added to it. Here is the code I have right now:
private class myActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// A BUNCH OF TEXT PROCESSING
//NOTE: myInfo.getContents() returns an ArrayList<myType>.
Iterator<myType> iterator = myInfo.getContents().iterator();
int i = 0;
while (iterator.hasNext()) {
myTextArea.setText(iterator.next().toString());
// to add time betweeen iterations i wanted to use the thread
// delay method.
}
}
}
My code is not working because JTextArea doesn't have an action listener.
UPDATE
NOTE: Many replies stated that I should use an ActionListener for the JTextArea; However, Eclipse is not showing me that JTextArea has a method called addActionListener.
I'm kind of stuck here, which Java Swing component do you think would be the most suitable in this scenario?
The text in my array may be long, so a one lined label would not be a good choice.
What other alternatives or approaches do I have?
Thank you very much, any help and suggestions are appreciated.
This is basic example is based on the suggestion posted by #Robin
public class TestDisplayString {
public static void main(String[] args) {
new TestDisplayString();
}
public TestDisplayString() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextArea textArea;
private List<String> content;
private Iterator<String> iterator;
public TestPane() {
readText();
setLayout(new BorderLayout());
textArea = new JTextArea(10, 40);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
add(new JScrollPane(textArea));
iterator = content.iterator();
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (iterator.hasNext()) {
textArea.setText(iterator.next());
} else {
((Timer)e.getSource()).stop();
}
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
protected void readText() {
content = new ArrayList<>(25);
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/Text.txt")));
String text = null;
while ((text = reader.readLine()) != null) {
if (text.trim().length() > 0) {
content.add(text);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (Exception e) {
}
}
}
}
}
This is the contents of the "Text.txt" file.
How to Use Swing Timers
A Swing timer (an instance of javax.swing.Timer) fires one or more
action events after a specified delay. Don't confuse Swing timers with
the general-purpose timer facility that was added to the java.util
package in release 1.3. This page describes only Swing timers.
In general, we recommend using Swing timers rather than
general-purpose timers for GUI-related tasks because Swing timers all
share the same, pre-existing timer thread and the GUI-related task
automatically executes on the event-dispatch thread. However, you
might use a general-purpose timer if you don't plan on touching the
GUI from the timer, or need to perform lengthy processing.
You can use Swing timers in two ways:
To perform a task once, after a delay.
For example, the tool tip manager uses Swing timers to determine when to show a tool tip and when to hide it.
To perform a task repeatedly.
For example, you might perform animation or update a component that displays progress toward a goal.
Swing timers are very easy to use. When you create the timer, you
specify an action listener to be notified when the timer "goes off".
The actionPerformed method in this listener should contain the code
for whatever task you need to be performed. When you create the timer,
you also specify the number of milliseconds between timer firings. If
you want the timer to go off only once, you can invoke
setRepeats(false) on the timer. To start the timer, call its start
method. To suspend it, call stop.
Note that the Swing timer's task is performed in the event dispatch
thread. This means that the task can safely manipulate components, but
it also means that the task should execute quickly. If the task might
take a while to execute, then consider using a SwingWorker instead of
or in addition to the timer. See Concurrency in Swing for instructions
about using the SwingWorker class and information on using Swing
components in multi-threaded programs.
Let's look at an example of using a timer to periodically update a
component. The TumbleItem applet uses a timer to update its display at
regular intervals. (To see this applet running, go to How to Make
Applets. This applet begins by creating and starting a timer:
timer = new Timer(speed, this); timer.setInitialDelay(pause);
timer.start();
The speed and pause variables represent applet parameters; as
configured on the other page, these are 100 and 1900 respectively, so
that the first timer event will occur in approximately 1.9 seconds,
and recur every 0.1 seconds. By specifying this as the second argument
to the Timer constructor, TumbleItem specifies that it is the action
listener for timer events.
After starting the timer, TumbleItem begins loading a series of images
in a background thread. Meanwhile, the timer events begin to occur,
causing the actionPerformed method to execute:
public void actionPerformed(ActionEvent e) {
//If still loading, can't animate.
if (!worker.isDone()) {
return;
}
loopslot++;
if (loopslot >= nimgs) {
loopslot = 0;
off += offset;
if (off < 0) {
off = width - maxWidth;
} else if (off + maxWidth > width) {
off = 0;
}
}
animator.repaint();
if (loopslot == nimgs - 1) {
timer.restart();
} }
Until the images are loaded, worker.isDone returns false, so timer
events are effectively ignored. The first part of the event handling
code simply sets values that are employed in the animation control's
paintComponent method: loopslot (the index of the next graphic in the
animation) and off (the horizontal offset of the next graphic).
Eventually, loopslot will reach the end of the image array and start
over. When this happens, the code at the end of actionPerformed
restarts the timer. Doing this causes a short delay before the
animation sequence begins again.
Use your ActionListener in combination with a javax.Swing.Timer. The ActionListener assigned to the Timer will be called on regular intervals with the specified delay.
See the timer tutorial for more information
I already have a post related to the multithreading issue but I have some new questions+code. I have a multiball game project and it requires me to parse an XML file to obtain information about the ball(like size, speed, initial position etc). Now, I wanted to create a different thread to parse the XML file, but I cannot figure out a way to do it. Here is my code:
main() starts here:
public class BounceBallApp extends JFrame{
public BounceBallApp()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("BounceBallApp");
setSize(300,300);
setVisible(true);
add(new BallWorld());
validate();
}
public static void main(String[] args)
{
/*Create main GUI in the Event Dispatch Thread*/
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new BounceBallApp(); //main frame
}
});
}
}
Within the constructor for BallWorld(), I have an inner class BallContainer(), which contains a Start button:
jbtStart.addActionListener(new ActionListener()
{public void actionPerformed(ActionEvent e)
{
//Populate the ballList arraylist
if(filePathField.getText().equals(" "))
{
JOptionPane.showMessageDialog(null, "Please input the XML file","Information", JOptionPane.INFORMATION_MESSAGE);
}
else
{
XMLFilePath = filePathField.getText();
ballList = new BallList(XMLFilePath);//I want to put this in a thread
JOptionPane.showMessageDialog(null,"Game started!","Bouncing Balls",JOptionPane.INFORMATION_MESSAGE);
for(Ball ball:ballList.ballsArrayList)
{
timer.setDelay(1000/ball.getSpeed()); //change the delay of the timer to the ball's speed
timer.start(); //start the timer
bTimer = true; //timer is now on
}
}
}
});
}
Now the problem is that if I put the parsing process in another thread, then I have to wait for the ballsArrayList to fill before I can continue with the application. I was thinking of using invokeAndWait() but I read that that method cannot be called from the Event Dispatch Thread. So, How can I achieve this? Or is it even worthwhile?
Also, I wanted to move the calculation for moving the ball (calculating the x,y coords) to a thread, but again, I don't know how to implement it.
for(Ball ball:ballList.ballsArrayList)
{
ball.draw(g);
ball.move(ballContainerWidth,ballContainerHeight,buttonPanel.getHeight());
}
public void move(int ballContainerWidth,int ballContainerHeight,int buttonPanelHeight)
{
if((this.getX()+this.getsize()+this.getDx()) > ballContainerWidth)
{
this.setDx(-Math.abs(this.getDx()));
}
//the height/depth to which the balls can bounce is the (main ball container height) minus (button panel height)
if((this.getY()+this.getsize()+this.getDy()) > ballContainerHeight-buttonPanelHeight)
{
this.setDy(-Math.abs(this.getDy()));
}
if((this.getX()-this.getsize()) < 0 )
{
this.setDx(Math.abs(this.getDx()));
}
if((this.getY()-this.getsize()) < 0 )
{
this.setDy(Math.abs(this.getDy()));
}
int newX = (int)Math.round((this.getX()+this.getDx()));
int newY = (int)Math.round((this.getY()+this.getDy()));
this.setX(newX);
this.setY(newY);
}
Sorry for the long post, but multithreading is all new to me. I am a bit confused about it.
Initial loading of the files
I personally would opt for one of the following approaches
Increase the start-up time of your program by parsing all the files during start-up. For a few XML files this overhead might be very small. If it takes too long, you can consider showing a splash screen
Load the XML files when the start button is pressed, but show a progress bar until the loading is done. Start the game afterwards. A SwingWorker can help you with this. Examples can be found in the Swing documentation or here on SO.
Updating of the ball position
If the calculation is as easy as what is shown here, I would simply use a javax.swing.Timer to update the position on regular time intervals, and do the calculation on the Event Dispatch Thread.
If you want to do the calculation on a background thread just for the exercise, I would still opt for a calculation of the position on a background thread. The calculation should be using local variables which are only know to that background thread. Once the new position is calculated, update the position of the ball on the Event Dispatch Thread using SwingUtilities#invokeLater. This allows you to access the position during the paint operation without having to worry about threading issues. Probably easier then messing around with locks.