How to make a JPanel display over another JPanel when moved? - java

I have created a card game which allows the users to move the cards, which are JPanels, on top of each other. However, I noticed that if I attempt to move a card to another cards exact location (ie on top of it), that card will not always be displayed on top of that card.
For example, lets say we have 5 cards, which where built in order.
If move card1 to card2's location, then card1 will appear on top of card2. However, if I tried to move card5 to card3's location, then card5 will appear underneath card3.
How can can I make is so that the last card that I move will be the one on top?

However, I noticed that if I attempt to move a card to another cards exact location (ie on top of it), that card will not always be displayed on top of that card.
This sounds related to the Z-Ordering of components. Basically the default behaviour for Swing is that the last component added to a panel is painted first.
So you need to change the Z-Order when you add the card on the panel. You are probably using code like:
panel.add( card );
The easy solution is to use:
panel.add(0, card);
Or, when you handle the mousePressed() event when you click on the card your would use:
Component child = event.getComponent();
Component parent = child.getParent();
parent.setComponentZOrder(child, 0);
You may also want to look at the Overlap Layout which explains Z-Ordering a little more and provides a layout manager that can allow you to stack cards.

For this purpose card layout is your friend.
How to use card layout https://docs.oracle.com/javase/tutorial/uiswing/layout/card.html
Example uses:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CardLayoutDemo implements ItemListener {
JPanel cards; //a panel that uses CardLayout
final static String BUTTONPANEL = "Card with JButtons";
final static String TEXTPANEL = "Card with JTextField";
public void addComponentToPane(Container pane) {
//Put the JComboBox in a JPanel to get a nicer look.
JPanel comboBoxPane = new JPanel(); //use FlowLayout
String comboBoxItems[] = { BUTTONPANEL, TEXTPANEL };
JComboBox cb = new JComboBox(comboBoxItems);
cb.setEditable(false);
cb.addItemListener(this);
comboBoxPane.add(cb);
//Create the "cards".
JPanel card1 = new JPanel();
card1.add(new JButton("Button 1"));
card1.add(new JButton("Button 2"));
card1.add(new JButton("Button 3"));
JPanel card2 = new JPanel();
card2.add(new JTextField("TextField", 20));
//Create the panel that contains the "cards".
cards = new JPanel(new CardLayout());
cards.add(card1, BUTTONPANEL);
cards.add(card2, TEXTPANEL);
pane.add(comboBoxPane, BorderLayout.PAGE_START);
pane.add(cards, BorderLayout.CENTER);
}
public void itemStateChanged(ItemEvent evt) {
CardLayout cl = (CardLayout)(cards.getLayout());
cl.show(cards, (String)evt.getItem());
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event dispatch thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("CardLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
CardLayoutDemo demo = new CardLayoutDemo();
demo.addComponentToPane(frame.getContentPane());
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
You can easily modify it to you fulfill your goal.

Related

Java Swing change panel when a button is pressed from different class

I am writing a program in Java with Java Swing. I have a class, which is a custom JPanel (my class extends JPanel), which is a log in page. The panel contains a button called "Enter".
When I create my main JFrame, I add the Log in Panel in it. When the button "Enter" is pressed I want to remove the Log In panel and proceed to the next panel.
So how can I make my frame understand when the button "Enter" from the Log In panel is pressed (they are in different classes), so that it proceeds to the next page?
To be able to switch beetween JPanels enclose them in CardLayout:
JPanel cards;
final static String BUTTONPANEL = "Card with JButtons";
final static String TEXTPANEL = "Card with JTextField";
//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".
cards = new JPanel(new CardLayout());
cards.add(card1, BUTTONPANEL);
cards.add(card2, TEXTPANEL);
You should add ActonListener to the "Enter" button:
JButton enterButton = ...
enterButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
CardLayout cl = (CardLayout)// your reference to the CardLayout here eg. yourJFrame.getContentPane().getLayout();
cl.show(cards, "The name of panel to show, you gave it with the add operation on cardLayout eg. BUTTONPANEL OR TEXTPANEL");
}
});

The function of JPanel

