How to loop back to beginning of program - java

I am trying to create a while loop with this condition. (a > 1) So basically. every time a is set to greater than 1, it will close the JFrame I created and then start the program over. My problem is that, when I try edit the integer "a" from within an action listener, it doesn't recognize that it has already been declared. This is somewhat difficult to actually describe, so here is my code.
public class TestBox {
public static void main(String[] args) {
int a = 2;
while(a > 1){
a = 0;
JFrame frame = new JFrame("Test Box");
frame.setSize(1200, 800);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.setLayout(new GridLayout(6, 6, 1, 1));
JPanel panelAOne = new JPanel();
JPanel panelATwo = new JPanel();
JPanel emptySpace = new JPanel();
JLabel labelAOne = new JLabel();
labelAOne.setFont(new Font("Aerial", Font.ITALIC, 21));
labelAOne.setText("Welcome to the Test Box!");
JLabel labelATwo = new JLabel();
labelATwo.setFont(new Font("Aerial", Font.ITALIC, 21));
labelATwo.setText("Where would you like to go?");
JLabel emptyLabel = new JLabel("stuff goes here");
JButton buttonAOne = new JButton("Colors");
panelAOne.add(labelAOne);
panelAOne.add(labelATwo);
panelATwo.add(buttonAOne);
emptySpace.add(emptyLabel);
frame.add(panelAOne);
frame.add(buttonAOne);
frame.add(emptySpace);
frame.setVisible(true);
buttonAOne.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
a = 2;
//If this code is left as is, a GUI will open endlessly until it crashes the computer
}
}
});
}
}

You look to be trying to shoe-horn a linear console program into a Swing event-driven GUI program or visa versa, and you really don't want to do that, and I would recommend that you re-structure your program. Instead, show your GUI, get the input, and re-request the input in the GUI if it's wrong. Don't use a console-type program while loop for this.
So, for instance if the ActionListener finds that the input is not valid, clear the text in the JTextField by calling setText("") on the JTextField, show the user a JOptionPane informing them of the error in input and await another press of the button. But leave the JFrame displayed.
Edit: I don't even see a JTextField in your code, so I'm not sure where the user is supposed to enter input.

Your scheme cannot work, at least not reliably. At the end of the first iteration of your loop, local variable a still has the value set at the top of the loop (0), so the loop exits and the application's main thread dies. The GUI will continue to run, as it does so in a separate thread (the AWT's event-dispatch thread (EDT)), but even if you re-wrote your code so that an analog of variable a could be modified by your ActionListener, nobody who cares would still be paying attention by that point. (Probably. You have multiple threads sharing data without any synchronization, so really the behavior of your program is not well defined.)
GUI programming is fundamentally different from console programming, as Hovercraft Full Of Eels pointed out. It requires a significant mental adjustment to move from one to the other, but the basic paradigm of GUI programming is that everything your program does is a response to an event. Thus, if you want some sort of re-spawning behavior then you should obtain it by registering a listener for the appropriate event, and having it perform the work you want.

Related

How to close a frame that you have and open a new frame in JAVA

