I'm having a hard time trying to figure out what is a card layout. I read so many articles and implemented this small example of to see how card layout works. But I can't understand some methods(which are commented). Can someone please help me (I use commandline).
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class C_layout implements ActionListener
{
JButton b2;
JButton b1;
JFrame f1;
JPanel card1;
JPanel card2;
JPanel Jp;
void Example()
{
f1=new JFrame("CardLayout Exercise");
f1.setLocationRelativeTo(null);
f1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f1.setSize(500,500);
f1.setVisible(true);
Container cont=f1.getContentPane();
cont.setLayout(null);
Jp=new JPanel(new CardLayout()); //<-- How to implement card layout here (MAIN PANEL)
f1.add(Jp);
Jp.setLayout //<-- Not sure what means here ERROR
card1=new JPanel(); // First panel
Jp.add(card1);
card2=new JPanel(); // Second panel
Jp.add(card2);
JLabel lb1=new JLabel("This is the first Panel");
lb1.setBounds(250,100,100,30);
card1.add(lb1);
b1=new JButton("NEXT >>");
b1.setBounds(350,400,100,30);
b1.addActionListener(this);
card1.add(b1);
JLabel lb2=new JLabel("This is the second Panel");
lb2.setBounds(250,100,100,30);
card2.add(lb2);
b2=new JButton("<< PREV");
b2.setBounds(250,300,100,30);
b2.addActionListener(this);
card2.add(b2);
}
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==b1)
{
CardLayout cardLayout = (CardLayout) Jp.getLayout();
cardLayout.show(card2,"2");
}
if(e.getSource()==b2)
{
// I still haven't implemented this action listener
}
}
}
class LayoutDemo1
{
public static void main(String[] args)
{
C_layout c=new C_layout();
c.Example();
}
}
cont.setLayout(null); is bad, bad idea, lose it quickly...
You're going to need a reference to your CardLayout in order to manage it. Start by defining a instance field of CardLayout...
private CardLayout cardLayout;
Now, create your instance of CardLayout and apply it to your panel...
cardLayout = new CardLayout();
Jp=new JPanel(cardLayout);
This...
Jp.setLayout //<-- Not sure what means here ERROR
doesn't do anything, it's not a valid statement as far as Java is concerned, in fact, it's actually a method, which should take a reference to the LayoutManager you want to use, but since you've already done that when you created the instance of Jp, you don't need it...
You're going to need some way to identify the components you want to show, CardLayout does this via String names, for example...
card1=new JPanel(); // First panel
Jp.add(card1, "card1");
card2=new JPanel(); // Second panel
Jp.add(card2, "card2");
Now, in your ActionListener, you want to ask CardLayout to show the required view...
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==b1)
{
cardLayout.show(Jp1,"card2");
} else if(e.getSource()==b2)
{
cardLayout.show(Jp1,"card1");
}
}
Note, in order for CardLayout#show to work, you need to pace it a reference of the container to which the CardLayout is assigned AND the name of the view you want to display.
See How to Use CardLayout for more details
Related
I'm trying to make a little game that will first show the player a simple login screen where they can enter their name (I will need it later to store their game state info), let them pick a difficulty level etc, and will only show the main game screen once the player has clicked the play button. I'd also like to allow the player to navigate to a (hopefully for them rather large) trophy collection, likewise in what will appear to them to be a new screen.
So far I have a main game window with a grid layout and a game in it that works (Yay for me!). Now I want to add the above functionality.
How do I go about doing this? I don't think I want to go the multiple JFrame route as I only want one icon visible in the taskbar at a time (or would setting their visibility to false effect the icon too?) Do I instead make and destroy layouts or panels or something like that?
What are my options? How can I control what content is being displayed? Especially given my newbie skills?
A simple modal dialog such as a JDialog should work well here. The main GUI which will likely be a JFrame can be invisible when the dialog is called, and then set to visible (assuming that the log-on was successful) once the dialog completes. If the dialog is modal, you'll know exactly when the user has closed the dialog as the code will continue right after the line where you call setVisible(true) on the dialog. Note that the GUI held by a JDialog can be every bit as complex and rich as that held by a JFrame.
Another option is to use one GUI/JFrame but swap views (JPanels) in the main GUI via a CardLayout. This could work quite well and is easy to implement. Check out the CardLayout tutorial for more.
Oh, and welcome to stackoverflow.com!
Here is an example of a Login Dialog as #HovercraftFullOfEels suggested.
Username: stackoverflow Password: stackoverflow
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import javax.swing.*;
public class TestFrame extends JFrame {
private PassWordDialog passDialog;
public TestFrame() {
passDialog = new PassWordDialog(this, true);
passDialog.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new TestFrame();
frame.getContentPane().setBackground(Color.BLACK);
frame.setTitle("Logged In");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
});
}
}
class PassWordDialog extends JDialog {
private final JLabel jlblUsername = new JLabel("Username");
private final JLabel jlblPassword = new JLabel("Password");
private final JTextField jtfUsername = new JTextField(15);
private final JPasswordField jpfPassword = new JPasswordField();
private final JButton jbtOk = new JButton("Login");
private final JButton jbtCancel = new JButton("Cancel");
private final JLabel jlblStatus = new JLabel(" ");
public PassWordDialog() {
this(null, true);
}
public PassWordDialog(final JFrame parent, boolean modal) {
super(parent, modal);
JPanel p3 = new JPanel(new GridLayout(2, 1));
p3.add(jlblUsername);
p3.add(jlblPassword);
JPanel p4 = new JPanel(new GridLayout(2, 1));
p4.add(jtfUsername);
p4.add(jpfPassword);
JPanel p1 = new JPanel();
p1.add(p3);
p1.add(p4);
JPanel p2 = new JPanel();
p2.add(jbtOk);
p2.add(jbtCancel);
JPanel p5 = new JPanel(new BorderLayout());
p5.add(p2, BorderLayout.CENTER);
p5.add(jlblStatus, BorderLayout.NORTH);
jlblStatus.setForeground(Color.RED);
jlblStatus.setHorizontalAlignment(SwingConstants.CENTER);
setLayout(new BorderLayout());
add(p1, BorderLayout.CENTER);
add(p5, BorderLayout.SOUTH);
pack();
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
jbtOk.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (Arrays.equals("stackoverflow".toCharArray(), jpfPassword.getPassword())
&& "stackoverflow".equals(jtfUsername.getText())) {
parent.setVisible(true);
setVisible(false);
} else {
jlblStatus.setText("Invalid username or password");
}
}
});
jbtCancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
parent.dispose();
System.exit(0);
}
});
}
}
I suggest you insert the following code:
JFrame f = new JFrame();
JTextField text = new JTextField(15); //the 15 sets the size of the text field
JPanel p = new JPanel();
JButton b = new JButton("Login");
f.add(p); //so you can add more stuff to the JFrame
f.setSize(250,150);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Insert that when you want to add the stuff in. Next we will add all the stuff to the JPanel:
p.add(text);
p.add(b);
Now we add the ActionListeners to make the JButtons to work:
b.addActionListener(this);
public void actionPerforemed(ActionEvent e)
{
//Get the text of the JTextField
String TEXT = text.getText();
}
Don't forget to import the following if you haven't already:
import java.awt.event*;
import java.awt.*; //Just in case we need it
import java.x.swing.*;
I hope everything i said makes sense, because sometimes i don't (especially when I'm talking coding/Java) All the importing (if you didn't know) goes at the top of your code.
Instead of adding the game directly to JFrame, you can add your content to JPanel (let's call it GamePanel) and add this panel to the frame. Do the same thing for login screen: add all content to JPanel (LoginPanel) and add it to frame. When your game will start, you should do the following:
Add LoginPanel to frame
Get user input and load it's details
Add GamePanel and destroy LoginPanel (since it will be quite fast to re-create new one, so you don't need to keep it memory).
I am trying to make a Rock, Paper, Scissors game and I have added 3 buttons to the frame, however when I launch the program two of the buttons don't appear until you hover over them, anyone have any idea why?
import javax.swing.*;
import java.awt.event.*;
import java.util.Random;
import java.awt.FlowLayout;
import javax.swing.JOptionPane;
public class RPSFrame extends JFrame {
public static void main(String [] args){
new RPSFrame();
}
public RPSFrame(){
JFrame Frame1 = new JFrame();
this.setSize(500,500);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Rock, Paper or Scissors game");
this.setLocationRelativeTo(null);
ClickListener cl1 = new ClickListener();
ClickListener cl2 = new ClickListener();
ClickListener cl3 = new ClickListener();
JPanel panel1 = new JPanel();
JLabel label1 = new JLabel("Result:");
panel1.setLayout(new FlowLayout(FlowLayout.CENTER, 25, 25));
this.add(panel1);
this.setVisible(false);
JPanel panel2 = new JPanel();
JButton Rock = new JButton("Rock");
Rock.addActionListener(cl1);
panel2.setLayout(new FlowLayout(FlowLayout.LEFT));
panel2.add(Rock);
this.add(panel2);
this.setVisible(true);
JPanel panel3 = new JPanel();
JButton Paper = new JButton("Paper");
Paper.addActionListener(cl2);
panel3.setLayout(new FlowLayout(FlowLayout.CENTER));
panel3.add(Paper);
this.add(panel3);
this.setVisible(true);
JPanel panel4 = new JPanel();
JButton Scissors = new JButton("Scissors");
Scissors.addActionListener(cl3);
panel4.setLayout(new FlowLayout(FlowLayout.RIGHT));
panel4.add(Scissors);
this.add(panel4);
this.setVisible(true);
}
private class ClickListener implements ActionListener{
public void actionPerformed(ActionEvent e){
if(e.getSource() == "Rock"){
int AI = new Random().nextInt(3) + 1;
JOptionPane.showMessageDialog(null, "I have been clicked!");
}
}
}
}
The setVisible(true) statement should be invoked AFTER all the components have been added to the frame.
You currently have two setVisible(...) statements, so you need to get rid of the first one.
Edit:
Took a second look at the code. You have multiple setVisible(...) statements. Get rid of them all except for the last one.
Don't create separate panels for each button. Instead you create one panel (called buttonPanel) for all the buttons. In your case you might use a horizontal BoxLayout. Add a button to the panel, then add glue then add a button, then add glue and then add your final button. Then add this buttonPanel to the NORTH of the frame. ie. this.add(buttonPanel, BorderLayout.NORTH). Read the section from the Swing tutorial on How to Use Box Layout for more information on how the layout works and on what glue is.
The problem is that JFrame has a default BorderLayout. When you just add(component) without specifying a BorderLayout.[POSITION] e.g add(panel, BorderLayout.SOUTH), then the component will get added to the CENTER. The problem with that is each POSITION can only have one component. So the only component you see id the last one you add.
Now I don't know after specifying the positions, if you will get your desired result. A BorderLayout may not be the right fit. But just to see a change, you can set the layout to GridLayout(0, 1) and you will see the component.
this.setLayout(new GridLayout(0, 1));
If this is not the result you want, then you should look over Laying out Components within a Container to learn the different layouts available to you.
Also as I pointed out in my comment
if(e.getSource() == "Rock"){
with the above, you are trying to compare an object (ultimately a button) with a String. Instead you will want to compare the actionCommand
String command = e.getActionCommand();
if("Rock".equals(command)){
I have this program in which I'm using CardLayout. I have different panels with different attributes. I have a button called "Enter" that I decided to reuse on every panel, however each panel performs a different operation when the button is clicked. Is there a way to say, when button is clicked but I am at a specific panel, then do this. How can I point directly to a panel?
First thing you must consider is: You can't add one button to many panels, every panel should have it's own component(s).
If you add one button to many panels say :
JButton b = new JButton("Button");
//....
pan1.add(b);
pan2.add(b);
pan3.add(b);
In such case, the button will be added to the last panel means pan3, the other won't show the button.
Second, I would like to mention a #trashgod's good example from comments, and also in case confusing, look at this example:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class CardLayoutDemo extends JFrame implements ActionListener {
private CardLayout cardLayout;
private JButton pan1,pan2;
private JPanel mainPanel;
public CardLayoutDemo(){
cardLayout = new CardLayout();
mainPanel = new JPanel(cardLayout);
JPanel p1 = new JPanel();
JPanel p2 = new JPanel();
pan1 = new JButton("To Second Panel");
pan2= new JButton ("To First Panel");
pan1.addActionListener(this);
pan2.addActionListener(this);
p1.setBackground(Color.green);
p2.setBackground(Color.BLUE.brighter());
p1.add(pan1);
p2.add(pan2);
mainPanel.add(p1,"1");
mainPanel.add(p2,"2");
cardLayout.show(mainPanel, "1");
add(mainPanel);
setDefaultCloseOperation(3);
setLocationRelativeTo(null);
setVisible(true);
pack();
}
#Override
public void actionPerformed(ActionEvent ev){
if(ev.getSource()==pan1)
cardLayout.show(mainPanel, "2");
else if(ev.getSource()==pan2)
cardLayout.show(mainPanel, "1");
}
public static void main(String...args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new CardLayoutDemo().setVisible(true);
}
});
}
}
You can let the panel assign an ActionListener to the button each time the card is created. That way the constructor for a specific panel can determine what the functionality of the button will be.
I need to write a simple tennis game.
To move between different windows(panel with main menu, panel with game, panel with settings) I decided to use inner classes extends JPanel and replace it when some events like start new game occurs.
but the problem is - it doesn't see my inner class. I mean I add it to JFrame
mainframe.add(new MainMenuPanel());
but there is nothing on the screen when I run program. What's the problem?
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class MainFrame{
JFrame mainframe;
public static void main(String[] args){
new MainFrame();
}
public MainFrame() {
mainframe = new JFrame();
mainframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainframe.setSize(300, 400);
mainframe.setTitle("X-Tennis v0.1");
mainframe.add(new MainMenuPanel());
mainframe.getContentPane().setLayout(new GridLayout());
mainframe.getContentPane().setBackground(Color.WHITE);
mainframe.setVisible(true);
}
public class MainMenuPanel extends JPanel {
JPanel mainmenupanel;
JLabel label1;
JButton btnNewGame,btnJoinGame;
ImageIcon iconNewGame,iconJoinGame;
public MainMenuPanel(){
mainmenupanel = new JPanel();
label1 = new JLabel("X-TENNIS");
label1.setFont(new Font("Comic Sans MS",Font.ITALIC,20));
label1.setForeground(Color.BLUE);
btnNewGame = new JButton("New Game", iconNewGame);
btnNewGame.setFocusPainted(false);
btnNewGame.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e){
JOptionPane.showMessageDialog(mainframe, "New game");
//delete current panel and add another to mainframe
}
}
);
btnNewGame.setPreferredSize(new Dimension(140,30));
btnJoinGame = new JButton("Join game",iconJoinGame);
mainmenupanel.add(label1);
mainmenupanel.add(btnNewGame);
}
}
}
There is no need for mainmenupanel within the MainMenuPanel class as MainMenuPanel is a JPanel itself
Simple add all the components in MainMenuPanel directly to itself
You create a new JPanel, mainmenupanel, inside MainMenuPanel but never add that to the container itself. You could do
add(mainmenupanel);
If you intend for this JPanel to occupy the full area of the parent, then you can simply add your components directly to your instance of MainMenuPanel as indicated by #Mad
First you should add your component to the ContentPane. In Swing, all the non-menu components displayed by the JFrame should be in the ContentPane.
mainframe.getContentPane().add(new MainMenuPanel());
Edit: I was wrong about the content pane, see #MadProgrammer comment.
Then you have to add the JPanel that you create in MainMenuPanel to the MainMenuPanel instance itself.
add(mainmenupanel);
But you should probably get rid of that intermediary container itself and add your labels to the MainMenuPanel instance itself:
add(label1);
add(btnNewGame);
mainmenupanel.add(label1);
mainmenupanel.add(btnNewGame);
try this :
super.add(label1);
super.add(btnNewGame);
I have this piece of code and read that validate can refer to laying out a container's subcomponents. "Layout-related changes, such as setting the bounds of a component, or adding a component to the container, invalidate the container automatically." (source: javadoc).
However, I see no difference whatsoever between keeping validate() or removing it from this little piece of code.
Can you show me a convincing example where you can see distinct behaviour in two cases (with or without validate) to prove a point? Any other comments/advice appreciated.
public class Sw1
extends JApplet
{
JLabel lbl;
public void init()
{
lbl = new JLabel ("a label");
JPanel pan = (JPanel) getContentPane ();
pan.add(lbl);
validate();
}
}
Here is the program after I intended the push of a button to add a label. It renders an exception when I push the button:
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class Sw_test
extends JApplet
implements ActionListener
{
JLabel lbl;
JButton bt ;
JPanel pan ;
JLabel l;
public void init()
{
lbl = new JLabel ("label 1");
bt = new JButton ("go ahead, press me");
bt.addActionListener(this);
JPanel pan = (JPanel) getContentPane ();
pan.setLayout(new FlowLayout());
pan.add(lbl);
pan.add(bt);
validate();
}
public void actionPerformed(ActionEvent ev)
{
l = new JLabel("new label");
pan.add(l);
}
}
You would need to call it if you add a component to a panel after it has been initialized and made visible.
Try adding a button to your applet, and on the click of the button, add a new label to the applet.
i will quote the API:
The validate method is used to cause a container to lay out its subcomponents again. It should be invoked when this container's subcomponents are modified (added to or removed from the container, or layout-related information changed) after the container has been displayed.
so as you see, it is important if you modify your layout, AFTER it has been initialized.
That is the reason why you donĀ“t see any difference
btw: here is your example :
public class TestFrame extends JFrame{
private JButton b = new JButton();
public TestFrame() {
this.setLayout(new GridLayout(5,5));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(b);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
TestFrame.this.add(new JLabel("whatever"));
//try it with and without
//validate();
}
});
this.setSize(300, 300);
this.setVisible(true);
}
public static void main(String[] args) {
new TestFrame();
}
}