For some reason the last added button does not get displayed. I tried reordering them but still the same result. I checked the coordinates and they are correct.
The last 3 is where I get the problem. The last "add" button doesn't get displayed.
public class MainScreen extends Frame implements MouseListener{
HowToPlay otherFrame;
Button start, howto, settings, about ;
Image MainMenu;
MainScreen(){
Toolkit tkMM = Toolkit.getDefaultToolkit();
MainMenu = tkMM.getImage(this.getClass().getResource("MainMenu.jpg"));
otherFrame = new HowToPlay();
Button start = new Button ("Start Game");
start.setBounds(98, 333, 326, 51);
Button howto = new Button ("How to Play");
howto.setBounds(98, 389, 326, 29);
howto.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setVisible(false);
otherFrame.setVisible(true);
}
});
Button settings = new Button ("Settings");
settings.setBounds(98, 424, 326, 29);
Button about = new Button ("About");
about.setBounds(98, 462, 326, 29);
about.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setVisible(false);
otherFrame.setVisible(true);
}
});
add(start);
add(howto);
add(settings);
add(about);
setVisible(true);
setSize(500, 500);
setResizable(false);
setLayout(null);
setLocationRelativeTo(null); // Center the frame
setSize(500,500);//size of the canvass
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e){
super.windowClosed(e);
System.exit(0);
}
});
}
Wow nice it worked. Still puzzled by the disappearing button problem though.
The default layout manager is a BorderLayout.
When you add components to a BorderLayout without specifying a constraint the component will be added to the CENTER. However, only one component can be added to the center, so the last one added (the "about" button) will be handled by the layout manager.
When you invoke the setVisible(true) method, the layout manager is invoked and the "about" button is given a size/location. All the other buttons are ignored because the BorderLayout doesn't care about them.
However, the size of the frame is (0, 0) so there is no space to allocate to the "about" button so it is given a height of 0, which effectively means there is nothing to paint.
So when the frame is painted the other 3 buttons a painted properly but not the "about" button.
Change the code to:
setSize(500, 500);
setVisible(true);
and see what happens.
Your problem is that you're calling setLayout(null); at the end after adding all components. To fix this, call it at the beginning:
MainScreen() {
setLayout(null);
Toolkit tkMM = Toolkit.getDefaultToolkit();
//.....
Having said that I strongly urge you to avoid use of null layouts and setBounds
e.g.
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.SwingUtilities;
public class MainScreen2 extends Panel {
private static final int PREF_W = 500;
private static final int PREF_H = 500;
public MainScreen2() {
Panel bottomPanel = new Panel(new GridLayout(0, 1));
bottomPanel.add(new Button("Start Game"));
bottomPanel.add(new Button("How To Play"));
bottomPanel.add(new Button("Settings"));
bottomPanel.add(new Button("About"));
setLayout(new BorderLayout());
add(bottomPanel, BorderLayout.PAGE_END);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
Frame frame = new Frame("MainScreen2");
frame.add(new MainScreen2());
frame.pack();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
super.windowClosed(e);
System.exit(0);
}
});
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
The default layout manager for Frame is BorderLayout. When you use add(item) that item is placed in center, replacing anything that was there.
You need to use a different layout manager
Related
I'm making a Game where I have to DrawingComponent painting everything related to the game. Now I want to add a "close" button over the DrawingComponent.
public class SimulationWindow extends JFrame
{
private static final long serialVersionUID = 1L;
public SimulationWindow()
{
super("Game");
setUndecorated(true);
setExtendedState(JFrame.MAXIMIZED_BOTH);
getContentPane().setBackground(Color.BLACK);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton closeButton = new JButton();
closeButton.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0) {
Game.close = true;
}
});
add(new DrawingComponent());
add(closeButton);
setVisible(true);
}
}
But it's just a grey area that pops up. The DrawingComponent which is essentially the game is not visible.
add(new DrawingComponent());
add(closeButton);
You can only add a single component to the BorderLayout.CENTER, which is the default location when you don't specify a constraint.
If you want the button added to the drawing panel the basic code would be:
DrawingComponent draw = new DrawingComponent();
draw.setLayout(...); // set the appropriate layout manager
draw.add( closeButton );
add(draw, BorderLayout.CENTER); // explicitly see the location
I'm having a problem trying to change JPanels by using buttons. I have a JFrame with 2 panels, 1 of them is for the buttons, which i want them to always be showed. The other one is the one that i will be switching everytime i press one ot the buttons of the other panel. The problem is that everytime i press them nothing really ever displays, i keep my buttons but the other panel that i call does not appear.
Code for one of the buttons is as follows
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
ReparacaoPanel r = new ReparacaoPanel(this, this.jPanel1);
this.getContentPane().remove(this.jPanel1);
this.getContentPane().add(r);
//this.setContentPane(r);
this.visiblePanel.setVisible(false);
this.visiblePanel = r;
this.pack();
this.setVisible(true);
r.setLocation(200, 200);
this.getContentPane().revalidate();
this.repaint();
}
If i try to use "this.setContentPane(r);" (it sets the frame to only show the panel) the panel shows. But when i try to call it as i'm trying to do in the code above nothing is showed apart from the panel that has the buttons.
I have no idea what i'm doing wrong, it does not seem to be a problem with the JPanel that i'm trying to call as it shows if used alone.
Anyone can help me out?
Consider this working example for switching manually between panels. Which produces this output.
.........
Some tiny NumberPanel
Every new instance shows another number in the center.
import javax.swing.JPanel;
public class NumberPanel extends JPanel {
private static int counter = 0;
public NumberPanel() {
setLayout(new BorderLayout(0, 0));
JLabel lblNewLabel = new JLabel("" + counter++);
lblNewLabel.setHorizontalAlignment(SwingConstants.CENTER);
add(lblNewLabel);
}
}
Setting up a frame
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
frame.getContentPane().add(panel, BorderLayout.SOUTH);
JButton btnNewButton = new JButton("New button");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
frame.getContentPane().remove(numberPanel);
numberPanel = new NumberPanel();
frame.getContentPane().add(numberPanel, BorderLayout.CENTER);
frame.pack();
}
});
panel.add(btnNewButton);
numberPanel = new NumberPanel();
frame.getContentPane().add(numberPanel, BorderLayout.CENTER);
frame.pack();
}
Testprogram
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TestPanelSwitch {
private JFrame frame;
private NumberPanel numberPanel;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TestPanelSwitch window = new TestPanelSwitch();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public TestPanelSwitch() {
initialize();
}
private void initialize() {
// see above
}
}
Back to the Question
I think you only need to pack your frame, like in the anonymous ActionListener.
frame.getContentPane().remove(numberPanel);
numberPanel = new NumberPanel();
frame.getContentPane().add(numberPanel, BorderLayout.CENTER);
frame.pack();
EDIT
As leonidas mentioned it is also possible to revalidate the frame. This requires only to replace the upper call to pack by theese.
frame.invalidate();
frame.validate();
I'm trying to add a JPanel to my JFrame within an actionListener method, but it appears only after the second click on the button. This is a portion of my code where panCours is a JPanel and ConstituerData the targeted JFrame :
addCours.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
panCours.setBounds(215, 2, 480, 400);
panCours.setBorder(BorderFactory.createTitledBorder("Saisir les données concernant le cours"));
ConstituerData.this.getContentPane().add(panCours);
}
});
I don't understand why it doesn't appear as soon as I click on the button. Any explanation and help about how to fix this ?
You'll need to add a call to repaint(); (as well as probably revalidate();) to get the JPanel to show immediately. A basic example demonstrating your problem (and the solution) below;
public class Test {
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
JButton button = new JButton("Test");
button.setBounds(20, 30, 100, 40);
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
JPanel panel = new JPanel();
panel.setBackground(Color.red);
panel.setBounds(215, 2, 480, 480);
frame.add(panel);
frame.revalidate(); // Repaint here!! Removing these calls
frame.repaint(); // demonstrates the problem you are having.
}
});
frame.add(button);
frame.setSize(695, 482);
frame.setVisible(true);
}
}
The above said, (as suggested by others) it's only right that I recommend against the use of a null layout in future. The swing layouts are a little awkward to begin with, but they will help you a great deal in the long run.
the answer can be found in the following snippet:
you need to revalidate() the contentPane, not repaint the frame. you can add any panel you want to the contentpane like this. if you declare contentPane as a private field you dont need your getContentPane() call. contentPane is global so it can be reffered to directly from anywhere within the class.
be careful about NullPointerExeptions which can be thrown if you refer to it before initialising.
public class testframe extends JFrame {
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
testframe frame = new testframe();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public testframe() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = new JPanel();
setContentPane(contentPane);
JButton btnNewButton = new JButton("New button");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JPanel a = new JPanel();
contentPane.add(a);
contentPane.revalidate();
}
});
contentPane.add(btnNewButton);
}
}
I'm having a problem with this. I have a JPanel and normally I would create a JLabel like this:
JLabel lblNewLabel = new JLabel("New label");
lblNewLabel.setBounds(0, 0, 135, 14);
panel.add(lblNewLabel);
but I want each time I click a button, in that panel to be created a new JLabel with the same size, but with a different height possition. I tried:
panel.add(new JLabel(stringName));
but this way I don't get to set it's bounds. stringName I get from a JTextField.
First off, use a layout. Done correctly the layout will place the components like you want. Secondly, when dynamically adding a component to a layout you need to tell the layout to update. Here is an example, it adds a label each time a button is pressed:
public static void main(String[] args) {
final JFrame frame = new JFrame("Test");
frame.setLayout(new GridLayout(0, 1));
frame.add(new JButton(new AbstractAction("Click to add") {
#Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame.add(new JLabel("Bla"));
frame.validate();
frame.repaint();
}
});
}
}));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() {
frame.setVisible(true);
}
});
}
As said by #AndrewThompson use a correct LayoutManager, you should not be messing with setBounds etc.
Here is an example I made (Simply adds a JLabel to the JPanel each time the JButton is clicked):
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Test {
public Test() {
createAndShowUI();
}
private void createAndShowUI() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents(frame);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
}
private void initComponents(final JFrame frame) {
final JPanel panel = new JPanel();
JButton button = new JButton("Add label");
button.addActionListener(new ActionListener() {
int count = 1;
#Override
public void actionPerformed(ActionEvent e) {
JLabel _lbl = new JLabel("Label " + count);//make label and assign text in 1 line
panel.add(_lbl);//add label we made
panel.revalidate();
panel.repaint();
frame.pack();//so our frame resizes to compensate for new components
count++;
}
});
frame.add(panel, BorderLayout.CENTER);
frame.add(button, BorderLayout.SOUTH);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
}
Try swapping the order of of your commands, add the panel first then set the location.
I have a JScrollPane that contains a vertical Box. I'm inserting new JPanel's at the top of Box. If I use the scrollbar to scroll down I'd like for the current view to remain where I scrolled down to. For example, if I have 50 panels in the box and use the scrollbar to view panel 20, I'd like the view to remain on box 20 even though other boxes are added on top. Additionally, if I use the scrollbar to scroll back up to the top I'd like the view to display new panels as they are added. Any idea how to do this?
BTW, it isn't necessary to use a JScrollPane or a Box. The example code is just to help explain what I am trying to do.
Example code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TestScrollPane extends JFrame {
JScrollPane scrollPane;
Box box;
private static int panelCount = 0;
public TestScrollPane() {
setPreferredSize(new Dimension(200, 400));
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
scrollPane = new JScrollPane();
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.getVerticalScrollBar().setUnitIncrement(15);
box = Box.createVerticalBox();
scrollPane.getViewport().add(box);
this.add(scrollPane);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
Timer t = new Timer(500, new ActionListener() {
public void actionPerformed(ActionEvent ae) {
box.add(new TestPanel(), 0);
scrollPane.validate();
}
});
t.setRepeats(true);
t.start();
}
public class TestPanel extends JPanel {
int myId = panelCount++;
public TestPanel() {
this.setLayout(new GridBagLayout());
this.setBorder(BorderFactory.createBevelBorder(1));
JLabel label = new JLabel("" + myId);
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalAlignment(JLabel.CENTER);
this.setMaximumSize(new Dimension(100, 100));
this.setPreferredSize(new Dimension(100, 100));
this.add(label);
}
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
TestScrollPane testScrollPane = new TestScrollPane();
}
});
}
}
EDIT:
This is how I ended up changing the code. I feel somewhat foolish for not seeing the obvious. Anyways, thanx to those that helped.
public void actionPerformed(ActionEvent ae) {
Point view = scrollPane.getViewport().getViewPosition();
TestPanel panel = new TestPanel();
box.add(panel, 0);
scrollPane.validate();
if (view.y != 0) {
view.y += panel.getHeight();
scrollPane.getViewport().setViewPosition(view);
}
}
BTW, I had cross posted this question to http://www.coderanch.com/t/528829/GUI/java/JScrollPane-adding-JPanels-at-top#2398276 Just FYI for those that might care.
You could get the bounds of the component you want to make visible (using JComponent's getBounds method), and use that as an input to JViewPort's scrollRectToVisible method.
Something like:
Timer t = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent ae) {
TestPanel panel = new TestPanel();
box.add(panel, 0);
JViewport vp = scrollPane.getViewport();
Point p = vp.getViewPosition();
p.y += panel.getPreferredSize().height;
scrollPane.revalidate();
vp.setViewPosition(p);
}
});