Layouts - Part 4 - Card Layout

The Card Layout is very useful if you have various Panels of widgets you need to display a different times, in or out of order. There are commands built into the CardLayout to facilitate the switching of the various panels. This Layout isn't so much a layout, as it is a tool. To position the widgets in the Panel, we still need a Layout that does this. This is an ideal time to show you that LayoutManagers are only for the JPanel for which they were created. This means you can nest JPanels to get different Layouts for different parts of a GUI.

For this example, we need to prepare a panel with some buttons to demonstrate the features of the CardLayout. The JPanels we will display in turn will demonstrate different Y-axis alignments for the BoxLayout.

This is a complicated example, but there is nothing in this code we have not covered before bar the CardLayout stuff. If you have problems with any of it, please look back at the previous tutorials. The code should be well commented enough for you to at least get a grasp of what is going on.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
import javax.swing.*;
import java.lang.*;
import java.awt.*;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class CardLayoutExample implements  ActionListener{
    
    JButton firstButton, lastButton, nextButton, previousButton;
    JPanel cardPanel;
    JLabel titleLabel, descLabel;
    String cardNames[] = new String[3];
    String cardDescription[] = new String[3];
    int cardCounter = 0;

    public JPanel createContentPane (){
        
        JPanel totalGUI = new JPanel();
        
        // We first create a JPanel to show the Buttons.
        // This is an ideal place to show how easy it is to set up a panel
        // of well spaced buttons using a BoxLayout.
        
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
        
        buttonPanel.add(Box.createRigidArea(new Dimension(10,0)));
        
        previousButton = new JButton("<- Previous");
        previousButton.addActionListener(this);
        buttonPanel.add(previousButton);
        buttonPanel.add(Box.createHorizontalGlue());        
                
        firstButton = new JButton("First");
        firstButton.addActionListener(this);
        buttonPanel.add(firstButton);
        buttonPanel.add(Box.createHorizontalGlue());
                
        lastButton =  new JButton("Last");
        lastButton.addActionListener(this);
        buttonPanel.add(lastButton);
        buttonPanel.add(Box.createHorizontalGlue());
        
        nextButton = new JButton("Next ->");
        nextButton.addActionListener(this);
        buttonPanel.add(nextButton);
        buttonPanel.add(Box.createRigidArea(new Dimension(10,0)));
        
        // Now we need to create three demonstration panels to show
        // the various ways we can align panels in a BoxLayout.
        // To make this a bit quicker, we've created a method called
        // createSquareJPanel that will generate the coloured panels 
        // without loads of lines of code.

        // Shows the default alignment.
        JPanel align_1 = new JPanel();
        align_1.setLayout(new BoxLayout(align_1, BoxLayout.LINE_AXIS));
        
        // Created a red panel and a blue panel using my custom method.
        JPanel redPanel_1 = createSquareJPanel(Color.red, 50);
        JPanel bluePanel_1 = createSquareJPanel(Color.blue, 80);
        // Add the two panels to the BoxLayout panel with a space inbetween.
        align_1.add(redPanel_1);
        align_1.add(Box.createRigidArea(new Dimension(5,5)));
        align_1.add(bluePanel_1);
        
        // Shows the bottom alignment.
        JPanel align_2 = new JPanel();
        align_2.setLayout(new BoxLayout(align_2, BoxLayout.LINE_AXIS));
        
        JPanel redPanel_2 = createSquareJPanel(Color.red, 50);
        redPanel_2.setAlignmentY(Component.BOTTOM_ALIGNMENT);
        JPanel bluePanel_2 = createSquareJPanel(Color.blue, 80);
        
        align_2.add(redPanel_2);
        align_2.add(Box.createRigidArea(new Dimension(5,5)));
        align_2.add(bluePanel_2);
        
        // Shows the top alignment.
        JPanel align_3 = new JPanel();
        align_3.setLayout(new BoxLayout(align_3, BoxLayout.LINE_AXIS));

        JPanel redPanel_3 = createSquareJPanel(Color.red, 50);
        redPanel_3.setAlignmentY(Component.TOP_ALIGNMENT);
        JPanel bluePanel_3 = createSquareJPanel(Color.blue, 80);
        
        align_3.add(redPanel_3);
        align_3.add(Box.createRigidArea(new Dimension(5,5)));
        align_3.add(bluePanel_3);

        // This is the important bit of this application.
        // We use a JPanel with a cardPanel, and add the three panels in order.
        // Note that we have to instantiate this Panel outside of this method
        // so the ActionListener can change it on a buttons command.
        
        cardPanel = new JPanel(new CardLayout(150, 50));
        
        // To add a panel to a cardPanel Panel, we also provide a string
        // to explain the panel added.
        // To make it easier for the label on the GUI, we're going to put
        // the names into an array.
        
        cardNames[0] = "Component.CENTER";
        cardNames[1] = "Component.BOTTOM_ALIGNMENT";
        cardNames[2] = "Component.TOP_ALIGNMENT";
        
        cardDescription[0] = " - The Center of both widgets is aligned";
        cardDescription[1] = " - The bottom of the first widget is aligned with the center of the second";
        cardDescription[2] = " - The top of the first widget is aligned with the center of the second";
        
        cardPanel.add(align_1, cardNames[0]);
        cardPanel.add(align_2, cardNames[1]);
        cardPanel.add(align_3, cardNames[2]);
        
        // Now we've completed all that, we add our toolbar to the content pane
        // along with this cardPanel panel.
        // We will use a BorderLayout this time to position the toolbar above 
        // the CardPanels and the titlebar below.
        
        JPanel bottomPanel = new JPanel();
        bottomPanel.setLayout(new BorderLayout());
               
        bottomPanel.add(buttonPanel, BorderLayout.PAGE_START);
        bottomPanel.add(cardPanel, BorderLayout.CENTER);
        
        // To add a little bit extra, we'll put a label at the bottom
        // telling you what alignment we have.
        
        JPanel titlePanel = new JPanel();
        titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.PAGE_AXIS));
        
        titleLabel = new JLabel(cardNames[0]);
        descLabel = new JLabel(cardDescription[0]);
        
        titlePanel.add(titleLabel);
        titlePanel.add(descLabel);
        
        bottomPanel.add(titlePanel, BorderLayout.PAGE_END);

        // Finally we add that JPanel to the content pane.
        totalGUI.add(bottomPanel);
        totalGUI.setOpaque(true);
        return totalGUI;
    }

    // In this method, we create a square JPanel of a colour and set size 
    // specified by the arguments.
    private JPanel createSquareJPanel(Color color, int size)
    {
        JPanel tempPanel = new JPanel();
        tempPanel.setBackground(color);
        tempPanel.setMinimumSize(new Dimension(size, size));
        tempPanel.setMaximumSize(new Dimension(size, size));
        tempPanel.setPreferredSize(new Dimension(size, size));
        return tempPanel;
    }
    
    // The action performed changes the JPanel on display 
    // and the title/description bar at the bottom.
    public void actionPerformed(ActionEvent e) {
        
        // We need to get the current layout of the CardLayout panel
        // before we can change it.
        CardLayout cl = (CardLayout)(cardPanel.getLayout());

        // This section of code finds out the button that has been pressed
        // and changes the card displayed on the cardLayout.
        if(e.getSource() == firstButton)
        {
            cl.first(cardPanel);
            cardCounter = 0;
        }
        else if(e.getSource() == lastButton)
        {
            cl.last(cardPanel);
            cardCounter = 2;
        }
        else if(e.getSource() == nextButton)
        {
            cl.next(cardPanel);
            if(cardCounter < 2)
            { 
                cardCounter++;
            }
            else 
            {
                cardCounter = 0;  
            } 
        }
        else if(e.getSource() == previousButton)
        {
            cl.previous(cardPanel);
            if(cardCounter > 0)
            {
                cardCounter--;
            }
            else
            {
                cardCounter = 2;
            }
        }

        titleLabel.setText(cardNames[cardCounter]);
        descLabel.setText(cardDescription[cardCounter]);
    }


    private static void createAndShowGUI() {
    
        JFrame.setDefaultLookAndFeelDecorated(true);
        JFrame frame = new JFrame("[=] CardLayout Demonstration [=]");

        CardLayoutExample demo = new CardLayoutExample();
        frame.setContentPane(demo.createContentPane());
        
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}
This example is based around three main areas, the toolbar of buttons at the top, the centre area that demonstrates the BoxLayout and the bottom part which explains what you are looking at. It looks like this when run, but you should run it yourself and have a go.

