So I am currently having a problem with my Java Swing timer but first lemme describe what I am trying to do.
So I have a Swing GUI that updates a map with JSON data every 'X' number of seconds. The user can input the number of seconds into a text field and then click a button to start updating the map. The map will then update by querying the JSON based on the input.
So I am using a Swing timer to repeat a certain action event based on the input of the the user. Seen below:
clickOkButton.addActionListener(e1 -> {
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String url = "";
url = new_text.getText();
layer[0] = (RenderableLayer) geo.createLayerFromSource(url);
appFrame.getWwd().getModel().getLayers().set(20, layer[0]);
ltree.getModel().refresh(appFrame.getWwd().getModel().getLayers());
}
};
int time = Integer.parseInt(queryTime.getText());
Timer timer = new Timer(time * 1000, actionListener);
timer.setRepeats(true);
//timer.setDelay(1);
timer.start();
d.setVisible(false);
//System.out.println(text);
});
When the program is launched whatever time the user enters first works great. But then if they change the time the timer doesn't change.
int time = Integer.parseInt(queryTime.getText());
Timer timer = new Timer(time * 1000, actionListener);
It has something to do with these lines but I just can't figure it out. I'm pulling the numerical value from the text field and setting it as the delay in the timer. But it only works the first time the program is launched and not when it is changed.
Any help would be much appreciated.
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SwingTimerDemo extends JPanel {
final static int height = 500;
final static int width = 500;
final static String title = "default title";
JFrame frame = new JFrame(title);
JTextField field = new JTextField(10);
JTextArea area = new JTextArea(50,20);
public static void main(String[] args) {
SwingUtilities.invokeLater(
() -> new SwingTimerDemo().start());
}
public SwingTimerDemo() {
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
// add this panel to the frame
frame.add(this);
// add the JTextArea and JTextField to the panel
add(area);
add(field);
setPreferredSize(
new Dimension(500, 500));
frame.pack();
// center the frame on the screen
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void start() {
// append the string to the JTextArea
Timer t = new Timer(0, (ae)->area.append("Processing...\n"));
// set the inter-event delay to 2 seconds
t.setDelay(2000);
// start the timer
t.start();
field.addActionListener(ae->{
String text = field.getText();
field.setText(""); // "erase" the text
// convert to a number
int delay = Integer.parseInt(text);
// reset the timer delay
t.setDelay(delay*300);
});
}
}
Assuming you are familiar with Frames and Panels I will skip to the JTextField and JTextArea.
the field is where the user types in the delay. It is notified using an actionListener. That input is then retrieved, parsed as an int and sets the timer delay.
the area is simply a place where the timer writes the output.
Note that instead of an event when the user types in information, a button could be used instead. The user types in the information and then clicks the button. In that case, there would be no need for JTextField listener. Instead the listener would be for the button to check the text field.
This is a very rudimentary example to demonstrate the interaction between two actionListeners. If the user types in anything but a number an exception will be thrown. You may want to check out the Java Tutorials where they talk about event handling and other things that you would find interesting.
Related
I am a fairly new user with programming in Java with about a week and a bit experience, as of before I have been using python for about 3 years but thought to give java a try.
I have been trying to develop my skills by creating small projects and applications and am now creating a small GUI counter.
I have achieved creating the GUI with 2 buttons and a label and have tested the maths behind the application but I am struggling to work out how the ActionListener works as it feels a lot different to python when making a button have a action.
This is My Code;
package gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.*;
public class GUI{
//This creates a frame or panel to contain things
public static void main(String[] args) {
//Maths To The Counter
int Counter = 0;
System.out.println(Counter);
Counter =+ 1;
System.out.println(Counter);
//Creating The Frame
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setBackground(Color.WHITE);
frame.getContentPane().add(panel);
//Creating The Label
JLabel label3 = new JLabel("Counter: ");
panel.add(label3);
//Button Which should have a funtion to add and display the number
JButton button = new JButton("Click Here.");
panel.add(button);
//Button to reset the counter
JButton buttonReset = new JButton("Reset Counter.");
panel.add(buttonReset);
//Set Size Of Window
frame.setSize(new Dimension(500, 400));
//Set Starting Position to centre
frame.setLocationRelativeTo(null);
//Setting a default close action
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set Title
frame.setTitle("Counter");
//Disable Resize
frame.setResizable(false);
//Setting if its visible
frame.setVisible(true);
//Fits frame to fit everything
frame.pack();
}
}
enter code here
I know that in python a action is in a function so that has been my logic to this problem however I have seen that I need to use the actionlistener instead and I am struggling to get my head around it.
If Someone could show me how this type of action should be implemented it would be great help, I have watch some youtube videos and done a bit of research but im still struggling to understand in my situation how to do it.
For any confussion im sorry, overall my question is how do I add a action to a button in my program that can implement my maths at the start.
As well any feedback on the structure of my code would be welcomed as I am just starting in java and I do know poor structure can lead to mistakes.
This code should work:
Basically, in the main method I am creating an instance of the class and calling a method to create the gui.
I also created an instance variable as the counter, otherwise you won't be able to update the variable in your action listener.
public class Gui {
private int counter;
// This creates a frame or panel to contain things
public static void main(String[] args) {
Gui gui = new Gui();
gui.create();
}
private void create() {
// Creating The Frame
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setBackground(Color.WHITE);
frame.getContentPane().add(panel);
// Creating The Label
JLabel label3 = new JLabel("Counter: ");
panel.add(label3);
// Button Which should have a funtion to add and display the number
JButton button = new JButton("Click Here.");
panel.add(button);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(counter++);
}
});
// Button to reset the counter
JButton buttonReset = new JButton("Reset Counter.");
panel.add(buttonReset);
buttonReset.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
counter = 0;
}
});
// Set Size Of Window
frame.setSize(new Dimension(500, 400));
// Set Starting Position to centre
frame.setLocationRelativeTo(null);
// Setting a default close action
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Set Title
frame.setTitle("Counter");
// Disable Resize
frame.setResizable(false);
// Setting if its visible
frame.setVisible(true);
// Fits frame to fit everything
}
}
With Lambda expressions, you can simplify your action listeners as follows:
button.addActionListener(a -> System.out.println(counter++));
buttonReset.addActionListener(a -> counter = 0);
If you want to write more than 1 statement, then you can just put your code in curly brackets:
button.addActionListener(a -> {
System.out.println(counter++);
System.out.println("doing more stuff...");
});
JButton has a function called addActionListener. You can pass on an action listener by doing this:
button.addActionListener(() -> {
// Do some logic here
});
Here, I use a lambda expression as an action listener. Within the lambda expression you can place whatever logic you want to have.
Also note that you can add multiple different action listeners to the same button. In a nutshell, the way the JButton interacts with the ActionListeners is based on the observer-pattern.
Imagine this: When the JButton is pressed, it will notify all of it's observers saying "Hey, I have been pressed". Each observer can then independently decide what to do. In case of the JButton, all observers are ActionListeners. If you add multiple ActionListeners then the JButton will notify all of them, and as a result all of their actionPerformed(ActionEvent e) functions are executed. In the example above, I used a lambda expression which then by java is interpreted as an ActionListener.
Other ways to achieve the exact same functionality are:
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Do some logic here
}
});
In the example above, you use an anonymous class as an actionlistener.
public class MyClass {
public MyClass() {
JButton button = new JButton("press me");
button.addActionListener(new MyActionListener());
}
private class MyActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// Do some logic here
}
}
}
In the example above, an inner class is used.
In a nutshell, there is a ton of ways you can make your button have functionality. Above are just a few examples of how to do so.
Does this clarify it a bit more, or do you have some remaining questions?
My Thread.sleep(rand.nextInt(delay)) command in my ButtonListener class crashes my GUI. Any ideas? The program is supposed to add people to an ArrayList, then randomly select them and display them at a random time between 0 and the timeText JTextField, and it works until I add the sleep command. Any help would be appreciated thanks!
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
import java.awt.*;
import javax.swing.Timer;
public class MyProgram extends AppClass{
protected int x,y,width,height;
protected Color color;
private ArrayList<String> people = new ArrayList<String>();
private static JLabel person;
private Timer timer;
private ButtonListener listener;
private Random rand = new Random();
private JLabel addPeople;
private JTextField newPerson;
private JTextField timeText;
private Font font1 = new Font("Arial",1,17);
private Font font2 = new Font("Arial",1,65);
public MyProgram(){
setPreferredSize(new Dimension(1000,800));
people.add("me");
people.add("john");
people.add("greg");
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(600,400));
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
newPerson = new JTextField(2);
newPerson.setFont(font1);
addPeople = new JLabel("Add people:");
addPeople.setFont(font1);
person = new JLabel();
person.setFont(font2);
JButton addButton = new JButton("Add");
addButton.setFont(font1);
JButton startButton = new JButton("Start");
startButton.setFont(font1);
timeText = new JTextField(2);
timeText.setFont(font1);
JLabel time = new JLabel("Maximum time between draws:");
time.setFont(font1);
listener = new ButtonListener();
addButton.addActionListener(listener);
startButton.addActionListener(listener);
panel.add(addPeople);
panel.add(newPerson);
panel.add(addButton);
panel.add(time);
panel.add(timeText);
panel.add(startButton);
panel.add(person);
add(panel);
}
private class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent ae){
JButton button = (JButton) ae.getSource();
if(button.getText().equals("Add")){
people.add(newPerson.getText());
System.out.println(newPerson.getText());
System.out.println("also worked");
}else if(button.getText().equals("Start")){
int delay = Integer.parseInt(timeText.getText());
for(;;){
person.setText(people.get(rand.nextInt(people.size())));
try{
Thread.sleep(rand.nextInt(delay)); // the problem
}catch(Exception error){
System.out.println("Error");
}
}
}
}
}
}
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
import java.awt.*;
import javax.swing.Timer;
public class AppClass extends JPanel{
public static void main(String [] args){
JFrame frame = new JFrame();
frame.getContentPane().add(new Get());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
frame.setTitle("My Program");
}
}
There is only one thread that modifies the UI. This is by design, because it means that, even with the many sources of events that a UI could receive, it is impossible for concurrency bugs to corrupt the UI state.
There is only one thread that modifies the UI. That includes:
Receiving mouse events.
Receiving keyboard events.
Processing requests to repaint.
Processing UI timers.
Among many others.
If you're in code that modifies the UI, then you must be on the UI thread (else, you have a bug). If you're on the UI thread and you call Sleep(), then the UI thread stops doing things.
It'll stop responding to requests to repaint. It'll stop responding to keyboard events, mouse events, etc.
Instead, you have to use a forms timer to perform an animation. When someone clicks your "Start" button, you would set the first value, save off the rest of the values, and then start your timer, and then let the UI thread keep processing.
Each time the timer fires, you advance the state - you update the UI with the next value you want to show. You continue this until you've shown all values, and then stop the timer and release your state that told you where you were in the animation.
Yeah, you're just setting the text on some component, but this still falls under the animation pattern.
Be careful though - if your UI is closed while the animation timer is still running, you'll try to modify a UI that is gone. So now your UI code has to be careful to stop the animation timer if it's still running when the UI is closed.
actually, you are execute your gui into same thread and when you are use Thread.sleep(delay) you are also sleep you gui. so you have to use new thread for other processing.
Thread t = new Thread(new Runnable(){
#Override
public void run() {
}
});
So I am new in swing and working on the hangman game. So the way it is supposed to work is that the user is prompt to welcome message which will last a 3seconds, disappears, and then sends the user to the next frame. Everything is working perfectly except that when I run it the first frame is still visible and running in the shadow even though it goes to the next one. I have tried to use the dispose method but it's just closing the frame without going to the next one.
Here is my what I have done so far
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.Timer;
public class PA1test extends JFrame{
public static void main(String[] args) {
// opens the first page
JFrame gui = new JFrame("Hangman");
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.add(new First_PageImage());
gui.pack();
gui.setVisible(true);
// action to open the second page
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
JFrame secpage = new JFrame("Hangman");
secpage.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
secpage.add(new SecondPage());
secpage.pack();
secpage.setVisible(true);
}
};
// set timer to perform action after 3 seconds
Timer timer = new Timer(3000 ,taskPerformer);
timer.setRepeats(false);
timer.start();
}
}
A few points:
Setting the dispose operation to JFrame.EXIT_ON_CLOSE will exit your entire application. Consider using JFrame.DISPOSE_ON_CLOSE
Sounds like you wish to create a splash screen - consider reading Oracle's tutorial on using the SplashScreen class
If your intent is a splash screen and you wish to use a JFrame or JDialog rather than java's build in SplashScreen from (2), consider designing it to look like a splash screen by removing the decorations (setUndecorated(true)) and centering (setLocationRelativeTo(null);)
In order to dispose/hide the initial JFrame/splash, you need to do so after the Timer has fired, which can be done from within the Timer's ActionListener implementation - in order to access the splash screen instance from within the anonymous class, you must mark it as final.
Pseudo-code:
final JFrame splashScreen = new JFrame("Hangman");//mark as final for visibility's sake
splashScreen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
//logic to create the main UI frame here
splashScreen.dispose();
}
};
// set timer to perform action after 3 seconds
Timer timer = new Timer(3000 ,taskPerformer);
timer.setRepeats(false);
timer.start();
Try .setVisible(false) on the first frame.
The class is called operating systems and I have to make a simulated clock that reads information at certain time and does something with it but I am only asking about the clock part here. Right now, I have 4 buttons which are run, tick, read, and show status. I have an input, an output, and a timer text field. I have a clockstarter class that starts the clock when the program starts and writes it in the console. I'm supposed to have two threads so that while that is happening I can click "run" and the timer textfield starts continuously counting or if run is not clicked, I can manually add time with tick. I need help getting it to show in the text field.
public class ClockStarter implements Runnable {
private Thread thread;
private int currentTime;
private javax.swing.JTextField time;
public ClockStarter(javax.swing.JTextField t){
System.out.println("Clock Starter Constructor");
currentTime = -1;
time = t;
thread = new Thread(this);
thread.start();
}
public void run(){
while(true){
incrementTime();
System.out.println("Clock Starter Current Time ");
time = ("" + getCurrentTime());// I need to fix this line
try{Thread.sleep(1000);} catch(Exception e){}
}
}
public void incrementTime(){
currentTime++;
}
public int getCurrentTime(){
return currentTime;
}
}
Do not use Thread.sleep() 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.
Please read the Swing tutorial How to Use Text Fields:
A text field is a basic text control that enables the user to type a small amount of text. When the user indicates that text entry is complete (usually by pressing Enter), the text field fires an action event. If you need to obtain more than one line of input from the user, use a text area.
Example Code
/* TextDemo.java requires no other files. */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TextDemo extends JPanel implements ActionListener {
protected JTextField textField;
protected JTextArea textArea;
private final static String newline = "\n";
public TextDemo() {
super(new GridBagLayout());
textField = new JTextField(20);
textField.addActionListener(this);
textArea = new JTextArea(5, 20);
textArea.setEditable(false);
JScrollPane scrollPane = new JScrollPane(textArea);
//Add Components to this panel.
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.fill = GridBagConstraints.HORIZONTAL;
add(textField, c);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
add(scrollPane, c);
}
public void actionPerformed(ActionEvent evt) {
String text = textField.getText();
textArea.append(text + newline);
textField.selectAll();
//Make sure the new text is visible, even if there
//was a selection in the text area.
textArea.setCaretPosition(textArea.getDocument().getLength());
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event dispatch thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("TextDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Add contents to the window.
frame.add(new TextDemo());
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event dispatch thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I'm developing a chat application and when I press an enter button when focusing the JTextArea I want it to stop adding the unnecessary new line of text, so for example i will be able to determine when user has pressed the enter button and not typed anything inside the JTextArea. I am using a KeyListener for the means of detecting when an user has released the enter key and then sending the message. I firstly tried replacing the new line of text with an empty string message.replaceAll("[\n]", "") and also trimming the message, however it didn't work. Is there anything i'm doing wrong with my approach or would there be any other solution i could adapt?
Don't use a JTextArea for this, but instead use a JTextField.
You can then easily listen for the enter press by giving the JTextField an ActionListener.
Most Swing chat applications I've seen use two text components for this: a JTextArea to display incoming text and your sent text, and a JTextField to allow user input of the text to send.
Usually one is right on top of the other using a BorderLayout.
If you absolutely must use a JTextArea, then you will probably want to use Key Binding to capture the enter key and deal with it. Check out the How to use Key Bindings Tutorial.
For example:
Example Key Bindings Solution:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class CaptureTextAreaEnter extends JPanel {
private static final int COLS = 30;
private static final int VIEW_ROWS = 12;
private static final int ENTER_ROWS = 4;
private JTextArea chatViewArea = new JTextArea(VIEW_ROWS, COLS);
private JTextArea chatEnterArea = new JTextArea(ENTER_ROWS, COLS);
public CaptureTextAreaEnter() {
setLayout(new BorderLayout());
add(new JScrollPane(chatViewArea), BorderLayout.CENTER);
add(new JScrollPane(chatEnterArea), BorderLayout.SOUTH);
chatViewArea.setFocusable(false);
chatViewArea.setWrapStyleWord(true);
chatEnterArea.setWrapStyleWord(true);
chatViewArea.setLineWrap(true);
chatEnterArea.setLineWrap(true);
// start our set up of key bindings
// to get the correct InputMap
int condition = WHEN_FOCUSED;
// get our maps for binding from the chatEnterArea JTextArea
InputMap inputMap = chatEnterArea.getInputMap(condition);
ActionMap actionMap = chatEnterArea.getActionMap();
// the key stroke we want to capture
KeyStroke enterStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
// tell input map that we are handling the enter key
inputMap.put(enterStroke, enterStroke.toString());
// tell action map just how we want to handle the enter key
actionMap.put(enterStroke.toString(), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
String text = chatEnterArea.getText();
chatEnterArea.setText("");
chatViewArea.append(text + "\n");
// *** you will want to send text to your
// *** PrintStream to the chat server here
}
});
}
private static void createAndShowGui() {
CaptureTextAreaEnter mainPanel = new CaptureTextAreaEnter();
JFrame frame = new JFrame("CaptureTextAreaEnter");
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();
}
});
}
}
This is the solution that functioning perfectly for my system.
AddTxtA.getDocument().putProperty("filterNewlines", Boolean.TRUE);
**When user press on the "Enter" button in the JTextArea, a blank space will be input instead of new line. Below shown the sample output for two different situations.
1) Without AddTxtA.getDocument().putProperty("filterNewlines", Boolean.TRUE);.
OUTPUT: "My name
is Adam."
2) With AddTxtA.getDocument().putProperty("filterNewlines", Boolean.TRUE);.
OUTPUT: "My name is Adam."
To replace the standard behaviour of "enter" key you should use the Input/Action maps of your text area
See the method registerKeyboardAction(ActionListener anAction,String aCommand,KeyStroke aKeyStroke,int aCondition). As action listener you should take the Action from your "send" button, command is a string your choise, key-stroke is KeyStroke.getKeyStroke(KeyEvent.VK_ENTER) and condition is JComponent.WHEN_FOCUSED.