I am working with Swing, trying to learn how to use it. I am doing an excercise in my book where I am supposed to make buttons (JButtons) like the ones a dvd-player has. I am adding no funcionality to the buttons at this point. The program worked just fine when I first ran it. Then I thought that I would expand it by making a second panel (JPanel) with the same buttons. When I run my code however, I just get one set of buttons (not two areas with two sets of buttons).
Have I misunderstood the concept of Panels? I have understood a Panel to be an area (a container) in my frame where I can display different output, and that I can have several panels i one frame.
Here is my code:
import javax.swing.*;
public class Oppgave91 extends JFrame
{
public Oppgave91()
{
super ("We make buttons");
setDefaultCloseOperation(EXIT_ON_CLOSE);
JButton play = new JButton("Play");
JButton stopeject = new JButton("Stop/Eject");
JButton rewind = new JButton("Rewind");
JButton fastforward = new JButton("FastForward");
JButton pause = new JButton("Pause");
JPanel panel = new JPanel();
panel.add(play);
panel.add(stopeject);
panel.add(rewind);
panel.add(fastforward);
panel.add(pause);
JPanel panel2 = new JPanel();
panel2.add(play);
panel2.add(stopeject);
panel2.add(rewind);
panel2.add(fastforward);
panel2.add(pause);
add(panel);
add(panel2);
setVisible(true);
}
public static void main(String[] args)
{
Oppgave91 showbuttons = new Oppgave91();
showbuttons.pack();
}
}
A component can have only 1 parent. See the last line of my code snippet for how it should be done
JPanel panel = new JPanel();
panel.add(play);
panel.add(stopeject);
panel.add(rewind);
panel.add(fastforward);
panel.add(pause);
//right now panel is the parent component of play, stop, eject
JPanel panel2 = new JPanel();
panel2.add(play); //play is now owned by panel2, not panel
panel2.add(stopeject); //stopeject is now owned by panel2, not panel
panel2.add(new JButton("Rewind")); // this is how you should do this

Can't shift focus to unshown CardLayout JPanel

Why can't I shift focus to an unshown JPanel card in a CardLayout?
I'm implementing a CardLayout-based interface that needs to be keyboard navigation friendly. When a user finishes tabbing through all the fields on one card, the user needs to be able to tab to the next card.
I've already implemented a FocusTraversalPolicy that produces the right Component at each point in the process, and a FocusAdapter to pop up any cards newly tabbed to, but something is eating the messages and preventing focus change.
I can uncleanly pass the CardLayout to the FocusTraversalPolicy to change the card— though any of its functions are called several times in Swing's many threads and leads to strange behavior. Besides, that way's just dirty.
I do not want to use key bindings b/c that would require reimplementing all of the focus work Java already does for me, and is also really unclean.
Basically: Java dislikes shifting focus to unshown cards in CardLayouts— how can I override this?
I want to keep the program compartmentalized, as it runs in distinct steps.
This does not prevent you from creating a long scrolling form?
You can still create individual panels the way you are doing now. Then instead of adding these panels to a CardLayout where you need to swap panels, you can add the panels to a panel using a BoxLayout (or GridBagLayout).
This would even give more flexibility since each panel can be of a different size without impacting the size of every individual panel.
However, forms do not currently scroll automatically in a JScrollPane, so you may want to check out Scrolling a Form for a class this will do this for you.
It sounds like you have a wizard-like UI. If so, add a "Next" button as the last field on each card.
The action of the Next button would be to flip to the next card as set focus to the first entry field.
The last entry field on each card would transfer focus to the Next button, who could then be "pressed" with a strike of the spacebar when it receives focus (which is the default behavior of a JButton), keeping it keyboard-friendly.
This would alleviate the need for special KeyBindings or FocusTraversalPolicies.
EDIT:
Try this, using FocusListeners for the JTextFields. Tab thorugh the fields and the cards will flip to the next one automatically once you tab out of the last field. You could use ActionListeners instead if you wish:
EDIT 2: Added hack for panels that only have 1 field.
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
public class CardLayoutDemo2 implements Runnable
{
final static String CARD1 = "One";
final static String CARD2 = "Two";
final static String CARD3 = "Three";
JPanel cards;
CardLayout cardLayout;
JTextField tf1, tf2, tf3, tf4, tf5;
JButton dummy;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new CardLayoutDemo2());
}
public void run()
{
tf1 = new JTextField(10);
tf2 = new JTextField(10);
tf2.addFocusListener(new CardFlipper(CARD2));
tf3 = new JTextField(10);
tf4 = new JTextField(10);
tf4.addFocusListener(new CardFlipper(CARD3));
tf5 = new JTextField(10);
tf5.addFocusListener(new CardFlipper(CARD1));
dummy = new JButton()
{
#Override
public Dimension getPreferredSize()
{
return new Dimension(0,0);
}
};
dummy.addFocusListener(new FocusAdapter()
{
#Override
public void focusGained(FocusEvent e)
{
dummy.transferFocus();
}
});
JPanel card1 = new JPanel();
card1.add(new JLabel("One"));
card1.add(tf1);
card1.add(new JLabel("Two"));
card1.add(tf2);
JPanel card2 = new JPanel();
card2.add(new JLabel("Three"));
card2.add(tf3);
card2.add(new JLabel("Four"));
card2.add(tf4);
JPanel card3 = new JPanel();
card3.add(dummy);
card3.add(new JLabel("Five"));
card3.add(tf5);
cardLayout = new CardLayout();
cards = new JPanel(cardLayout);
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(cards, BorderLayout.CENTER);
f.setSize(180, 200);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private class CardFlipper extends FocusAdapter
{
private String nextCard;
CardFlipper(String cardName)
{
this.nextCard = cardName;
}
#Override
public void focusLost(FocusEvent e)
{
cardLayout.show(cards, nextCard);
}
}
}