Picture of the CardLayout

The first Panel is the toolbar, and we use the BoxLayout in this to great effect. We simply create our buttons and use horizontal glue to automatically size the buttons depending on the size of the frame. To make sure the buttons are not right beside the frame in the event the button frame is the largest, we have two rigid areas either side to keep things nicely spaced.
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
        
        buttonPanel.add(Box.createRigidArea(new Dimension(10,0)));
        
        previousButton = new JButton("<- Previous");
        previousButton.addActionListener(this);
        buttonPanel.add(previousButton);
        buttonPanel.add(Box.createHorizontalGlue());        
                
        firstButton = new JButton("First");
        firstButton.addActionListener(this);
        buttonPanel.add(firstButton);
        buttonPanel.add(Box.createHorizontalGlue());
                
        lastButton =  new JButton("Last");
        lastButton.addActionListener(this);
        buttonPanel.add(lastButton);
        buttonPanel.add(Box.createHorizontalGlue());
        
        nextButton = new JButton("Next ->");
        nextButton.addActionListener(this);
        buttonPanel.add(nextButton);
        buttonPanel.add(Box.createRigidArea(new Dimension(10,0)));
The CardLayout needs 'cards' to swap between, so in the next segment of code we create three 'cards'. These are JPanels with two coloured boxes in them, aligned in different ways so as to show the alignments used in BoxLayout.
This is not very complicated, but it is worth pointing out the method used to create the coloured boxes. This is a great way of saving space if you are creating a lot of widgets of a similar type. You simply create the widget, then pass it back.
149
150
151
152
153
154
155
156
157
    private JPanel createSquareJPanel(Color color, int size)
    {
        JPanel tempPanel = new JPanel();
        tempPanel.setBackground(color);
        tempPanel.setMinimumSize(new Dimension(size, size));
        tempPanel.setMaximumSize(new Dimension(size, size));
        tempPanel.setPreferredSize(new Dimension(size, size));
        return tempPanel;
    }
