Toggling between JPanels (hide/show) - java

I have two JPanels that sit on top of one another. The 'top' panel holds many widgets (JButtons, JTextFields, etc.). One of the buttons will initiate an action to display a number of images.
These images are displayed on the other JPanel. So, when this button is clicked, I want to hide the control panel and display the images panel. Sounds pretty simple.
Here is the code (I've omitted a lot of stuff that I don't think is relevant). In the constructor, if I switch which panel is visible when the app launches, it looks fine either way. When I click the button, I should go from my dark gray control panel to my blue images panel. Except that what happens is my dark gray control panel becomes an empty white panel. Any ideas?
public GUI() {
JFrame frame = new JFrame();
...
JPanel imagesPanel = new ImagesPanel();
imagesPanel.setBackground(Color.BLUE);
imagesPanel.setVisible(false);
frame.getContentPane().add(imagesPanel, BorderLayout.CENTER);
// make a JPanel to hold all of the buttons and text fields
JPanel imagesPanel = new ImagesPanel();
controlPanel.setBackground(Color.DARK_GRAY);
controlPanel.setVisible(true);
frame.getContentPane().add(controlPanel, BorderLayout.CENTER);
...
JButton btnDisplayImages = new JButton("Display Images");
btnDisplayImages.setPreferredSize(standardButtonSize);
btnDisplayImages.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
imagesPanel.setVisible(true);
controlPanel.setVisible(false);
frame.repaint();
frame.setVisible(true);
}
});
// button added to control panel
...
}

Use CardLayout. (docs.oracle.com/javase/tutorial/uiswing/layout/card.html)
final String IMAGES_PANEL = "Images Panel";
final String CONTROL_PANEL = "Control Panel";
CardLayout cardLayout;
JPanel cards;
//Where the components controlled by the CardLayout are initialized:
//Create the "cards".
JPanel card1 = new JPanel();
...
JPanel card2 = new JPanel();
...
//Create the panel that contains the "cards".
cardLayout = new CardLayout();
cards = new JPanel(cardLayout);
cards.add(card1, IMAGES_PANEL);
cards.add(card2, CONTROL_PANEL);
...
//Show images panel
cardLayout.show(cards,IMAGES_PANEL);
...
//Show control panel
cardLayout.show(cards, CONTROL_PANEL);

Related

buttons to change card in cardlayout

I have 2 panels in my frame, 1 is for buttons (I want to use radioButton, but for now it is easier using buttons) and the other one is for the card layout panel. My plan is to shuffle the chad when I press specific button. Like the move button will show me the move panel card. Move panel card has x0 label and text field, Line panel card has x0 and x1 both label and text field.
There are 2 classes, 1 is for the buttonpanel = Buttons
the other one is for the cards = PanelMiddle
Here's my code:
public class PanelMiddle{
JPanel controlPanel = new JPanel();
CardLayout cl = new CardLayout();
JPanel movePanel = new JPanel();
JPanel linePanel = new JPanel();
JLabel x0Label = new JLabel("x0");
JTextField x0TextField = new JTextField(3);
JLabel x1Label = new JLabel("x1");
JTextField x1TextField = new JTextField(3);
public PanelMiddle(){
controlPanel.setLayout(cl);
//move panel
movePanel.setLayout(new GridLayout (1,2));
movePanel.add(x0Label);
movePanel.add(x0TextField);
controlPanel.add(movePanel,"Move"); //add the keyword Move to show the move card
//line panel
linePanel.setLayout(new GridLayout (2,2));
//linePanel.add(x0Label);
linePanel.add(x1Label);
//linePanel.add(x0TextField);
linePanel.add(x1TextField);
controlPanel.add(linePanel,"Line"); // add the keyword Line to show the line card
}
}
In the other class I have:
public class Buttons extends PanelMiddle{
JPanel buttonPanel = new JPanel();
JButton moveB = new JButton ("Move");
JButton lineB = new JButton ("Line");
public Buttons(){
buttonPanel.setLayout(new GridLayout (2,1));
buttonPanel.add(moveB);
buttonPanel.add(lineB);
action();
}
public void action(){
moveB.addActionListener((e) -> {
cl.show(controlPanel,"Move");
});
lineB.addActionListener((e) -> { cl.show(controlPanel,"Line");});
}
}
The result that I got is weird. It doesn't show fully my panel. But when I tried commenting all the line panel, it works. Does someone have a fix here?
NB: Im sorry I dont know how to edit the text here so its a little bit messy.
edit 1 : as guleryuz says, I commented out the x0Label and x0TextField from the line panel
in swing component hierarchy a component can only be added to one container, you are adding x0Label and x0TextField two both panels. so when you add x0Labe two second panel (linePanel) it will be removed from movePanel (same case for x0TextField) so movePanel becomes empty.
more details here

