Ocracoke
Ocracoke

Reputation: 1768

Showing a JTable in a JTree behaves differently with different look and feels

I have a JTree where some of the data to be shown should be shown in a JTable. So to do this, I created a renderer class (extending DefaultTreeCellRenderder) to change the rendered component returned depending on what the object value contains when initially passed into the renderer.

If it is a table to be shown, it is contained within a JScrollPane (as the below code shows).

This works (in that the JScrollPane containing the JTable is shown in its entirety) with the Cross Platform Look and Feel but when using the System Look and Feel, the JScrollPane seems to be constrained to the height of the leaf node. Is there a way of changing this so it acts like the Cross Platform look and feel?

JFrame

public class ExampleJTreeWithJTable extends JFrame {

    public ExampleJTreeWithJTable() {
        setLayout(new BorderLayout(0, 0));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        DefaultMutableTreeNode root = new DefaultMutableTreeNode("Example");
        root.add(new DefaultMutableTreeNode("TABLE:Column A=Cell 1.1,Cell 2.1,Cell 3.1¬Column B=Cell 1.2,Cell 2.2,Cell 3.2"));

        JTree tree = new JTree(root);
        tree.setCellRenderer(new ExampleRenderer());

        add(tree, BorderLayout.CENTER);
        pack();
    }

    public static void main(String... args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); // Using getCrossPlatformLookAndFeelClassName works...

            EventQueue.invokeLater(new Runnable() {

                @Override
                public void run() {
                    ExampleJTreeWithJTable window = new ExampleJTreeWithJTable();
                    window.setLocationRelativeTo(null);
                    window.setVisible(true);
                }
            });
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
            e.printStackTrace();
        }
    }
}

Renderer

public class ExampleRenderer extends DefaultTreeCellRenderer {
    private Map<String, JScrollPane> tables = new HashMap<String, JScrollPane>();

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
        Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);

        if (value.toString().startsWith("TABLE:")) {
            c = tables.get(value.toString());

            if (c == null) {
                JTable table = new JTable(createModel(value.toString()));
                table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

                JScrollPane pane = new JScrollPane(table);

                tables.put(value.toString(), pane);
                c = tables.get(value.toString());
            }
        }

        return c;
    }

    private TableModel createModel(String tableData) {
        tableData = tableData.substring(6, tableData.length());

        DefaultTableModel model = new DefaultTableModel();
        String[] colData = tableData.split("¬");

        for (String data : colData) {
            String[] components = data.split("=");
            model.addColumn(components[0], components[1].split(","));
        }

        return model;
    }
}

Examples of output

Cross Platform Look and Feel

Code above using CrossPlatform LAF

System Look and Feel

Code above using System LAF

I've tried manually setting the JScrollPane to the preferred size of the JTable with no effect. I have a feeling it is to do with how the System LAF calculates the height of the component against the leaf node but I am not certain here.

I have also tried simply showing the JTable rather than the JScrollPane. In the Cross Platform LAF, this works. In the System LAF, it shows the first row of the table.

Upvotes: 0

Views: 384

Answers (1)

Ocracoke
Ocracoke

Reputation: 1768

I figured this out from the below link:

https://www.daniweb.com/software-development/java/threads/386037/different-height-of-jtree-nodes

In essence, all that is needed to do is set the row height to 0 to make the leaf node height variable, depending on the component being rendered as opposed to a fixed (I presume) default value.

Therefore, the code would look like this:

JTree tree = new JTree(root);
tree.setRowHeight(0);
tree.setCellRenderer(new ExampleRenderer());

The result is this:

Effect of code change above

Upvotes: 1

Related Questions