You then simply call this method like any other, and pass the colour and size as arguments.
61
        JPanel redPanel_1 = createSquareJPanel(Color.red, 50);
Now we get into the panel with the CardLayout, what you are all here to see. We instantiate the CardLayout as an argument of the JPanel. This is just a quicker way of setting the layout on the JPanel, rather than using .setLayout(). Both work.
The CardLayout passes in two integers when instantiated. These are the padding between the cards and the CardLayout Panel. There is 150px of padding on the x-axis between the JPanel holding the coloured boxes and the end of the CardPanel on BOTH sides.
97
        cardPanel = new JPanel(new CardLayout(150, 50));
If you leave this blank the default is 0 and the card is placed without any padding. You should edit the code and try this to see it's effect on the layout.

We then create the title and description for each of the cards and put them in a String array for easy access.

When you add 'card' panels to a CardLayout, you need to provide a String as an argument. This is for when you use the .show() command, you pass the String as an argument to find the specific card. For clarity, we use the title we have created as the String argument. We have not demonstrated the .show() command in this tutorial, but it is covered in the JComboBox example.
112
113
114
        cardPanel.add(align_1, cardNames[0]);
        cardPanel.add(align_2, cardNames[1]);
        cardPanel.add(align_3, cardNames[2]);
We finish the GUI by creating a JPanel with the BorderLayout and adding the Toolbar, the CardLayout Panel and a Description Panel to it. We then add that Panel to the ContentPane.
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
        JPanel bottomPanel = new JPanel();
        bottomPanel.setLayout(new BorderLayout());
               
        bottomPanel.add(buttonPanel, BorderLayout.PAGE_START);
        bottomPanel.add(cardPanel, BorderLayout.CENTER);
        
        // To add a little bit extra, we'll put a label at the bottom
        // telling you what alignment we have.
        
        JPanel titlePanel = new JPanel();
        titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.PAGE_AXIS));
        
        titleLabel = new JLabel(cardNames[0]);
        descLabel = new JLabel(cardDescription[0]);
        
        titlePanel.add(titleLabel);
        titlePanel.add(descLabel);
        
        bottomPanel.add(titlePanel, BorderLayout.PAGE_END);

        // Finally we add that JPanel to the content pane.
        totalGUI.add(bottomPanel);
The last thing to look at in this example is the ActionListener. We have covered these before in the JButton tutorial.
Before we can change the CardLayout to something, we need to know it's previous state. This is done by using the .getLayout() command on the CardLayout panel. We cast the answer as a CardLayout by using the syntax (CardLayout).
165
        CardLayout cl = (CardLayout)(cardPanel.getLayout());
To give a command to the CardPanel to change, we use the CardLayout we have just found and use one of the change commands (like first(), last() etc). We also need to pass the panel it is changing as an argument.
171
            cl.first(cardPanel);
Finally, we use the int variable cardCounter to track the state of the CardLayout panel, and display the correct title and description for each.
204
205
        titleLabel.setText(cardNames[cardCounter]);
        descLabel.setText(cardDescription[cardCounter]);
Hopefully with this example, you can see how a CardLayout is used to switch between JPanels easily. This could be useful for a Step-Process in your GUI where perhaps you need to take someone's personal details THEN the bank details THEN the car-hire details instead of trying to cram it onto one page.
This example should also show you how easy it is to make a semi-difficult GUI using LayoutManagers. There was very little absolute positioning in that GUI, no maths needed to be done.

Exercises

Exercise #1 - Colour the background of a card to see it's size.
Exercise #2 - Add a fourth card, this time demonstrating Box 1 top aligned with Box 2, which is bottom aligned with Box 3.

Hint for Exercise #2

Exercise #1 Full Code
Exercise #2 Full Code

Questions

Questions on the card layout? Don't fold!

Question 1. What does CardLayout actually let you do?

  • a) Set JPanels as cards, and allows the user to display the cards one at a time.
  • b) Set JPanels as cards, and allows the user to display all of the JPanels at once.
  • c) Set JPanels as cards, and allows the user to display the cards upside-down.

Question 2. How do we add a 'Card' JPanel to a JPanel with the card layout?

  • a) .add(JPanel)
  • b) .add(JPanel, String defining JPanel's id)
  • c) .add(String defining JPanel's id, JPanel)

Question 3. What does the command .first() do to a CardLayout?

  • a) Displays the first alphabetically-sorted card.
  • b) Displays the first card added.
  • c) Displays the first card, smallest to largest in size.




Back Top Next
Email Me
Code Style


Required Lessons

External Links

Created and Edited by Stuart Davidson
All Rights Reserved ©

Valid XHTML 1.0 Strict