JFrame will not load new screen

I have a class that extends JFrame and works by adding in 2 panels with BoxLayout buttons, and one JTabbedPane in the center which displays graphs.
I want one of the buttons to remove all current components in the frame and add new ones.
Here are the methods used.
private void createAndShowGraphs() {
ImageIcon createImageIcon(lsuLettersPath); //simple png file to fill one tab
final JTabbedPane jtp = new JTabbedPane();
JLabel iconLabel = new JLabel();
iconLabel.setOpaque(true);
jtp.addTab(null, icon, iconLabel);
//Here is where the errors begin
JPanel menu = new JPanel();
menu.setLayout(new BoxLayout(menu, BoxLayout.Y_AXIS));
//I want this button to remove all components currently in the JFrame and replace them with new components specified in the createAndShowIntro() method
menu.add(new JButton(new AbstractAction("Intro Pane") {
public void actionPerformed(ActionEvent e) {
//I've also tried putting removeAll in the Intro method
removeAll();
createAndShowIntro();
}
}));
add(jtp, BorderLayout.CENTER);
add(menu, BorderLayout.WEST);
pack();
setVisible(true);
}
private void createAndShowIntro() {
System.out.println("Made it to Intro");
//all I want is a blank JLabel with the String "test" to show up
JPanel test = new JPanel();
test.setLayout(new BorderLayout());
JLabel label = new JLabel();
label.setText("test");
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setVerticalAlignment(SwingConstants.CENTER);
test.add(label);
add(test, BorderLayout.CENTER);
test.revalidate();
label.revalidate();
validate();
test.repaint();
label.repaint();
repaint();
pack();
setVisible(true);
}
When I call createAndShowGraphs() in main() and then hit the 'Intro' button, everything freezes and nothing is actually removed. I know it makes it the Intro method because of the "Made it to Intro" string output to the terminal.
I've tried all kinds of combinations of invalidate(), validate(), revalidate(), repaint() on the labels and on the frame itself. Really frustrated because I don't know how else I'm going to be able to display 3 different screens to switch back and forth between while only actually displaying one at a time.
Thanks for your time.

How to add two JPanels to a JFrame in the center?

