Closing only a single instance of JFrame? - java

So I have two classes, Main and MakeUserWindow, inside of my Main class I call MakeUserWindow several times with different parameters by using a loop. The only problem is, this creates several windows that overlap each-other (Which isn't that much of a deal, it's just that I can get 20 windows on top of each-other). What I thought of doing was simply using window.dispose(); right before recalling the instance, however, when I do that it closes all instances of the window. Not allowing me to recreate the instance using the same variable. Is there a way of closing only the single instance like window.close(); that I am unaware about, or am is there just a better way of doing this? I have searched for awhile before coming here, no results have helped.
For some reference, here is a simplified version of what I am doing
(MakeUserWindow is a class that extends JFrame)
MakeUserWindow newWindow;
for(stuff){
newWindow.dispose();
newWindow = new MakeUserWindow("parameters here");
}
EDIT---
The reason I initialize MakeUserWindow outside of the loop is because I need to use newWindow's properties.
Thanks for reading, -Zach.

I had tested it and this is what i got:
JFrame frame = new JFrame();
for (int i = 0; i < 5 ; i++) {
frame.dispose();
frame = new JFrame();
}
More or less like your code. Only the last frame survived cause you are closing the others when you do the ".dispose()". What you can do is a Map that keeps all the instances.
Map<String, JFrame> frames = new HashMap<String, JFrame>();
JFrame frame = new JFrame();
for (int i = 0; i < 5 ; i++) {
frame = new JFrame();
frames.put("Window" + i,frame);
}
And if you want to close a frame you do:
frames.get("WindowX").dispose();

Related

