3

I have a CardLayout in which I add cards only as needed. So, when the need arises to show a particular card (identifed by its name), I need a way to check if a card with that name is already present, so that I can either show or create it accordingly.

According to the CardLayout documentation

Flips to the component that was added to this layout with the specified name, using addLayoutComponent. If no such component exists, then nothing happens.

So, no error will be thrown if I ask it to show a card that hasn't been added yet. I couldn't find any API that would let me check if a card is present.

So, is this possible to do? If not how would one go about addressing this? There is the solution that I manually remember what cards I have added but it feels swing should be able to handle this.

sharat87
  • 7,330
  • 12
  • 55
  • 80
  • I think that by Name(s) is there method show() http://download.oracle.com/javase/7/docs/api/java/awt/CardLayout.html#show%28java.awt.Container,%20java.lang.String%29 from http://download.oracle.com/javase/tutorial/uiswing/layout/card.html – mKorbel May 18 '11 at 07:06
  • @mKorbel, I'm not sure I get you. The `show()` method takes in the name and shows the card associated with that name. My question is how would one check if a card associated with a particular name exists. – sharat87 May 18 '11 at 07:14
  • @ Shrikant Sharat please and check 'Related' topic on right side under 'CAREERS2.2' here I found f.e. http://stackoverflow.com/questions/2998538/switching-between-cards-in-a-cardlayout-using-getparent – mKorbel May 18 '11 at 07:40

2 Answers2

5

CardLayout API provides no way to check if a component has already been added with a given name.

If you really want to do that (but I would strongly advise AGAINST doing that), then you could use reflection on the CardLayout used by the container, and read its vector field, then check each entry (of type CardLayout$Card) for the given name. As you see, that looks like a hack and it could break if CardLayout was refactored some day (current implementation is quite ugly).

The best way would be for you to directly keep track of the names of all added children in a Set<String> field somewhere. And this is really not a big deal to do that anyway.

jfpoilpret
  • 10,449
  • 2
  • 28
  • 32
  • Oh well, I definitely don't want to go with the reflection way of things, for the reasons you suggest. Guess I'll go with keeping track myself. Thanks for the reflection idea btw :) – sharat87 May 18 '11 at 08:38
  • 1
    You're welcome. That was fun to take a look at the "guts" of CardLayout ;-) – jfpoilpret May 18 '11 at 09:05
4

So, when the need arises to show a particular card (identifed by its name), I need a way to check if a card with that name is already present, so that I can either show or create it accordingly.

  1. Get the current component that is displayed in the container
  2. Attempt to show a different card
  3. Get the component now displayed in the container
  4. If the two components are the same, nothing happened and you need to create the card and add it to the container.

This approach will save you managing the Set of cards yourself.

Edit:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class CardLayoutTest implements ActionListener
{
    JPanel cards;

    public void addComponentToPane(Container pane) {
        JPanel comboBoxPane = new JPanel();
        String comboBoxItems[] = { "Red", "Orange", "Green", "Yellow", "Blue"};
        JComboBox cb = new JComboBox(comboBoxItems);
        cb.setEditable(false);
        cb.addActionListener(this);
        comboBoxPane.add(cb);

        cards = new JPanel(new CardLayout());

        pane.add(comboBoxPane, BorderLayout.PAGE_START);
        pane.add(cards, BorderLayout.CENTER);

        JPanel red = new JPanel();
        red.setBackground(Color.RED);
        red.setPreferredSize( new Dimension(200, 50) );
        cards.add(red, "Red");

        JPanel green = new JPanel();
        green.setBackground(Color.GREEN);
        green.setPreferredSize( new Dimension(200, 50) );
        cards.add(green, "Green");

        JPanel blue = new JPanel();
        blue.setBackground(Color.BLUE);
        blue.setPreferredSize( new Dimension(200, 50) );
        cards.add(blue, "Blue");
    }

    public void actionPerformed(ActionEvent e)
    {
        Component visible = getVisibleCard();

        JComboBox comboBox = (JComboBox)e.getSource();
        String item = comboBox.getSelectedItem().toString();
        CardLayout cl = (CardLayout)(cards.getLayout());
        cl.show(cards, item);

        //  change code below to create and show your card.

        if (visible == getVisibleCard())
            JOptionPane.showMessageDialog(cards, "Card (" + item + ") not found");

    }

    private Component getVisibleCard()
    {
        for(Component c: cards.getComponents())
        {
            if (c.isVisible())
                return c;
        }

        return null;
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("CardLayoutTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        CardLayoutTest demo = new CardLayoutTest();
        demo.addComponentToPane(frame.getContentPane());
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args)
    {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}
camickr
  • 321,443
  • 19
  • 166
  • 288
  • Nice idea. However, I don't find any API to get the displayed component from the `CardLayout` instance.. – sharat87 May 19 '11 at 01:34
  • You don't get it from the CardLayout, you just get the component directly from the panel using panel.getComponent(0). – camickr May 19 '11 at 02:10
  • ah, I see. I will try this out (I've already implemented the other solution though :). Thanks for the idea – sharat87 May 19 '11 at 04:36
  • Actually, forget this idea. The panel will contain all the panels so the first panel will not always be the one that is visible. You would need to loop though all the components added to the panel to find the component that is visible. The way the CardLayout works it sets all the components visibility to false except for the one component that you want to show. – camickr May 19 '11 at 04:50
  • haha ok. Thanks for digging into this, really appreciate it :) – sharat87 May 19 '11 at 04:55
  • Since I led you on a wild goose chase, see the edit for an example of what your would have to do. – camickr May 19 '11 at 05:13
  • wow! Dude! Thanks for effort! The `getVisibleCard` method shows part of the extra trouble I guess :) – sharat87 May 19 '11 at 05:20