This question already has answers here:
Why does Java not see the updated value from another thread?
(5 answers)
Closed 2 years ago.
I have updated this ask, i created a simple program with the following problem.
This is the version code that work:
package com.company;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main {
static boolean readytoconnect = false;
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
JPanel panel = new JPanel();
frame.add(panel);
panel.setBounds(0, 0, frame.getWidth(), frame.getHeight());
panel.setBackground(Color.lightGray);
panel.setLayout(null);
JButton connect = new JButton("Connect");
panel.add(connect);
connect.setBounds(200, 200, 80, 40);
connect.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
readytoconnect = true;
}
});
Thread threadtoconnect = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
System.out.println("WAITING TO CONNECT");
if (readytoconnect) {
System.out.println("CONNECTED");
}
}
}
});
threadtoconnect.start();
}
}
This is the version code that don't work:
package com.company;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main {
static boolean readytoconnect = false;
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
JPanel panel = new JPanel();
frame.add(panel);
panel.setBounds(0, 0, frame.getWidth(), frame.getHeight());
panel.setBackground(Color.lightGray);
panel.setLayout(null);
JButton connect = new JButton("Connect");
panel.add(connect);
connect.setBounds(200, 200, 80, 40);
connect.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
readytoconnect = true;
}
});
Thread threadtoconnect = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
//System.out.println("WAITING TO CONNECT");
if (readytoconnect) {
System.out.println("CONNECTED");
}
}
}
});
threadtoconnect.start();
}
}
the difference between these is that in the first, which works, there is the output before the 'IF' and in the second, which does not work, there is no output.
Android does not support the Swing library, you must use functions from the Android SDK to get input etc.
I would advise looking up "OnClickListener" or the like in the Android Docs
First, I will point out a couple of problems with both versions of the code:
You appear to have two threads reading and updating a shared readytoscan field without any obvious synchronization. Unless that field is declared as volatile, that is liable to lead to memory anomalies.
The code in the run() method that assigns to readytoscan should probably be assigning false to it ... not true.
The code will leak open sockets!
You are creating an anonymous subclass of Thread which overrides the run() method. It is more conventional to create an instance of Thread and pass it a Runnable ... or a lambda.
I don't think that any of the above explain the strange behavior that you reported though. The only cause (that I can see) for the inner loop can run just once is if the run() method is crashing. Specifically, something in the inner loop must be throwing an unchecked exception.
To track this down, you could add an extra catch (Throwable ...) to the try and log the exception that it catches. If that doesn't yield an explanation for the behavior, try creating a minimal reproducible example. For example, you could simplify the plain Java version so that it didn't use Swing ... which I don't think is implicated in the problem.
My guess is that the problem is coming from the new Socket(...) call. For example, it could be a SecurityException.
The problems I described above are for both Android and Java with the use of the Swing library, so let's say Java is the problem in general.
It is a bad strategy to assume that the bug is in Java or Android or Swing ... just because you can't find a cause in your code. Think of it this way: the Java / Android platforms work for millions of other developers and run on billions of devices (I am guessing these numbers). They have worked for (in the Java case) 20+ years. The chances that there is something fundamentally wrong with the Java AND Android platforms that other people haven't already found, reported and fixed is vanishingly small.
Related
I am trying to build a window with one picture that covers up the screen. The picture is a JLabel and the window is a JFrame. After trying countless ways and looking up multiple tutorials for hours, I have not figured out how to do this. I agree, this is a very simple question, but I simply do not understand how I can approach this problem. Here is my code I have tried(I commented out some things that I tried earlier):
package Buttons;
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import java.awt.GridLayout;
public class Mewindow extends JFrame {
private JFrame mewindow;
private JLabel mepic = new JLabel(new ImageIcon("me.png"));
public Mewindow() {
super("Here is a picture of ME!");
mewindow.setLayout(new GridLayout(1, 0, 0, 0));
// Icon me = new ImageIcon(getClass().getResource("me.png"));
add(mepic);
mewindow.setVisible(true);
mewindow.setSize(250, 250);
mewindow.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
}
}
Thank you so much for the time you are taking for reading this, I really appreciate the effort you are putting into helping a fellow programmer!
Problem #1...
You have no main method, so unless you're creating the class from another class, it won't run...
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Mewindow frame = new Mewindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
Problem #2...
Now, you will run into a NullPointerException, because mewindow is not initialised, but, you don't actually need it, because you're using the values within the constructor of the class, so you would end up with a StackOverflowException if you tried to intiialise it ... but it doesn't make sense to use it anyway...
public class Mewindow extends JFrame {
private JLabel mepic
public Mewindow() {
super("Here is a picture of ME!");
setLayout(new GridLayout(1, 0, 0, 0));
mepic = new JLabel(new ImageIcon(getClass().getResource("me.png")));
add(mepic);
setVisible(true);
setSize(250, 250);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
}
}
Now your code assumes that me.png is stored within the same package as the Mewindow, just beware of that.
And, the resulting code actually running (replace with my own picture)
Suggestions...
Don't extend directly from JFrame, use a JPanel instead and then add that to an instance of JFrame, your code will be more re-usable
I'm reading Thinking in Java and the author stresses that main method shouldn't call swing methods. As an example of that practice he presents the following piece of code (available on his webpage):
//: gui/SubmitSwingProgram.java
import javax.swing.*;
import java.util.concurrent.*;
public class SubmitSwingProgram extends JFrame {
JLabel label;
public SubmitSwingProgram() {
super("Hello Swing");
label = new JLabel("A Label");
add(label);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 100);
setVisible(true);
}
static SubmitSwingProgram ssp;
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
public void run() { ssp = new SubmitSwingProgram(); }
});
TimeUnit.SECONDS.sleep(1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ssp.label.setText("Hey! This is Different!");
}
});
}
} ///:~
The gui object is then created and initialized through invokeLater method making it thread safe. But few pages later the author presents the following code:
//: gui/Button2.java
// Responding to button presses.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static net.mindview.util.SwingConsole.*;
public class Button2 extends JFrame {
private JButton
b1 = new JButton("Button 1"),
b2 = new JButton("Button 2");
private JTextField txt = new JTextField(10);
class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String name = ((JButton)e.getSource()).getText();
txt.setText(name);
}
}
private ButtonListener bl = new ButtonListener();
public Button2() {
b1.addActionListener(bl);
b2.addActionListener(bl);
setLayout(new FlowLayout());
add(b1);
add(b2);
add(txt);
}
public static void main(String[] args) {
run(new Button2(), 200, 150);
}
} ///:~
where SwingConsole is:
//: net/mindview/util/SwingConsole.java
// Tool for running Swing demos from the
// console, both applets and JFrames.
package net.mindview.util;
import javax.swing.*;
public class SwingConsole {
public static void
run(final JFrame f, final int width, final int height) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
f.setTitle(f.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(width, height);
f.setVisible(true);
}
});
}
} ///:~
So contrary to the previous example an object implementing JFrame is created and initialized within the main method / main thread.
My question is then:
(1) Is the second example wrong or is the first one exaggerated?
(2) Is it enough that I call swing methods through invokeLater only after the setVisible call and before that statement it is safe to call swing methods within main thread?
The second example is wrong. Swing components must be created and used from the event dispatch thread. See https://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html.
Quote from the javadoc:
Calls to an application's main method, or methods in Applet, are not invoked on the event dispatching thread. As such, care must be taken to transfer control to the event dispatching thread when constructing and showing an application or applet.
(emphasis mine)
The example with the main() method might be a bit misleading. The rule AFAIK is that only the main thread is allowed to execute Swing updates/redraws etc. and the main() method per definitionem is the main thread. All other threads are required to schedule stuff via SwingUtilities.invokeLater(...). It might be that even for the main thread it improves latencies since you might do time consuming stuff in the main thread and schedule GUI refreshes foe whenever the main thread is less busy.
EDIT: Proven Wrong
I'm writing a program that completes the following tasks in sequence:
Collects user input from a JPanel
Uses the input to copy dependencies from the program directory into a new project directory
Uses the input to construct an interactive graph in the project directory
I have a separate class for each task and a main class that calls each object in sequence.
My problem is that the main class evaluates step 2 before step 1 is complete. Because the user has not yet closed the JPanel when the main class calls object 2, the user input is not collected before step 2 starts and the program crashes.
What I need is a way to signal to class 2 that the JPanel in class 1 has been closed. This way step 2 begins after the input fields have been collected in step 1.
Is there a way to have the window closing in class 1 trigger an action in class 2? If not, what would be the best way to solve this problem?
"Is there a way to have the window closing in class 1 trigger an action in class 2? If not, what would be the best way to solve this problem?"
As Boris the Spider pointed out, you should be using a model dialog. You're probably using a frame. You should read up on Modality to learn its behavior and features. Also take some time to look at How to make Dialogs. In short, turning the dialog's modality on (which is defaulted on JOptionPane static showXxx methods and can be set on JDialog either through setModalityType or passed through the constructor), the flow will "block" until the dialog is closed.
Below is an example. It may be overcomplicated for such a simple task (as it could easily be accomplished with a JOptionPane), but it shows how to use a JDialog. Look as the ShowDialogActionListener class. The dialog is set visible and flow is not continued in the actionPerformed until the dialog is closed, which is when the Input is obtained from the dialog.
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class DialogDemo {
private JFrame frame = new JFrame();
public DialogDemo() {
JButton button = new JButton("Open Dialog");
button.addActionListener(new ShowDialogActionListener());
frame.setLayout(new GridBagLayout());
frame.add(button);
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
class ShowDialogActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
InputDialog dialog = new InputDialog(frame, true);
System.out.println("Opened dialog.....");
long start = System.currentTimeMillis();
dialog.setVisible(true);
System.out.println("Dialog closed after "
+ (System.currentTimeMillis() - start) + " ms");
Input input = dialog.getInput();
ServiceOne service = new ServiceOne();
service.serviceMethod(input);
}
}
class ServiceOne {
public void serviceMethod(Input input) {
System.out.println(input.getInput());
}
}
class InputDialog extends JDialog {
private Input input;
public InputDialog(JFrame parent, boolean modal) {
super(parent, modal);
JPanel panel = new JPanel(new GridLayout(0, 1));
final JTextField field = new JTextField(20);
JButton okButton = new JButton("OK");
panel.add(field);
panel.add(okButton);
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String text = field.getText();
input = new Input();
input.setInput(text);
InputDialog.this.dispose();
}
});
setLayout(new GridBagLayout());
add(panel);
setSize(250, 250);
setLocationRelativeTo(parent);
}
public Input getInput() {
return input;
}
}
class Input {
private String input = "default";
public void setInput(String input) {
this.input = input;
}
public String getInput() {
return input;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DialogDemo();
}
});
}
}
As I said earlier, the same could easily have been accomplished with
String input = JOptionPane.showInputDialog("Enter a Message");
The above would also block flow execution.
The trick to communicating events between classes is the wait() and notify() methods.
Suppose you're executing a main method. At some point main invokes another class, say a gui. Here you want main to pause and wait for certain events in the gui to complete before main proceeds with the rest of its actions.
This is accomplished by synchronizing code blocks between the two classes and telling main to wait() until the gui notifies it to proceed, notify(). For example:
main
public static void main(String[] args) throws Exception {
GUI gui = new GUI();
// Do some things
doSomething();
doSomthingElse();
// Make sure we wait until gui input has been collected before proceeding
synchronized(gui) {
try {
gui.wait();
}
catch(InterruptedException e){
e.printStackTrace();
}
}
// Do some things using the gui input we've been waiting for
doSomeMoreThings();
}
gui
// The gui method we want to synchronize
public void collectInput() {
synchronized(this) {
// Collect fields
name = nameField.getText();
age = ageField.getText();
date = dateField.getText();
// Notify waiter that our business is complete
notify();
}
}
I'm using a Swing Timer to execute animations in my program. In many instances, there are several different calls made to the animate() method at once, with a separate Timer created for each. I know that, because of the way Swing timers are designed, these all get executed together - all my animations occur at the same time. However, there are some instances where I need to wait for one animation to complete to execute another.
Is there a way to make Swing timers execute sequentially - one after the other, rather than all at once? Or is there an alternative mechanism to the Swing timer that might better match my use case?
EDIT: I'm afraid I oversimplified my use case a bit. #peeskillet's suggestion would work perfectly if I knew at each "scene transition" what animations would need to be executed, or if the same sequence of animations occurred each time. Unfortunately that's not the case -- each transition requires a different set of animations, with different sets of components moving onto, off of and around on the panel.
What I want is to execute the animations of items off the screen first, and then (after that completes) animate the components on the screen. It's not a problem to distinguish between these "types" of animations at runtime - they're initiated from different methods, and thus its easy to imagine adding them to two different "queues" - a queue of outgoing items and a queue of incoming items. Having done so, I could then implement the basic strategy of calling a
That said - that all only makes sense to me intuitively, heuristically - I haven't figured out how to implement it in practice. What would those "queues" actually be, and what class would hold and later execute them?? Presumably one that implements Runnable, creating a second thread that can execute the animations with tighter control on how they proceed? Or does the event-dispatch thread give me the ample control here, if only I fully grasped how to use it? In which case - please help me do that.
(PS I realize that I've changed the question significantly here, essentially turning it into a new question, and that #peetskillet answered it as previously worded perfectly well, so I accepted that answer and posted a new question here.
"Is there a way to make Swing timers execute sequentially - one after the other, rather than all at once? "
Just use a `boolean of some sort, telling when the first timer when it should stop and when the second timer should start. Something like
Timer timer1 = new Timer(delay, null); <---- initialize
Timer timer2 = new Timer(delay, null);
boolean something = false;
public Constructor() {
timer1 = new Timer(delay, new Action Listener(){
public void actionPerformed(ActionEvent e) {
if (something) { ------
timer2.start(); |
timer1.stop(); |---- some code should lead to
} esle { | `something` being true. Maybe
animateFirstSomething(); | another if statement inside the
} | else. Like if x equals y
} ------ something = true, else,
}); animateFirstSomething()
timer1.start();
timer2 = new Timer(delay, new ActionListener(){
public void actionPerformed(ActionEvent e) {
animationSecondSomething();
}
});
}
Here's simple example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestTwoTimers extends JPanel {
int rectOneX = 0;
int rectTwoX = 0;
Timer timer1 = new Timer(100, null);
Timer timer2 = new Timer(100, null);
boolean rectOneGo = true;
public TestTwoTimers() {
timer1 = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (rectOneGo) {
if (rectOneX >= 225) {
timer2.start();
timer1.stop();
} else {
rectOneX += 10;
repaint();
}
}
}
});
timer2 = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (rectTwoX < 225) {
rectTwoX += 10;
repaint();
} else {
timer2.stop();
}
}
});
final JButton button = new JButton("Start First Timer");
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
timer1.start();
}
});
setLayout(new BorderLayout());
add(button, BorderLayout.PAGE_END);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(rectOneX, 50, 75, 75);
g.setColor(Color.BLUE);
g.fillRect(rectTwoX, 150, 75, 75);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
JFrame frame = new JFrame("Double Timers");
frame.add(new TestTwoTimers());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
I'm having issues figuring out how to open one window when another closes if the other window is initiated within a sub class. Here is the clumsy code I am trying to use, but it halts the setting visible of the sub classe's window. Perhaps due to it being within an action event or perhaps it is halting the main thread.
tutorial = new tutorialWindow();
this.setVisible(false);
tutorial.setLocationRelativeTo(null);
tutorial.setVisible(true);
tutorial.setCurrentUser(users.getCurrentUser());
while(tutorial.isOpen() == true ) {
}
this.setVisible(true);
users.updateUser(tutorial.getCurrentUser());
My thoughts were that it would just get stuck in the section of code until the other window closes and would then appear again when the tutorialWindow has a Open boolean set to false due to it breaking the while loop.
Im sure this is a matter of using correct threads, or perhaps the various notify methods but as of now I am not sure how to do that.
You could do it using WindowListener. In the following sample WindowAdapter implements WindowListener and I just override the public void windowClosed(final WindowEvent e) method, opening the second window.
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class TestJFrame {
public static void main(final String args[]) {
JFrame jFrame1 = new JFrame();
jFrame1.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jFrame1.add(new JLabel("First JFrame"));
jFrame1.pack();
final JFrame jFrame2 = new JFrame();
jFrame2.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jFrame2.add(new JLabel("Second JFrame"));
jFrame2.pack();
jFrame1.addWindowListener(new WindowAdapter() {
#Override
public void windowClosed(final WindowEvent e) {
jFrame2.setVisible(true);
}
});
jFrame1.setVisible(true);
}
}