Search code examples
javaswinglistenerjtreecardlayout

How to dynamically change a JPanel with a CardLayout using a JTree Listener?


I am trying to change the Panel with a CardLayout using the contents of a JTree. It is only working on the last selection. What listeners or changes should I do with my code?

I am also writing the text in the console and it seems to be getting the correct value. That is why it is frustrating me.

My code should display 1st level when 1st level nodes are clicked and 2nd level when 2nd level nodes are clicked. I used

DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
                                .getLastSelectedPathComponent();

and it is only affecting the last components. How do I fix this? Thanks!

Source code:

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

public class ProblemTree2 extends JFrame {

    private DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
    private DefaultTreeModel model = new DefaultTreeModel(root);
    private JTree tree = new JTree(model);
    private JPanel card;

    public ProblemTree2() {

        card = new JPanel(new CardLayout());
        card.setBorder(BorderFactory.createLineBorder(Color.BLACK));

        JLabel label1 = new JLabel("1st level");
        JLabel label2 = new JLabel("2nd level");

        DefaultMutableTreeNode n1 = new DefaultMutableTreeNode(
                "1st level: Child 1");
        n1.add(new DefaultMutableTreeNode("2nd level: Child l"));
        DefaultMutableTreeNode n2 = new DefaultMutableTreeNode(
                "1st level: Child 2");
        n2.add(new DefaultMutableTreeNode("2nd level: Child 2"));
        DefaultMutableTreeNode n3 = new DefaultMutableTreeNode(
                "1st level: Child 3");
        n3.add(new DefaultMutableTreeNode("2nd level: Child 3"));


        card.add(label1,"1st level: Child 1");
        card.add(label1,"1st level: Child 2");
        card.add(label1,"1st level: Child 3");

        card.add(label2,"2nd level: Child l");
        card.add(label2,"2nd level: Child 2");
        card.add(label2,"2nd level: Child 3");

        root.add(n1);
        root.add(n2);
        root.add(n3);

        tree.setEditable(true);
        tree.setSelectionRow(0);
        tree.setRootVisible(true);
        tree.setShowsRootHandles(true);


        tree.getSelectionModel().addTreeSelectionListener(
                new TreeSelectionListener() {
                    @Override
                    public void valueChanged(TreeSelectionEvent e) {

                        final CardLayout cards = (CardLayout) card
                                .getLayout();
                        DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
                                .getLastSelectedPathComponent();
                        System.out.println(selectedNode.toString());
                        cards.show(card,selectedNode.toString());
                    }
                });


        JScrollPane scrollPane = new JScrollPane(tree);
        scrollPane.setPreferredSize(new Dimension(500,500));
        getContentPane().add(scrollPane, BorderLayout.WEST);
        getContentPane().add(card, BorderLayout.CENTER);

        setSize(1000, 600);
        setVisible(true);
    }

    public static void main(String[] arg) {
        ProblemTree2 pt = new ProblemTree2();
    }
}

Solution

  • This...

    card.add(label1, "1st level: Child 1");
    card.add(label1, "1st level: Child 2");
    card.add(label1, "1st level: Child 3");
    
    card.add(label2, "2nd level: Child l");
    card.add(label2, "2nd level: Child 2");
    card.add(label2, "2nd level: Child 3");
    

    is causing your problems, you can't assign the same instance of a component multiple keys.

    When you try and add the component a second time, it is first removed from it's parent container, which is causing the CardLayout to remove the "name" as well, meaning only the lines...

    card.add(label1, "1st level: Child 3");
    // and...
    card.add(label2, "2nd level: Child 3");
    

    are actually working (been added to the UI/CardLayout)

    You will have to provide a new instance of the component for each name.

    Alternatively, you could provide more details to your TreeNode that would allow you to ascertain which level it is and show the correct component for that level, meaning you would only have two components in your CardLayout, one for each level.

    The DefaultMutableTreeNode allows you to supply a "user" Object to it. By default, it uses the toString method of this object for the text for the node, but you can use a TreeCellRenderer to customise this or supply a value for the toString method of your "custom level" object