I'm making a game and I'm trying to switch between different JPanels to display the menu, world etc.
public static void setComponent(Component component){
frame.getContentPane().removeAll();
frame.getContentPane().add(component, 0);
}
This is the method to change the component of the JFrame frame, this works for the first time with whichever JPanel component I give it, but when I try to change it from inside any class from a keyListener method say, the content pane doesn't display anything.
#Override
public void mousePressed(MouseEvent arg0) {
Frame.setComponent(new MenuPanel());
}
Method to create the JFrame and the thread that repaints the components
public static void createFrame(){
frame = new JFrame();
frame.setSize(size);
frame.setTitle(title);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setLayout(new GridLayout(1,1,0,0));
new Thread(){
public void run(){
while(true){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(frame.getComponentCount() != 0){
//System.out.println("?");
frame.getContentPane().repaint();
System.out.println(frame.getContentPane().getComponentAt(1, 1));
}
}
}
}.start();
setComponent(new MenuPanel());
frame.setVisible(true);
}
Getting this result from the Output Stream when first loading
Menu.MenuPanel[,0,0,794x571,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,preferredSize=]
Then after using the setComponent method from another class
javax.swing.JPanel[null.contentPane,0,0,794x571,invalid,layout=java.awt.GridLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,preferredSize=]
When you call your setComponent method the first time, you do that before you call frame.setVisible(true) that forces the frame to validate all its components and show them.
When you change the components later you need to revalidate your frame manually so it gets aware of its components. Add these lines to your setComponent method and it should work.
public static void setComponent(Component component){
frame.getContentPane().removeAll();
frame.getContentPane().add(component, 0);
frame.revalidate(); // revalidate all the frame components
frame.repaint(); // and repaint the frame
}
Related
I am trying to make a Swing GUI that includes some 3D stuff using Java3D's Canvas3D object. The problem is that it takes a while for a Canvas3D object to initialize, and I want the Swing GUI to come up right away. My solution to this problem is to initialize the Canvas3D in a separate thread, and then add it to the JFrame once it is initialized. However, when that separate thread adds the Canvas3D to the JFrame, the window loses focus for a moment, which is undesirable. How can I prevent that from happening? I have included a simple example to illustrate what I am trying to do:
public class Main extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main()::setup);
}
private void setup() {
setSize(600, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
Thread thread = new Thread() {
#Override
public void run() {
Canvas3D canvas = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
SwingUtilities.invokeLater(() -> {
Main.this.add(canvas); //the window loses focus for a moment here
Main.this.revalidate();
});
}
};
thread.start();
}
}
I am using Java3D 1.7.1.
I have modified my code as per R VISHAL's comment, but the problem still persists.
public class Main extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main()::setup);
}
private void setup() {
setSize(600, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
SwingWorker<Canvas3D, Object> worker = new SwingWorker<Canvas3D, Object>() {
#Override
public Canvas3D doInBackground() {
return new Canvas3D(SimpleUniverse.getPreferredConfiguration());
}
#Override
public void done() {
try {
Main.this.add(get());
} catch (InterruptedException|ExecutionException e) {
throw new RuntimeException();
}
Main.this.requestFocusInWindow();
Main.this.revalidate();
}
};
worker.execute();
}
}
Ok so this might not be the answer but it was to large to put it as an comment
Use a JPanel having a CardLayout as your frame's contentpane, have one screen inside this panel set as the background[or whatever initial screen you want to display before the canvas is displayed], and then once the canvas is initialized add it to the content pane as the second screen and then call the CardLayout's show() method do display the canvas
public class Add
{
public static void main(String args[])
{
JFrame frame=new JFrame("Test");
JPanel mainPanel=new JPanel(new CardLayout());
mainPanel.addMouseListener(new MouseAdapter()
{
int count=0;
#Override
public void mouseClicked(MouseEvent m)
{
System.out.println("Focus "+(++count));
}
});
JPanel background=new JPanel();
background.setBackground(Color.WHITE);
mainPanel.add("Screen1",background); //Initial Screen To Show Something To The User While Canvas Is Being Initialized
frame.setContentPane(mainPanel);
SwingWorker worker=new SwingWorker<Canvas3D,Object>()
{
#Override
public Canvas3D doInBackground(){return new Canvas3D();}
#Override
public void done()
{
try
{
mainPanel.add("Screen2",get()); //Add Canvas To MainPanel
CardLayout layout=(CardLayout)mainPanel.getLayout();
layout.show(mainPanel,"Screen2"); //Remember This While Using CardLayout
}
catch(InterruptedException | ExecutionException ex){}
}
};
frame.setSize(500,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
worker.execute();
}
private static final class Canvas3D extends JPanel
{
private Canvas3D()
{
super(new BorderLayout());
try{Thread.sleep(5000);}//Mimicing Long Operations
catch(Exception ex){}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0,0,500,500);
g.setFont(new Font("",Font.BOLD,15));
g.setColor(Color.RED);
g.drawString("CANVAS 3D",100,100);
}
}
}
Of course you would use your actual canvas3d instead of this custom one and there is no need to revalidate or requestFocus()
If you are still worried about focus you could always create an javax.swing.Timer class to request focus to your mainPanel or frame[Or Both See What Works For You] every second/or millisecond
Timer timer=new Timer(1000,new ActionListener() //milliseconds make it 1 if your are dead serious about focus
{
#Override
public void actionPerformed(ActionEvent e)
{
mainPanel.requestFocusInWindow();
frame.requestFocusInWindow(); //May not be required since we are already requesting focus in mainPanel
}
});
timer.start();
If you want to get even more paranoid about focus you could always add an focus listener
mainPanel.addFocusListener(new FocusListener()
{
#Override
public void focusLost(FocusEvent e)
{
mainPanel.requestFocusInWindow();
frame.requestFocusInWindow();
}
#Override
public void focusGained(FocusEvent e){}
});
If any of these suggestions did/didn't work comment below :)
I had a problem while calling two mouse events, one into the other. I wanted to show a second frame (frame2) when the user clicks on a component (component1) from the first frame (frame1), then returns to the previous frame (frame1) if the component2 is clicked on. All this using one file.
This is what I wrote:
component1.addMouseListener(this on);
#Override
public void mouseClicked(MouseEvent e)
{
if(e.getSource() == component1)
{
frame1.dispose();
frame2.setVisible(true);
component2.addMouseListener(new MouseAdapter() {
public void mouseClicked() {
frame2.dispose();
frame1.setVisible(true);
}
});
}
}
The first event works, but not the second.
Thank you for answering.
Here is a fully functional example where there are 2 frames, each with a label that, when clicked, hides one frame and shows the other, done in Java 10. See if this works for you and explain how your code differs from this. Note that I only created 2 MouseListeners, one for each frame. I did not recreate the MouseListener in the other MouseListener's code. Also, I did not dispose the frame, which will likely cause problems. If I had disposed frame1, I would most likely have to create a new JFrame and assign it to the frame1 instance member.
Please note you have to click on the label itself, not somewhere else on the frame.
import javax.swing.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class TwoFrames {
public static void main(String[] args) {
TwoFrames twoFrames = new TwoFrames();
twoFrames.start();
}
private void start() {
setupFrames();
}
JFrame frame1 = new JFrame("Frame 1"),
frame2 = new JFrame("Frame 2");
JLabel component1 = new JLabel("Click me 1"),
component2 = new JLabel("Click me 2");
private void setupFrames() {
frame1.getContentPane().add(component1);
frame2.getContentPane().add(component2);
component1.setOpaque(true);
component2.setOpaque(true);
component1.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
frame1.setVisible(false);
frame2.setVisible(true);
}
});
component2.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
frame2.setVisible(false);
frame1.setVisible(true);
}
});
frame1.setSize(300, 300);
frame2.setSize(400, 400);
frame1.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame2.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(() -> frame1.setVisible(true));
}
}
The dispose() method actually destroys the window and so, frame1 should become null and you are most likely getting a null pointer exception.
Consider calling frame1.setVisible(false) and frame2.setVisible(false) instead of the dispose() method.
Also, you could consider using to separate mouse listener objects instead of adding a new mouse listener to component2 when component1 is clicked.
I'm changing my JFrame's content pane and simply want to focus a JTextField in the new panel. So I'm doing this:
JPanel pNew = new JPanel();
frame.setContentPane(pNew);
frame.revalidate();
frame.repaint();
public JPanel() {
...
tf.requestFocusInWindow();
}
When I use setVisible(false) and setVisible(true) instead of revalidating and repainting my frame, I get my wished effect, but that's not the way I want to do it.
What else happens in setVisible() but revalidating and repainting?
A CardLayout is typically used to swap panels.
However, even the default implementation of CardLayout does not set focus on the panel when it is swapped. However you can check out Card Layout Focus which will allow you to request focus on the panel when it is switched.
The requestFocusInWindow() method only works on a component that is displayed in a visible frame. So you can't invoke the method in the constructor of the class.
You could use the RequestFocsListener found in Dialog Focus. It will wait until the panel is added to a visible GUI before generating the event.
I got it to work simply by putting the requestFocusInWindow() call in the button's action listener. As camickr mentioned the call needs to be made after the constructor. Here's an example program showing how I got it to work. Hope it helps!
public class PanelRevalidate {
public JFrame frame;
public MyPanel panel1, panel2;
public PanelRevalidate() {
frame = new JFrame();
panel1 = new MyPanel(1);
panel2 = new MyPanel(2);
frame.setContentPane(panel1);
panel1.getSwap().addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
frame.setContentPane(panel2);
frame.revalidate();
panel2.getTextField().requestFocusInWindow();
}
});
panel2.getSwap().addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
frame.setContentPane(panel1);
frame.revalidate();
panel1.getTextField().requestFocusInWindow();
}
});
frame.setVisible(true);
frame.setResizable(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new PanelRevalidate();
}
});
}
}
And the MyPanel class:
public class MyPanel extends JPanel {
public JTextField tf;
public JButton swap;
public JLabel panel_label;
public MyPanel(int n) {
tf = new JTextField(25);
swap = new JButton("Swap");
panel_label = new JLabel("panel " + n);
add(tf);
add(swap);
add(panel_label);
}
public JButton getSwap() {
return swap;
}
public JTextField getTextField() {
return tf;
}
}
I have a ComponentListener for the main JFrame that sends an event when it is resized but by the time the event is fired, the Jpanel has already been resized.
public class Test implements ComponentListener {
private JFrame frame;
private JPanel panel;
public Test() {
frame = new JFrame();
panel = new JPanel();
frame.setPreferredSize(new Dimension(300, 300));
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.addComponentListener(this);
frame.pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
Test test = new Test();
test.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
#Override
public void componentResized(ComponentEvent e) {
System.out.println(frame.getSize() + " : " + panel.getSize());
frame.revalidate();
}
#Override
public void componentHidden(ComponentEvent e) {}
#Override
public void componentMoved(ComponentEvent e) {}
#Override
public void componentShown(ComponentEvent e) {}
}
How do I control JPanel setSize during a JFrame resize?
Edit: I understand BorderLayout sets components to fill the border. How does it set the size of the component? Is it calling setSize? if yes, why an overridden setSize called?
You can't control a components size directly when it is added to a container under a LayoutManager's control. That defeats the purpose of using a layout manager.
If you want to be notified when a components size has changed, add a ComponentListener to the component (not the container).
Is it calling setSize?
Layout manager use getPreferredSize(), getMinimumSize() and getMaximum() size as hints. The layout manager and use or ignore these hints. When you add a component to the CENTER of the BorderLayout all the hints are ignored and the component is sized to the space available. So, yes, the layout manager will invoke the setSize() (and setLocation) methods of a component. You should not invoke the setSize() method directly
why isn't my overridden setSize called?
You did not override the setSize() method. When you execute your code you invoked the setSize() method. Then later the layout manager is invoked when the frame is packed() and the layout manager will invoke the setSize() method on the panel.
You cannot resize a component in a BorderLayout if it's constraint is BorderLayout.CENTER.
The component takes automatically all the available space.
More informations : http://docs.oracle.com/javase/tutorial/uiswing/layout/border.html
I have a button in my GUI that is supposed to close its window. However, its event handler is located in an AbstractAction subclass that is located in a seperate method from the JFrame. Because of this I cannot see a way to tell the JFrame to close from my AbstractAction.
Heres the basic layout of my code:
public PointWindow()
{
initialize();
}
public void initialize()
{
JFrame frame = new JFrame();
// JFrame stuff
frame.setContentPane(createGUI());
frame.setVisible(true);
}
public JPanel createGUI()
{
JPanel gui = new JPanel();
// Code....
class MakeGraphACT extends AbstractAction
{
public void actionPerformed(ActionEvent e)
{
frame.setVisible(false); // <--- How to get this to work?
frame.dispose(); // <---
new GraphWindow(pointList);
}
}
//Code...
return gui;
}
Have I done a bad job planning my code or am I just missing something obvious?
You need to pass the frame as a final parameter to the createGUI() method.
You will then be able to access it from within the inner class.