I want to open a new frame to show more details and for me to start on a new window like a new fresh page
here is my code:
public class OBA {
public static void main(String[] args) {
JFrame f = new JFrame("OBA");
f.setSize(1366, 768);
f.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
f.setLayout(null);
f.setVisible(true);
JLabel l1 = null;
JLabel l2 = null;
JLabel l3 = null;
l1 = new JLabel("Welcome");
l1.setBounds(625, 100, 100, 100);
l1.setFont(new Font("Courier New", Font.BOLD, 22));
l1.setForeground(Color.BLACK);
l2 = new JLabel("To", JLabel.CENTER);
l2.setBounds(625, 100, 100, 150);
l2.setFont(new Font("Courier New", Font.BOLD, 22));
l2.setForeground(Color.BLACK);
l3 = new JLabel("OBA", JLabel.CENTER);
l3.setBounds(623, 100, 100, 200);
l3.setFont(new Font("Courier New", Font.BOLD, 22));
l3.setForeground(Color.BLACK);
f.add(l1);
f.add(l2);
f.add(l3);
}
}
You created a JFrame, f, so you know how to create a JFrame. To close that frame for good, you can call f.dispose(); if you just want to hide it, call f.setVisible(false). To open a new frame, do something like what you did to create f.
I don’t know exactly why you want to do this, but if (as I’ve interpreted your question) you want to close the current window and immediately create a new one, in a lot of situations there’s an easier way. In lieu of disposing one JFrame and creating another, you can keep the existing JFrame and remove whatever’s already in it, such as with successive calls of f.remove(), one for each component you want gone. If you put your components in a LayoutManager instead of straight into the frame, you could put another LayoutManager in with f.setLayout() and swap everything out in one go, which might be easier.
For more information on JFrame, see the Oracle documentation.
Edit (in response to a comment): If you want to wait before changing what’s shown, you need to use a method that causes a delay. java.lang.thread has a static method sleep() that does exactly this; to wait for five seconds, you can use Thread.sleep(5000); (since it accepts milliseconds). This method can throw an exception, though, if you interrupt the thread that’s running sleep(). So you should wrap it in a try block:
try {
Thread.sleep(5000);
} catch(InterruptedException ex) {
// Your thread was interrupted (maybe the user quit the program?)
// Handle that here.
}
f.remove(/* The element to remove goes here. */);
// Etc...
Of course, since this pauses the thread for five seconds, you should not call sleep() in a thread that handles things like user input, which could occur during those five seconds.
Oracle has documentation for this, too.
This is getting to be a different topic from that of the original question, but threads in Java (and in general) have lots of little intricacies that trip people up. If you need more help with sleep() or threading, you might want to ask a new question, or look for an existing one that relates to your problem.

Java code doesn't work without a println

Here is my code:
while(monster.curHp > 0)
{
System.out.println("");
if(battle.pressedButton)
{
text = Player.name + ": " + Player.curHitPoints + " " + monster.name + ": " + monster.curHp;
battle = new GUIForBattle(text,Player,monster);
}
}
The weird thing is that if I have that println line in the while loop the code will work normally and when the button is pressed we will update text to have the current status and we will redraw the GUI using the GUIForBattle class, however if I don't have that println it wont redraw. Any advice? Thank you!
Here is the GUIForBattle for more context
public class GUIForBattle extends JFrame {
boolean pressedButton = false;
public GUIForBattle(String words, player PlayerOne, Monster monster)
{
JFrame frame = new JFrame(); //frame that holds everything
JPanel Panel = new JPanel(new GridLayout(5,5)); //panel where things get added
JLabel text = new JLabel(words); // text label
JButton attack = new JButton("Attack"); //makes a button used to attack
//adding what pressing the attack button would do
attack.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
int attackAmount = PlayerOne.weaponEquipped.att;
monster.curHp = monster.curHp - attackAmount;
pressedButton = true;
}
}
);
JButton Item = new JButton("Item"); // makes a button used to use items
Item.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//we need to make a item interface
}
});
Panel.add(text); //adds the text to the panel
Panel.add(attack);
Panel.add(Item);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 800); //setting size of frame
frame.add(Panel); //adding the panel to frame
frame.setVisible(true); //making the frame visible
}
}
Your code is inherently multi-threaded; one thread is running through that little while loop; the other is the swing application thread that will be handling your swing event handlers.
If you use shared variables like this (both threads access pressedButton) you need to make sure that variable is synchronized between threads. There are several ways of handling this, but an easy way for this particular problem would be to make the variable volatile.
If the variable is not synchronized in any way, there is no guarantee by the JVM as to when one thread will 'see' the changes made to it by the other. And typically, if you keep one thread occupied like you're doing here (this while loop is called a busy wait) it will never take the time to synchronize, and you'll never see the updates.
The println is an IO operation, meaning at some point your thread will be waiting for IO to complete. Most likely this causes the JVM to synchronize the variables, which is why you notice this difference.
In any case, relying on this without thinking about synchronization can be considered a bug.
Java threads and memory handling are a complex subject, and not something I would advise for beginners to jump in to; it could be overwhelming. Just try to avoid sharing memory between threads for now. For the moment, just run your logic in your swing application code (it's not ideal, but for some beginner code it's probably a good starting point).
When you feel ready for it, read up on the memory model and what it implies for multi-threading.