Changing layout of a JPanel sent to JOptionPane with the pane still running

Trying to change the look of a JOptionPane while its open, depending on which radiobutton the user clicks. What am I doing wrong? It works perfect if I for example add a button and move a JLabel from side to side of the window.
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
import static javax.swing.JOptionPane.*;
public class ChangePanel extends JFrame{
private JButton click = new JButton("CLICK ME!");
ChangePanel(){
add(click, BorderLayout.SOUTH);
click.addActionListener(new ButtonListen());
setVisible(true);
setSize(300,100);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public class ButtonListen implements ActionListener{
public void actionPerformed(ActionEvent e){
PopUpPanel pop = new PopUpPanel();
showConfirmDialog(ChangePanel.this, pop, "Changeable", OK_CANCEL_OPTION);
}
}
//Send this as Parameter to the ConfirmDialog
public class PopUpPanel extends JPanel implements ActionListener{
JRadioButton jewelry = new JRadioButton("Jewelry");
JRadioButton shares = new JRadioButton("Shares");
JRadioButton machine = new JRadioButton("Machine");
PopUpPanel(){
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
ButtonGroup bg = new ButtonGroup();
JPanel north = new JPanel();
bg.add(jewelry);
jewelry.addActionListener(this);
bg.add(shares);
shares.addActionListener(this);
bg.add(machine);
machine.addActionListener(this);
north.add(jewelry);
north.add(shares);
north.add(machine);
add(north);
}
//Listener for RadioButtons
public void actionPerformed(ActionEvent e){
JTextField info1Txt = new JTextField(12);
JTextField info2Txt = new JTextField(12);
JTextField info3Txt = new JTextField(3);;
JRadioButton b = (JRadioButton)e.getSource();
if(b.getText().equals("Jewelry")){
//Dummy test text
System.out.println("Jewelry");
JPanel info1 = new JPanel();
info1.add(new JLabel("info1:"));
info1.add(info1Txt);
add(info1);
JPanel info2 = new JPanel();
info2.add(new JLabel("info2:"));
info2.add(info2Txt);
add(info2);
JPanel info3 = new JPanel();
info3.add(new JLabel("info3:"));
info3.add(info3Txt);
add(info3);
validate();
repaint();
}else if(b.getText().equals("Shares")){
//Dummy test text
System.out.println("Shares");
}else
//Dummy test text
System.out.println("Machine");
}
}
public static void main(String[] args){
new ChangePanel();
}
}
As you are working with BoxLayout, you should provide size hints to the PopUpPanel panel, which you haven't given.
When a BoxLayout lays out components from top to bottom, it tries to size each component at the component's preferred height. If the vertical space of the layout does not match the sum of the preferred heights, then BoxLayout tries to resize the components to fill the space. The components either grow or shrink to fill the space, with BoxLayout honoring the minimum and maximum sizes of each of the components.
check out the official tutorial page discussion: BoxLayout Feature
Call revalidate() and repaint() on the container after removing or adding components to it. So if you change the following lines:
validate();
repaint();
to:
revalidate();
repaint();
The content should appear. Though, it will not fit the original size of the JOptionPane. You can override PopUpPanel.getPreferredSize() to return desired size so that JOptionPane is packed properly, ie:
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
You can also use JDialog instead of JOptionPane.
Also, consider using CardLayout instead of swapping components manually. Check How to Use CardLayout for examples.
Why not just use setPreferredSize(new Dimension(300, 300)) in PopUpPanel constructor? Works fine for me. Good eye on revalidate and repaint.

Java CardLayout Main Menu Problem

Ok so im working on this game in java called 8 bit chimera. Im working on the main menu right now but when im using the card layout the window wont open for some reason. Here is some code.
import javax.swing.*;
import java.awt.*;
public class MainScreen extends JFrame{
String Title = "MainMenu";
MainMenuComp MMC = new MainMenuComp();
BreedingGround BGR = new BreedingGround();
public MainScreen() {
setTitle("8-bit Chimera "+Title);
setSize(800,600);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
add(MMC);
add(BGR);
}
public static void main(String[] args){
new MainScreen();
}
}
that was the Main window
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MainMenuComp extends JPanel implements ActionListener{
BreedingGround BGR = new BreedingGround();
ImageData ID = new ImageData();
Image TitleBg;
Image Title;
CardLayout CL;
JButton Play;
public MainMenuComp() {
setLayout(new GridBagLayout());
GridBagConstraints GBC = new GridBagConstraints();
ImageIcon TitleData = new ImageIcon(ID.TitleSource);
ImageIcon TitleBackGroundData = new ImageIcon(ID.TitleBackGroundSource);
ImageIcon PlayData = new ImageIcon(ID.PlaySource);
TitleBg = TitleBackGroundData.getImage();
Title = TitleData.getImage();
Play = new JButton();
Play.setIcon(PlayData);
add(Play,GBC);
add(BGR,"Breed");
}
public void actionPerformed(ActionEvent AE){
if(AE.getSource() == Play){
CL.show(this, "Breed");
}
}
public void paintComponent(Graphics g){
g.drawImage(TitleBg,0,0,800,600,this);
g.drawImage(Title,250,80,280,140,this);
}
}
this was the card layout
import javax.swing.*;
import java.awt.*;
public class BreedingGround extends JPanel{
ImageData ID = new ImageData();
Image Swamp;
CardLayout CL;
public BreedingGround(){
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
ImageIcon SwampData = new ImageIcon(ID.SwampSource);
Swamp = SwampData.getImage();
}
public void paintComponent(Graphics g){
g.drawImage(Swamp,0,0,800,600,this);
}
}
and that was what i wanted the CardLayout to open. The problem is that when i try to run it the window wont run and this keeps showing in the compiler.
--------------------Configuration: 8-bit Chimera - JDK version 1.6.0_26 - --------------------
Exception in thread "main" java.lang.IllegalArgumentException: cannot add to layout: constraints must be a GridBagConstraint
at java.awt.GridBagLayout.addLayoutComponent(GridBagLayout.java:685)
at java.awt.Container.addImpl(Container.java:1074)
at java.awt.Container.add(Container.java:927)
at MainMenuComp.<init>(MainMenuComp.java:26)
at MainScreen.<init>(MainScreen.java:7)
at MainScreen.main(MainScreen.java:23)
Process completed.
All i really want to know is what this is saying.
I don't see where you ever set the layout of a container to be CardLayout, and if you don't set the layout to this, you can't magically use it. If you haven't yet gone through the CardLayout tutorial, consider doing so as it's all explained there.
Edit 1
Comment from Alexander Kim:
when i added the cardbagLayout it wont load the image and the button size filled the whole screen. I also took away the grids
You need to nest your JPanels in order to nest layouts. Use a single JPanel as the CardLayout container whose single function it is is to display other JPanels (the "cards"). These other JPanels will use whatever layouts that are necessary to properly display the components that they hold such as your JButton or "grids" (whatever they are). And even these JPanels may hold other JPanels that use other layouts.
Again, please read the layout tutorials as it's all described well there. You will not regret doing this.
Edit 2
Here's a very simple example that uses a CardLayout. The component displayed by the CardLayout using JPanel (called the cardContainer) is changed depending on which item is selected in a combobox.
Here's the CardLayout and the JPanel that uses it:
private CardLayout cardLayout = new CardLayout();
// *** JPanel to hold the "cards" and to use the CardLayout:
private JPanel cardContainer = new JPanel(cardLayout);
And here's how I add a component to the cardlayout-using JPanel:
JPanel redPanel = new JPanel();
//...
String red = "Red Panel";
cardContainer.add(redPanel, red); // add the JPanel to the container with the String
I also add the String to a JComboBox so I can use this combo box later to tell the CardLayout to display this JPanel (redPanel) if the user selects the item "Red" in this same JComboBox:
cardCombo.addItem(red); // also add the String to the JComboBox
Here's the ActionListener in the JComboBox that lets me change the item displayed in the cardlayout using JPanel:
cardCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String item = cardCombo.getSelectedItem().toString();
// *** if combo box changes it tells the CardLayout to
// *** swap views based on the item selected in the combo box:
cardLayout.show(cardContainer, item);
}
});
And here's the whole shebang:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SimpleCardLayoutDemo {
private CardLayout cardLayout = new CardLayout();
// *** JPanel to hold the "cards" and to use the CardLayout:
private JPanel cardContainer = new JPanel(cardLayout);
private JComboBox cardCombo = new JComboBox();
private JPanel comboPanel = new JPanel();;
public SimpleCardLayoutDemo() {
JPanel greenPanel = new JPanel(new BorderLayout());
greenPanel.setBackground(Color.green);
greenPanel.add(new JScrollPane(new JTextArea(10, 25)), BorderLayout.CENTER);
greenPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
greenPanel.add(new JButton("Bottom Button"), BorderLayout.PAGE_END);
String green = "Green Panel";
cardContainer.add(greenPanel, green);
cardCombo.addItem(green);
JPanel redPanel = new JPanel();
redPanel.setBackground(Color.red);
redPanel.add(new JButton("Foo"));
redPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
String red = "Red Panel";
cardContainer.add(redPanel, red);
cardCombo.addItem(red);
JPanel bluePanel = new JPanel();
bluePanel.setBackground(Color.blue);
JLabel label = new JLabel("Blue Panel", SwingConstants.CENTER);
label.setForeground(Color.white);
label.setFont(label.getFont().deriveFont(Font.BOLD, 32f));
bluePanel.add(label);
String blue = "Blue Panel";
cardContainer.add(bluePanel, blue);
cardCombo.addItem(blue);
comboPanel.add(cardCombo);
cardCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String item = cardCombo.getSelectedItem().toString();
// *** if combo box changes it tells the CardLayout to
// *** swap views based on the item selected in the combo box:
cardLayout.show(cardContainer, item);
}
});
}
public JPanel getCardContainerPanel() {
return cardContainer;
}
public Component getComboPanel() {
return comboPanel ;
}
private static void createAndShowUI() {
SimpleCardLayoutDemo simplecardDemo = new SimpleCardLayoutDemo();
JFrame frame = new JFrame("Simple CardLayout Demo");
frame.getContentPane().add(simplecardDemo.getCardContainerPanel(), BorderLayout.CENTER);
frame.getContentPane().add(simplecardDemo.getComboPanel(), BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// to run Swing in a thread-safe way
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Your problem is with add(BGR,"Breed");. The layout of MainMenuComp is a GridBagLayout, so the constraint must be a GridBagConstraint, not a String (you have "Breed" as the constraint).

Categories

Resources