TreeTable
Hier ist eine einfache Implementation einer TreeTable (gem. Alexandru Toth), die ein gemeinsames Model für JTree und JTable benutzt. Es ist dann kein Problem mehr, dynamisch neue Zeilen hinzuzufügen:
<code=java>package treetable;
import java.awt.Color; import javax.swing.*; import javax.swing.table.*;
/** JTable that has different renderers depending on the classes in TreeModel */ public class Table extends JTable {
void registerRenderers() { setDefaultRenderer(Color.class, new TableColorRenderer(Color.WHITE)); }
public Table() { super();
registerRenderers(); }
public Table(TableModel dm) { super(dm);
registerRenderers(); }
public Table(TableModel dm, TableColumnModel cm) { super(dm, cm);
registerRenderers(); }
@Override public TableCellRenderer getCellRenderer(int row, int column) { Object value = getModel().getValueAt(row, column);
/** enable to have custom renderers depending on class of cell */ TableCellRenderer tcr = getDefaultRenderer(value.getClass());
return tcr; }
}</code=java>
<code=java>package treetable;
import javax.swing.*; import javax.swing.event.*; import javax.swing.tree.*; import javax.swing.table.*; import java.util.Vector;
public class TableAdapter extends AbstractTableModel {
private JTree tree; private JTable table; private int[] sel_rows; private ListSelectionListener selectionListener;
public TableAdapter(JTree t) { tree = t;
tree.addTreeExpansionListener(new TreeExpansionListener() {
public void treeExpanded(TreeExpansionEvent event) { fireTableDataChanged(); select(); }
public void treeCollapsed(TreeExpansionEvent event) { fireTableDataChanged(); select(); } }); }
public void addTable(JTable t) { table = t;
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) { sel_rows = tree.getSelectionRows(); select(); } }); selectionListener = new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) { int row = table.getSelectedRow(); tree.setSelectionRow(row); } }; table.getSelectionModel().addListSelectionListener(selectionListener);
}
private void select() { if (sel_rows != null) { table.getSelectionModel().setSelectionInterval(sel_rows[0], sel_rows[0]); } }
//assumes all nodes have same number of items as root node public int getColumnCount() { DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot(); TableNode n = (TableNode) root.getUserObject(); return n.getDataRow().size(); }
public int getRowCount() { return tree.getRowCount(); }
@Override public String getColumnName(int col) { return new String("col" + col); }
public Object getValueAt(int row, int col) { TreePath tPath = tree.getPathForRow(row); Object[] oPath = tPath.getPath(); int len = oPath.length; DefaultMutableTreeNode node = (DefaultMutableTreeNode) oPath[len - 1]; TableNode treeNode = (TableNode) node.getUserObject(); Vector dataRow = treeNode.getDataRow();
return dataRow.elementAt(col); }
@Override public void setValueAt(Object value, int row, int col) { TreePath tPath = tree.getPathForRow(row); Object[] oPath = tPath.getPath(); int len = oPath.length; DefaultMutableTreeNode node = (DefaultMutableTreeNode) oPath[len - 1]; TableNode treeNode = (TableNode) node.getUserObject(); Vector dataRow = treeNode.getDataRow(); dataRow.setElementAt(value, col); }
@Override public Class getColumnClass(int c) { DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot(); TableNode n = (TableNode) root.getUserObject(); return n.getDataRow().elementAt(c).getClass(); }
@Override public boolean isCellEditable(int rowIndex, int columnIndex) { return true; }
}</code=java>
<code=java>package treetable;
import java.awt.*; import javax.swing.*; import javax.swing.table.*; import javax.swing.border.*;
public class TableColorRenderer extends JLabel implements TableCellRenderer {
Border unselectedBorder = null; Border selectedBorder = null; boolean isBordered = true;
public TableColorRenderer(Color color) { super(); setOpaque(true); setBackground(color); }
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (isSelected) { if (selectedBorder == null) { selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getSelectionBackground()); } setBorder(selectedBorder); } else { if (unselectedBorder == null) { unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getBackground()); } setBorder(unselectedBorder); } if (value.getClass() == Color.class) { setBackground((Color) value); } else { setBackground(Color.WHITE); } return this; }
}</code=java>
<code=java>package treetable;
import java.util.*;
public class TableNode {
private String label; private Vector dataRow;
public TableNode(String s, Object x, Object y) { label = s; dataRow = new Vector(); dataRow.add(x); dataRow.add(y); }
@Override public String toString() { return label; }
public Vector getDataRow() { return dataRow; }
}</code=java>
<code=java>package treetable;
import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.tree.*; import javax.swing.table.*;
public class TreeTable extends JFrame {
private JTableHeader tableHeader; private JTree tree; private TableAdapter adapter; private Table table; private JButton btAdd; private DefaultMutableTreeNode root; private JSplitPane splitPane; private Color selectionColor; private Rectangle selectedRowBounds; private boolean expand;//allows the MouseListener to detect "handles" action (->no row selection)
public TreeTable() { super("Tree Table Demo"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tree = new JTree(createTree()) {
//Override paintComponent of JTree: @Override public void paintComponent(final Graphics g) { if (selectedRowBounds != null) { setOpaque(false); //Set selection Color: g.setColor(selectionColor); //Draw selection rectangle using the width of JTree: g.fillRect(0, (int) selectedRowBounds.getY(), getWidth(), (int) selectedRowBounds.getHeight()); } else { setOpaque(false); } super.paintComponent(g); } }; adapter = new TableAdapter(tree); table = new Table(adapter); table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); //connect Selection listeners adapter.addTable(table); //same height !! table.setRowHeight(18); tree.setRowHeight(table.getRowHeight()); customizeTree(); // splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, tree, table); splitPane.setDividerLocation(150); splitPane.setBackground(Color.WHITE);
JScrollPane scrollPane = new JScrollPane(splitPane); getContentPane().add(scrollPane, BorderLayout.CENTER); JPanel header = new JPanel(null); tableHeader = table.getTableHeader(); header.add(tableHeader); header.setPreferredSize(new Dimension(0, 16)); getContentPane().add(header, BorderLayout.NORTH);
setSize(400, 300); setLocationRelativeTo(null);
table.addComponentListener(new ComponentAdapter() {
@Override public void componentResized(ComponentEvent e) { Point loc = table.getLocationOnScreen(); tableHeader.setBounds(new Rectangle(loc.x - getLocation().x - 3, 0, 3000, 18)); } }); btAdd = new JButton(); add(btAdd, BorderLayout.SOUTH); btAdd.setAction(new AbstractAction("Add") {
public void actionPerformed(ActionEvent e) { DefaultMutableTreeNode n = new DefaultMutableTreeNode(new TableNode("NEW", new Integer(5), Color.GREEN)); DefaultMutableTreeNode parent = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); if (parent != null) { parent.add(n); ((DefaultTreeModel) tree.getModel()).nodeStructureChanged(parent); table.repaint(); validate(); } } }); }
private DefaultMutableTreeNode createTree() { // create root node and add more nodes root = new DefaultMutableTreeNode(new TableNode("World", new Integer(10), Color.YELLOW));
DefaultMutableTreeNode n = new DefaultMutableTreeNode(new TableNode("Europe", new Integer(5), Color.GREEN)); root.add(n);
DefaultMutableTreeNode n1 = new DefaultMutableTreeNode(new TableNode("Finland", new Integer(1), Color.ORANGE)); n.add(n1);
n1 = new DefaultMutableTreeNode(new TableNode("Romania", new Integer(1), Color.RED)); n.add(n1);
n1 = new DefaultMutableTreeNode(new TableNode("Germany", new Integer(3), Color.BLUE)); n.add(n1);
n = new DefaultMutableTreeNode(new TableNode("America", new Integer(4), Color.WHITE)); root.add(n);
n = new DefaultMutableTreeNode(new TableNode("Asia", new Integer(1), Color.MAGENTA)); root.add(n);
return root; }
private void customizeTree() { tree.setFocusable(false); selectionColor = table.getSelectionBackground(); tree.putClientProperty("JTree.lineStyle", "Horizontal"); tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setOpaque(false); //Adapt the default selection colors: DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) tree.getCellRenderer(); renderer.setBorderSelectionColor(Color.WHITE); renderer.setBackgroundSelectionColor(new Color(0, 0, 0, 0)); //Add the TreeSelectionListener: TreeSelectionModel selectionModel = tree.getSelectionModel(); selectionModel.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(final TreeSelectionEvent e) { paintSelectionRect(); } }); //Add the TreeExpansionListener: tree.addTreeExpansionListener(new TreeExpansionListener() {
public void treeCollapsed(final TreeExpansionEvent event) { paintSelectionRect(); expand = true; }
public void treeExpanded(final TreeExpansionEvent event) { paintSelectionRect(); expand = true; } }); //Add MouseListener if you want to listen to whole line width: tree.addMouseListener(new MouseAdapter() {
@Override public void mousePressed(final MouseEvent e) { if (!expand) { int row = tree.getClosestRowForLocation(e.getX(), e.getY()); if (e.getClickCount() == 2) { if (tree.isCollapsed(row)) { tree.expandRow(row); } else { tree.collapseRow(row); } } else { tree.setSelectionRow(row); } } expand = false; } });
} //This method is called in valueChanged, treeCollapsed and treeExpanded //to paint the selection rectangle:
private void paintSelectionRect() { //Get selected row bounds: int[] rows = tree.getSelectionRows(); if (rows == null) { selectedRowBounds = null; return; } selectedRowBounds = tree.getRowBounds(rows[0]); //Repaint the JTree: tree.repaint(); }
public static void main(final String[] args) { Runnable gui = new Runnable() {
@Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception ex) { ex.printStackTrace(); } new TreeTable().setVisible(true); } }; //GUI must start on EventDispatchThread: SwingUtilities.invokeLater(gui); }
}</code=java>