I am creating a game which has a background image with cards displayed overtop. I would like to place the background image and cards such that they're always centered vertically and horizontally, even upon resizing the JFrame.
Currently, I am creating the cards (each a JPanel) and adding them into a container JPanel (no layout manager), then I add that Jpanel to the JFrame. After that I place the background image in a JPanel, then add that JPanel to the JFrame. The result is: The background image is hidden behind the cards and revealed when removing each card as desired. The background image is always centered but the card's JPanel does not move around upon resize. I am having a hard time getting the cards to always be centered, no matter what I try. I also need to add another JPanel to the JFrame in the South border, so that will need to work as well. I appreciate your assistance!
In the class that extends JFrame:
setSize(1060,700);
cardPanel = new JPanel();
cardPanel.setSize(1060,700);
cardPanel.setOpaque(false);
cardPanel.setLayout(null);
...card.setLocation(x, y); //loop through cards
...cardPanel.add(card); //and add each one
add(cardPanel, BorderLayout.CENTER); //add cardPanel to JFrame
//Add background image
bgPanel = new JPanel();
URL url = getClass().getResource("images/dragon_bg.png");
imgIcon = new ImageIcon(url);
JLabel background = new JLabel(imgIcon);
bgPanel.setLayout(new BorderLayout());
bgPanel.add(background);
add(bgPanel, BorderLayout.CENTER);
setVisible(true);
I would like to place the background image and cards such that they're always centered vertically and horizontally, even upon resizing the JFrame.
Then you need to use layout managers on your panels. The layout manager is responsible for redoing the layout.
How to add two JPanels to a JFrame in the center?
You could try using the OverlayLayout for this. I think the basic code would be:
JPanel contentPane = new JPanel( new GrigBagLayo9ut() );
frame.add(contentPane, BorderLayout.CENTER);
JPanel overlay = new JPanel()
overlay.setLayout( new OverlayLayout(overlay) );
contentPane.add(overlay, new GridBagConstraints()); // this should center the overlay panel
overlay.add(yourCardPanel); // you care panel must use a suitable layout
overlay.add(new JLabel() ); // use a JLabel for the background not a custom panel
I also need to add another JPanel to the JFrame in the South border,
The default layout manager for a JFrame's content pane is a BorderLayout. We already added the game panel to the center, so know you just add your other panel to the SOUTH.
If the OverlayLayout doesn't work the way you want then you will need to nest panels. Something like:
JPanel center = new JPanel( new GridBagLayout() );
frame.add(center, BorderLayout.CENTER);
JLabel background = new JLabel(...);
background.setLayoutManager( new GridBagLayout() );
center.add(background, new GridBagConstraints());
background.add(yourCardPanel, new GridBagConstraints());
Edit:
Using nested panels:
import java.awt.*;
import javax.swing.*;
public class GridBagLayoutCenter extends JPanel
{
public GridBagLayoutCenter()
{
setLayout( new BorderLayout() );
JLabel background = new JLabel( new ImageIcon("mong.jpg") );
background.setLayout( new GridBagLayout() );
add(background, BorderLayout.CENTER);
JPanel tiles = new JPanel();
tiles.setPreferredSize( new Dimension(200, 200) );
tiles.setBackground( Color.RED );
background.add(tiles, new GridBagConstraints());
add(new JLabel("SOUTH"), BorderLayout.SOUTH);
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("GridBagLayoutCenter");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new GridBagLayoutCenter() );
frame.setSize(400, 400);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
The preferred size of the "tiles" panel should not be hardcoded. The size should be determined by your custom layout manager based on the tiles that you add to the panel. The size should not change as tiles are removed.
I ultimately decided to place the background image in the card panel itself, then put the card panel in a box layout manager so that it's always centered. I renamed cardPanel to gameBoard. Definitely could be cleaner, but I can only work with my requirements.
setSize(new Dimension(1000, 600));
gameBoard = new JPanel();
gameBoard.setLayout(null);
gameBoard.setOpaque(false);
Dimension expectedDimension = new Dimension(920, 500);
gameBoard.setPreferredSize(expectedDimension);
gameBoard.setMaximumSize(expectedDimension);
gameBoard.setMinimumSize(expectedDimension);
//add cards to gameBoard here
JLabel background = new JLabel( new ImageIcon( getClass().getResource("images/graphic.png") ) );
background.setLocation(79,0); //manually center graphic
background.setBounds(new Rectangle(0, 0, 920, 500));
gameBoard.add(background);
Box centerBox = new Box(BoxLayout.Y_AXIS);
centerBox.setOpaque(true);
centerBox.setBackground(Color.WHATEVER);
centerBox.add(Box.createVerticalGlue());
centerBox.add(gameBoard);
centerBox.add(Box.createVerticalGlue());
add(centerBox);
setVisible(true);

Need "non-rectangular Card Layout Panel" or "Transparent component"

I've got a new requirement to change my display.
Currently I have a row of 3 radio buttons sitting above a Panel with a card layout. The buttons CONTROL which card of the Card Layout Panel is displayed. But this, it seems, wastes valuable screen real estate.
The row of buttons can easily be transformed to a column of buttons using a GridLayout(4,1) on the panel that owns them and their title label. This panel could then theoretically be placed in the upper left corner of the each of the cards displayed in the card layout panel, and the top row of each card could be moved up to the right of the button panel, allowing more space for what has to come below.
But what shall be the containment model for this? In this variant, the button panel must appear on each card. Who owns it? Logically it needs to be owned by the parent panel on which all this sits, but it won't display on the cards, which will cover it over - unless I could
add the button panel to each card at the moment of its display, which would probably be messy and maybe cause flicker.
make the top left corner of each card be transparent, allowing it to show the upper left button panel owned by its parent. But how would I do that?
Other solutions?
Maybe
Have two card layout panels controlled by the radio buttons. The upper right one that contains all but the the button panel and the lower component that sits below both.
It sounds like you could use a JLayeredPane as the parent component of both, the panel containing the radio buttons and the card panel.
JLayeredPane allows its child components to overlap, each child belonging to a layer.
To specify the layer, you can pass an Integer constant into the JLayeredPane's add(Component, Object) method as the constraint argument. The integer constants are defined in the JLayeredPane class itself. You can use JLayeredPane.DEFAULT_LAYER for the card panel and JLayeredPane.PALETTE_LAYER for the radio button panel.
Having set the layer, you'll still have to position the two panels correctly in x,y space. I would recommend just overriding the JLayeredPane's doLayout() method with something like this (haven't tested):
public void doLayout()
{
cardPanel.setBounds( new Rectangle( getSize() ));
radioButtonPanel.setBounds( new Rectangle( radtioButtonPanel.getPreferredSize() ));
}
You may be able to use OverlayLayout to display the control panel in the top-left of your card panel.
I would forget the transparency idea. Just put the options as a list to the left of (or right of, or over/under) the card panel. I would definitely NOT put the panel of controls on the CardPanel itself. It should be outside.
The list of selections could be radios, buttons, or in this example, a JList of items that can grow w/o messing up the layout. For example:
/*
* CardLayoutDemo.java
*/
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
#SuppressWarnings("unchecked")
public class CardLayoutDemo implements Runnable
{
final static String CARD1 = "Gray Panel";
final static String CARD2 = "Blue Panel";
final static String CARD3 = "Green Panel";
JPanel cards;
CardLayout cl;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new CardLayoutDemo());
}
public void run()
{
final JList jList = new JList(new String[]{CARD1, CARD2, CARD3});
jList.setPrototypeCellValue("XXXXXXXXXXXX");
jList.setVisibleRowCount(5);
jList.setSelectedIndex(0);
jList.addListSelectionListener(new ListSelectionListener()
{
#Override
public void valueChanged(ListSelectionEvent e)
{
String name = jList.getSelectedValue().toString();
cl.show(cards, name);
}
});
JScrollPane scroll = new JScrollPane(jList);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
Dimension dim = new Dimension(300, 300);
JPanel card1 = new JPanel();
card1.setBackground(Color.GRAY);
card1.setPreferredSize(dim);
JPanel card2 = new JPanel();
card2.setBackground(Color.BLUE);
card2.setPreferredSize(dim);
JPanel card3 = new JPanel();
card3.setBackground(Color.GREEN);
card3.setPreferredSize(dim);
cl = new CardLayout();
cards = new JPanel(cl);
cards.add(card1, CARD1);
cards.add(card2, CARD2);
cards.add(card3, CARD3);
JFrame f = new JFrame("CardLayout Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(scroll, BorderLayout.WEST);
f.add(cards, BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
If you wanted a label for the selections, just make a "selection panel" that contains the JLabel and the JScrollPane (or use your grid of buttons panel), and put it in Borderlayout.WEST (instead of the adding the JScrollPane directly).
Also, look into JTabbedPane as an alternative.

How to make JLabel consume space when not visible?

I have a program which creates 2 Panels and then places a label and two buttons in them. The label is set to invisible setVisible(false) and then the two buttons are added and the frame is packed. When i click the first button, the label is shown, setVisible(true), and the seccond one hides it again, setVisible(false). When i click each button, they move to fill the space of the label as it hides, and move again to get out of the way of the label as it is shown. I want to stop this from happening and have the buttons stay in the same place even when the label is hidden.
Here is the code:
public class MainFrame extends JFrame{
public JLabel statusLabel;
public JButton show;
public JButton hide;
public MainFrame(){
super("MagicLabel");
JPanel topPanel = new JPanel(); //Create Top Panel
statusLabel = new JLabel(""); //Init label
statusLabel.setVisible(false); //Hide label at startup
topPanel.setSize(400, 150); //Set the size of the panel, Doesn't work
topPanel.add(statusLabel); //Add label to panel
JPanel middlePanel = new JPanel(); //Create Middle Panel
show= new JButton("Show"); //Create show button
hide= new JButton("Hide"); //Create hide button
middlePanel.setSize(400, 50); //Set the size of the panel, Doesn't work
middlePanel.add(show); //Add show button
middlePanel.add(hide); //Add hide button
this.add(topPanel, "North"); //Add Top Panel to North
this.add(middlePanel, "Center"); //Add Middle Panel to Center
addActionListeners(); //void:adds action listeners to buttons
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBounds(100, 100, 512, 400);
this.setPreferredSize(new Dimension(400,200)); //Set size of frame, Does work
this.pack();
this.setVisible(true);
}
public void animateInstall(boolean var0){ //Void to show and hide label from action listeners
statusLabel.setVisible(var0);
sendWorkingMessage("Boo!");
}
public void sendWorkingMessage(String message){ //Void to set text of label
this.statusLabel.setForeground(new Color(225, 225, 0));
this.statusLabel.setText(message);
}
void addActionListeners(){
show.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
animateInstall(true);
}
});
hide.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
animateInstall(false);
}
});
}
this.setPreferredSize(new Dimension(400,200));
this.setMinimumSize(new Dimension(400,200));
So pack() cannot interfere.
Use CardLayout. Add the JLabel and empty JPanel. Instead of seting it visible/invisible swap the cards showing the JLabel or the JPanel when necesary.
Extending JFrame is not advisable, better extend JPanel put all your components inside and then add it to a JFrame
You need to learn how to use SwingUtilities.invokeLater(): See example how your should look like
You need to learn about Layout: Tutorial
Very dumb and easy approach in your code would be:
this.statusLabel.setForeground(bgColor); //background color
this.statusLabel.setText(" "); //some number of characters
By default for you frame you are using BorderLayout. You can try to have like:
this.add(topPanel, BorderLayout.NORTH); //Add Top Panel to North
this.add(middlePanel, BorderLayout.SOUTH); //Add Middle Panel to South
rather than at center.
Or you can create an intermediate container panel for these 2 panels, or consider other layout managers like BoxLayout, etc

Categories