Java NullPointer Exception with Jpanel [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 5 years ago.
Hello guys I started a personal project to where I would basically make a meal planner based of how many calories a person wants to consume per day for myself. I know it maybe a tough challenge for me since I'm a beginner, but I can do it! I will leave the backend later, but for now I wanted to work on the UI and I'm having trouble with this piece of code. I want to make a list of panels to be held in a panel, based on how many number of meals they want is how many panels will appear. any insight on this will be much appreciated.
package mealplanner;
import javax.swing.*;
/**
* Created by Usman on 6/8/2017.
*/
public class MealPlannerPanel extends JPanel {
JPanel[] panel;
int mealsPerDay, caloriesPerDay;
public MealPlannerPanel(){
mealsPerDay = Integer.parseInt(JOptionPane.showInputDialog("How many meals would you like per day?",null));
caloriesPerDay = Integer.parseInt(JOptionPane.showInputDialog("What is your daily calorie aim?",null));
panel = new JPanel[mealsPerDay];
for(int i = 0; i < panel.length; i++){
add(panel[i]);
}
}
public static void main(String[] args){
MealPlannerPanel planner = new MealPlannerPanel();
JFrame frame = new JFrame("Meal Planner");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(planner);
frame.pack();
frame.setVisible(true);
}
}
panel = new JPanel[mealsPerDay];
That statement just creates an array that is able to hold the given number of panels.
It does not actually create the panel (so when you index into the array you will get a null object).
So you need something like:
for(int i = 0; i < panel.length; i++){
JPanel onePanel = new JPanel();
panel[i] = onePanel;
add(panel[i]);
}
Also, be more descriptive with you variable names. "panel" implies a single component. Given it is meant to represent an array the least you can do is call it "panels" so we know there is more than one.

JFrame issue. This keeps printing double the amount of windows I need

I have two more classes that this main class is using, but I think that it can be answered without those classes because I think it is a logic problem.
I'm trying to create a JFrame that prints out a drawing. The way the API is set up, I want public Viewer to create the frame and set the title and then main to instantiate a viewer. But the way I have this set up keeps printing double the amount of frames that I need. Also, when I try to concatenate
v.setTitle(v.getTitle() + String.format(" pi = %.4f", pi));
it doesn't work. It just prints a new JFrame with just
String.format(" pi = %.4f", pi));
and the original JFrame prints its name (two separate frames). So I know it has to be a logic problem somewhere, but I can't figure it out.
import java.util.Scanner;
import javax.swing.JFrame;
public class Viewer extends JFrame{
private ControlPanel cp;
public Viewer(String name, int in){
JFrame frame = new JFrame();
frame.setSize(550, 550);
frame.setTitle("Name: " + name);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cp = new ControlPanel(in);
frame.add(cp);
frame.setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
int n = (int) 1e4;
System.out.println("Enter the number of runs to make <1 to 4>:");
#SuppressWarnings("resource")
Scanner in = new Scanner(System.in);
int runs = in.nextInt();
if (runs > 4){
runs = 4;
}
if (runs < 1){
runs = 4;
}
for (int i = 1; i <= runs; i ++){
n = n * runs;
Viewer v = new Viewer("Trey Wilson", n);
int hits = v.cp.getHits();
double pi = 4.0 * hits / n;
v.setTitle(v.getTitle() + String.format(" pi = %.4f", pi));
v.pack();
v.setVisible(true);
v.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
}
Your code has multiple errors:
It extends JFrame and creates a JFrame object (just remove the extends JFrame from the class, it's recommended to do so)
Here lies your problem:
Viewer v = new Viewer("Trey Wilson", n);
this calls the Viewer's class constructor but the constructor itself sets it's own title, size, etc, and then you're modifying those attributes and making another call to setVisible(true) which makes this last frame visible again, so remove one or the other.
You're not placing your program on the EDT, see SwingUtilities.invokeLater() why is it needed?
See The use of multiple JFrames, Good / Bad Practice? (Bad) you should instead want to try using a single JFrame with multiple JPanels switching them with a Card Layout or using JDialogs to display / retrieve information to / from user
From your comment above:
I'm completely new to Jframe (along with java) and was given an API to follow along with. What does extends mean? I'll give it a google, but didn't know if you had an answer on how to fix it.
I recommend you to learn the OOP basics, Java concepts first before going into Java Swing which will make your learning harder because it will add more complexity to it and make it harder to you to maintain or make a quality software.
THIS SOLVES THE PROBLEM OF DOUBLE FRAME APPEARING:
So you can either take away frame.setVisible(true); in the constructor as in:
public Viewer(String name, int in){
JFrame frame = new JFrame();
frame.setSize(550, 550);
frame.setTitle("Name: " + name);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cp = new ControlPanel(in);
frame.add(cp);
}
OR Rather take away v.setVisible(true); from you main method.
I will advice you to take away frame.setVisible(true);. But depending on you requirement, choose the best for yourself.

A JFrame that won't disappear

I have a following situation: my program repeatedly calls something that makes a JFrame with text appear. I read the text from that frame. The frame has a special type, let's call it MyFrameType, so I recognize it by the type, and dispose of the frame at the end of each call, in all possible way of closing a frame I know of... (the following code is within a loop)
callMyFrame();
Frame[] frames = Frame.getFrames();
for (Frame openFrame : frames) {
if (openFrame instanceof MyFrameType) {
MyFrameType myFrame = (MyFrameType) openFrame;
(do something, read the frame etc.)
myFrame.setVisible(false);
myFrame.dispose();
myFrame.dispatchEvent(new WindowEvent(
myFrame,
WindowEvent.WINDOW_CLOSED));
break;
}
}
Now, the problem is: while the frame gets actually closed (which is not always case with the last frame for some reason - sometimes it gets closed, sometimes not), the closed frames are still listed by the frames array and the program ends up reading from the wrong frame - the first one of MyFrameType it finds, although only one of them is actually open. I know that getFrames() gets all frames created by the application. But how do I exclude those disposed frames, so that it doesn't just pick a random previously created, long forgotten frame? The frames in question have apparently no owner, so that removing ownerless frames does not do the job.
You can just check if the JFrame is displayable, here is a simple example that you can use to verify:
JFrame jFrame = new JFrame();
jFrame.setVisible(true);
jFrame.dispose();
System.out.println(jFrame.isDisplayable());
Output:
false
So in your loop instead of just checking if the JFrame is an instanceof MyFrameType you can do:
for (Frame openFrame : frames) {
if (openFrame instanceof MyFrameType && openFrame.isDisplayable()) {
}
}
You should set the frame in the array to null, or otherwise indicate that it is "deleted". Also, to do this you need to use a standard for-loop instead of the enhanced for-loop, something like this:
Frame[] frames = Frame.getFrames();
for(int i=0; i<frames.length; i++) {
if (frames[i] instanceof MyFrameType) {
(do something, read the frame etc.)
frames[i].setVisible(false);
frames[i].dispose();
frames[i].dispatchEvent(new WindowEvent(
frames[i],
WindowEvent.WINDOW_CLOSED));
frames[i] = null;
break;
}
}

Java threads with Swing

I would like to illustrate a project about railroads.
I decided to use Swing.
I have a background map in a JPanel and I draw little circles that moves on the railroads.
It works perfectly if I have only one train, but I would like to add more trains.
Here is what I started to do (and works) :
public static void main(String[] args) {
// JFrame and background panel construction
JFrame frame = new JFrame();
JLayeredPane lpane = new JLayeredPane();
ImagePanel panelBg = new ImagePanel(new ImageIcon("map.jpg").getImage());;
frame.setPreferredSize(new Dimension(1791, 695));
frame.setLayout(new BorderLayout());
frame.add(lpane,BorderLayout.CENTER);
lpane.setBounds(0,0,panelBg.getImg().getWidth(null),panelBg.getImg().getHeight(null));
panelBg.setBounds(0,0,panelBg.getImg().getWidth(null),panelBg.getImg().getHeight(null));
panelBg.setOpaque(true);
lpane.add(panelBg, new Integer(0), 0);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
go(lpane,panelBg);
}
private static void go(JLayeredPane pan,ImagePanel panBg) {
Parcours panelP = new Parcours();
panelP.setBounds(0,0,panBg.getImg().getWidth(null),panBg.getImg().getHeight(null));
panelP.setOpaque(false);
pan.add(panelP, new Integer(1), 0);
for(int i=0; i<panelP.getTable().size(); i++){
panelP.setPosX(panelP.getTable().get(i).getX()-6);
panelP.setPosY(panelP.getTable().get(i).getY()-6);
panelP.repaint();
try{
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
"go" reads an ArrayList containing coordinates where my circle should go.
I really don't know how to create several trains.
Should I create several JPanels or only one with all my circles ?
If I remember well, I should use Threads but I tried to implement them and I can't start.
Thank you for your help
You could use a central data object that stores the trains.In every cycle the trains get drawn inside swing. On the other side the trains get updated from your threads.
Another approach contains train objects that get drawn and run a thread inside them to update them self.
Thank you Robin (see comments of my first post), Swing Timers seem to be the best solution so far.
I removed my awful Thread.sleep and set timers instead, it works, thanks again.
Thank you Templar too

java swing repaint()

(second question in a few hours)
Kay so I'm making a chess variant in java, I have my console program working how I want it but now I'm trying to convert it to a swing GUI while STILL keeping the console things intact. So up to now I have my array of squares with pieces in them for the console, and a 2-dimensional array of JPanels with pieces in them for the GUI. I haven't implemented moving pieces in the GUI yet so I'm still doing it from the console but the actual GUI doesn't update after I've moved a piece...even though it does on the console (sorry if this is confusing).
The GUI consists of a constructor which calls some methods drawBoard() and drawSidebar() and sets sizes, titles etcetc...so this is what the main method looks like:
public static void main(String args[]) {
ChessGUI GUI = new ChessGUI();
Board console = new Board();
do {
console.printBoard();
console.getScore();
console.getMove();
GUI.boardPanel.revalidate();
GUI.sidePanel.revalidate();
GUI.repaint();
} while (true);
}
and drawBoard() incase it makes any difference:
public void drawBoard() {
LayoutManager layout = new GridLayout(NUMBER_OF_ROWS, NUMBER_OF_COLS);
boardPanel.setLayout(layout);
boardPanel.setPreferredSize(new Dimension(200, 450));
chessBoard = new JPanel[NUMBER_OF_ROWS][NUMBER_OF_COLS];
for (int i = 0; i < NUMBER_OF_ROWS; i++) {
for (int j = 0; j < NUMBER_OF_COLS; j++) {
chessBoard[i][j] = new JPanel();
chessBoard[i][j].setBackground(getColor(i,j));
int index = i * 4 + j;
if (!(boardArray.chessBoard[index].square.isEmpty())) {
Piece piece = (Piece) boardArray.chessBoard[index].square.firstElement();
chessBoard[i][j].add(new JLabel(piece.toString()));
}
boardPanel.add(chessBoard[i][j]);
}
}
}
the repaint and revalidate methods don't seem to be calling at all, even though the console is being updated :(
I don't really understand what you are doing. But it doesn't make sense to recreate the entire board panel every time a move is made. All Swing components can only have a single parent, to the easier solution is to just move the piece from one panel to the other. So the code would be something like:
previousPanel.remove( piece );
currentPanel.add( piece );
previousPanel.revalidate();
previousPanel.repaint();
currentPanel.revalidate();
It looks like you're never actually removing anything from 'boardPanel,' even though you are resetting its LayoutManager.
A safer approach might be to remove 'boardPanel' from its container, then create a new instance for 'boardPanel,' add that to the container, then add the other JPanel pieces to this new 'boardPanel.' Effectively, you would be reconstructing the entire JPanel hierarchy after every move.
As you've noticed, Swing can be quite finicky once you start trying to add/move/remove components after they've been added to containers. For games, often the best approach would be to have 1 JComponent/Component and use Java2D methods to draw on top of it. Swing is typically only used for forms-based applications.
Changing the layout doesn't do anything.
You need to call boardPanel.removeChildren()
However, this is going to be extremely slow.
Really, what you should be doing is have your own JPanel, overwrite paintComponent() and draw the images into the appropriate dimensions using Java Graphics.

Categories