Swing is very slow with long strings

I built a simple Java program that logs in a JTextArea component.
JTextArea _log = new JTextArea();
_log.setEditable(false);
JScrollPane scrollLog = new JScrollPane(_log);
scrollLog.setPreferredSize(getMaximumSize());
add(scrollLog);
The problem is that logging like this takes 15ms on average:
public void log(String info) {
_log.append(info + "\n");
}
This is far(!) slower than logging using System.out.println. Logging takes more time than the whole running time of the algorithm!
Why is the JTextArea is so slow? Is there a way to improve it?
EDIT 1:
I am using separate thread for the algorithm, and using SwingUtilities.invokeLater to update the log in the UI.
The algorithm tread finish his work after 130ms on average, but the JTextArea finish his appends after 6000ms on avarage.
EDIT 2:
I tried to test this by use setText of string that contains 2500 charaters. In that case the operation took 1000ms on average.
I tried to use another controller then JTextArea and I get same results.
Is it hard for Swing components to deal with large strings? What can I do about it?
EDIT 3:
I just test with this code:
public class Test extends JFrame {
public Test() {
final JTextArea log = new JTextArea();
log.setEditable(false);
log.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
JScrollPane scrollLog = new JScrollPane(log);
scrollLog.setPreferredSize(getMaximumSize());
JButton start = new JButton("Start");
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long start = System.nanoTime();
for (int i = 0; i < 2500; i++) {
log.append("a\n");
}
long end = System.nanoTime();
System.out.println((end - start) / 1000000.0);
}
});
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(2, 1));
panel.add(scrollLog);
panel.add(start);
add(panel);
}
public static void main(String[] args) {
Test frame = new Test();
frame.setSize(600,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
The time of that for loop is 1870ms on avarage.
This is the only code that I ran (include the declaration of _log at the top of the question)
A JTextArea is not slow.
Far(!) away from System.out.println.
System.out.println() executes on a separate Thread.
The log takes more time then the hole running time of the algorithm!
So your algorithm is probably executing on the Event Dispatch Thread (EDT) which is the same Thread as the logic that appends text to the text area. So the text area can't repaint itself until the algorithm is finished.
The solution is to use a separate Thread for the long running algorithm.
Or maybe a better choice is to use a SwingWorker so you can run the algorithm and "publish" results to the text area.
Read the section from the Swing tutorial on Concurrency for more information and a working example of a SwingWorker.
Edit:
//log.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
The above line is causing the problem. I get 125 for the first test and 45 when I keep clicking the button.
That property is not needed. The text is still displayed on the left side of the text pane. If you want right aligned text then you need to use a JTextPane and set the attributes of the text pane to be right aligned.
That is why you should always post an MCVE. There is no way we could have guessed from your original question that you were using that method.
Edit2:
Use the alignment feature of a JTextPane:
SimpleAttributeSet center = new SimpleAttributeSet();
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
textPane.getStyledDocument().setParagraphAttributes(0, doc.getLength(), center, false);
Now any new text you add to the document should be center aligned. You can change this to right.

visualizing JPanel change within a loop

I am new to Java swing programming. I want to make a frame which will appear red and blue in turn one after another. So, I took 2 child JPanel, 1 for red and other for blue, and a for-loop. On each iteration I remove one panel from parent panel and add another. But, when I run the program it only shows the last state of the frame.
Can anyone explain why? And what's the intended approach to make a program work like that?
My code:
public class Test2 extends JFrame {
public Test2() {
JPanel Red = new JPanel(new BorderLayout());
JPanel Blue = new JPanel(new BorderLayout());
//...initialize Red and Blue
Red.setBackground(Color.red);
Blue.setBackground(Color.blue);
Red.setPreferredSize(new Dimension(200,200));
Blue.setPreferredSize(new Dimension(200,200));
JPanel panel = new JPanel(new BorderLayout());
panel.setPreferredSize(new Dimension(200,200));
add(panel);
pack();
setTitle("Border Example");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
int M = 1000000; //note that, I made a long iteration to not finish the program fast and visualize the effect
for(int i=0;i<M;i++)
{
if(i%(M/10)==0) System.out.println(i); //to detect whether the program is running
if(i%2==0)
{
panel.removeAll();
panel.repaint();
panel.revalidate();
panel.add(Red,BorderLayout.CENTER);
}
else
{
panel.removeAll();
panel.repaint();
panel.revalidate();
panel.add(Blue,BorderLayout.CENTER);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Test2 ex = new Test2();
ex.setVisible(true);
}
});
}}
Don't use a loop. Swing will only repaint the frame once the entire loop has finished executing.
Instead you need to use a Swing Timer. When the Timer fires you invoke your logic. Read the section from the Swing tutorial on How to Use Swing Timers.
Here is a simple example of a Timer that simply displays the time every second: Update a Label with a Swing Timer
Also, don't remove/add panels. Instead you can use a Card Layout and sway the visible panel. Again read the tutorial on How to Use CardLayout.
Basically you don't need to use a while (or any other) loop, Swing only paints once it has finished that loop then repaint the GUI.
As stated before by #camickr on his answer, you could try a Swing Timer; here's an example that does exactly what you want.
From your comment on another answer:
Could you please explain why "repaint" does not work in a loop? And why is the Timer working without a "repaint"?
Swing is smart enough to know it doesn't needs to repaint in a loop, instead it will repaint once it the loop finishes, if you read the tutorial on Swing Custom Paint on the step 3 it says:
"Swing is smart enough to take that information and repaint those sections of the screen all in one single paint operation. In other words, Swing will not repaint the component twice in a row, even if that is what the code appears to be doing."
And Timer will repaint it, because it's not running on the EDT but in it's own Thread
I would suggest to take in one step at a time.
First make it run without changing panels / colors.
Now it doesn't because this
public final void Test2() {
is a method (which is never used) and not a constructor.
Change to a constructor declaration like :
public Test2() {
to make the program do something. Then you can go to the next step.
Also use Java naming conventions (like blue instead of Blue).

JOptionPane using one window for input and output

So I'm working on a little project and I'm looking for the base code for how to do this in JOptionPane. I'm still really new to this side of Java. I'm not looking for a whole lot, I just didn't know where to start.
The program should populate the screen with a JOptionPane window. I need it to be modeled like the picture below. The bottom row is a text input from the user and when they hit the enter key, the text should "refresh/clear" and then the middle string area should populate with both the user input and then just below it the result of an if statement according the the code.
for example: The user enters in: "Hello".
Then the text input should refresh and the grey box should do this: "User: Hello."
"Computer: Hello user".
I would really appreciate any and all help on this.
You don't System.out.println() into a gui component. Doesn't work like this. You can write a console program and use JOptionPanes to get user input, but the output would be used in the console program. You would need to create a gui program mimic a console.
Here's a basic layout to start you off
public class Game extends JFrame {
JTextArea jta = new JTextArea(10, 30);
JTextField jtf = new JTextField(30);
public Game(){
add(jtf, BorderLayout.SOUTH);
add(jta, BorderLayour.CENTER);
jta.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
}
});
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
JFrame frame = new Game();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
});
}
}
What I would suggest is since this is a very basic program, I would use an array of String commands and iterate through them.
For example:
String[] question = {"Do you want to go to school?",
"Do you want to drive or walk?"};
jta.setText(questions[0]);
Then in your actionPerformed get the answer from the text field. Use an if statement like
if (jtf.getText().equals("yes") {
jta.append(questions[1]);
}
And so on. If you have no idea what I'm talking about, I would really consider using the Swing tutorials I mentioned. There's a lot of info in those tutorials.

Categories

Resources