TreeTable: Unterschied zwischen den Versionen

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen
Zeile 444: Zeile 444:
 
                     System.out.println(loc.x + " " + getLocation().x);
 
                     System.out.println(loc.x + " " + getLocation().x);
 
                     tableHeader.setBounds(new Rectangle(loc.x - TreeTable.this.getLocation().x - 3, 0, 1100, 18));
 
                     tableHeader.setBounds(new Rectangle(loc.x - TreeTable.this.getLocation().x - 3, 0, 1100, 18));
 +
                    tableHeader.setReorderingAllowed(false);
 
                 }
 
                 }
 
             });
 
             });
 
 
         }
 
         }
  

Version vom 27. März 2009, 20:33 Uhr

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 java.awt.Dimension; import java.awt.Rectangle; import java.util.Enumeration; import javax.swing.*; import javax.swing.table.*; import javax.swing.tree.DefaultMutableTreeNode;

/** 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));
       setUI(new UIMultiSpan());
   }
   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;
   }
   @Override
   public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
       Rectangle sRect = super.getCellRect(row, column, includeSpacing);
       if ((row < 0) || (column < 0) ||
               (getRowCount() <= row) || (getColumnCount() <= column)) {
           return sRect;
       }
       int index = 0;
       int columnMargin = getColumnModel().getColumnMargin();
       Rectangle cellFrame = new Rectangle();
       if (column > 0 && shouldCombineCells(row)) {
           return cellFrame;
       }
       int aCellHeight = rowHeight;
       cellFrame.y = row * aCellHeight;
       cellFrame.height = aCellHeight;
       Enumeration enumeration = getColumnModel().getColumns();
       while (enumeration.hasMoreElements()) {
           TableColumn aColumn = (TableColumn) enumeration.nextElement();
           cellFrame.width = aColumn.getWidth() + columnMargin;
           if (index == column) {
               break;
           }
           cellFrame.x += cellFrame.width;
           index++;
       }
       while (enumeration.hasMoreElements() && shouldCombineCells(row)) {
           TableColumn aColumn = (TableColumn) enumeration.nextElement();
           cellFrame.width += aColumn.getWidth() + columnMargin;
       }
       if (!includeSpacing) {
           Dimension spacing = getIntercellSpacing();
           cellFrame.setBounds(cellFrame.x + spacing.width / 2,
                   cellFrame.y + spacing.height / 2,
                   cellFrame.width - spacing.width,
                   cellFrame.height - spacing.height);
       }
       return cellFrame;
   }
   private boolean shouldCombineCells(int row) {
       TableAdapter model = (TableAdapter) getModel();
       JTree tree = model.getTree();
       DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getPathForRow(row).getLastPathComponent();
       return node.getLevel() < 2;
   }

}</code=java>


<code=java>package treetable; /* JTree already has a good data model (DefaultTreeModel), and it's easier to implement an adapter that simply puts a TableModel on top of the TreeModel, rather than to implement JTree renderers in a JTable and work around the API to achieve correct displays.

The class "TableAdapter" puts an AbstractTableModel on top of DefaultTreeModel which is suggested by Swing for a JTree. The TableModel consists of the dataRow vector from "TableNode". The TableAdapter is also responsible for firing updates on the table data in case the JTree is collapsed or expanded.

  • /

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.repaint();
           }
       };
       table.getSelectionModel().addListSelectionListener(selectionListener);
   }
   public JTree getTree() {
       return tree;
   }
   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 false;
   }

}</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; /* TreeModels contain DefaultMutableTreeNodes. Each DefaultMutableTreeNode contains a UserObject for which we can easily create a class "TableNode" having a String to be displayed in the JTree, and a vector of objects to be displayed in the JTable.

  • /

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 DefaultMutableTreeNode root;
   TreeTablePanel[] treeTablePanels;
   public TreeTable() {
       super("Tree Table Demo");
       setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       DefaultTreeModel model = new DefaultTreeModel(createTree());
       setLayout(new GridLayout(0, 1));
       treeTablePanels = new TreeTablePanel[1];
       for (int i = 0; i < treeTablePanels.length; i++) {
           treeTablePanels[i] = new TreeTablePanel(model, treeTablePanels);
           add(treeTablePanels[i]);
       }
       setSize(1000, 300);
       setLocationRelativeTo(null);
   }
   private DefaultMutableTreeNode createTree() {
       // create root node and add more nodes
       root = new DefaultMutableTreeNode(new TableNode("Welt", "", Color.YELLOW));
       DefaultMutableTreeNode n = new DefaultMutableTreeNode(new TableNode("Europa", "Europa (griechisch Εὐρώπη, Eurṓpē) ist das westliche Fünftel der eurasischen Landmasse. ", Color.GREEN));
       root.add(n);
       DefaultMutableTreeNode n1 = new DefaultMutableTreeNode(new TableNode("Finnland", "5,3 Millionen Einwohner", Color.ORANGE));
       n.add(n1);
       n1 = new DefaultMutableTreeNode(new TableNode("Rumänien", "21,5 Millionen Einwohner", Color.RED));
       n.add(n1);
       n1 = new DefaultMutableTreeNode(new TableNode("Deutschland", "82 Millionen Einwohner", Color.BLUE));
       n.add(n1);
       n = new DefaultMutableTreeNode(new TableNode("Amerika", "Der amerikanische Doppelkontinent erstreckt sich in seiner Nord-Südachse vom 83. Breitengrad Nord (Kap Columbia) bis zum 56. Breitengrad Süd (Kap Hoorn).", Color.WHITE));
       root.add(n);
       n = new DefaultMutableTreeNode(new TableNode("Asien", "Asien ist mit rund 44,5 Mio. km² der größte Kontinent und Teil von Eurasien.", Color.MAGENTA));
       root.add(n);
       return root;
   }
   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);
   }
   class TreeTablePanel extends JPanel implements TableModelListener {
       private JTree tree;
       private Rectangle selectedRowBounds;
       private Color selectionColor;
       private TableAdapter adapter;
       private Table table;
       private JSplitPane splitPane;
       private JTableHeader tableHeader;
       private JButton btAdd;
       private TreeTablePanel[] treeTablePanels;
       public TreeTablePanel(DefaultTreeModel model, TreeTablePanel[] treeTablePanels) {
           super(new BorderLayout());
           this.treeTablePanels = treeTablePanels;
           tree = new JTree(model) {
               //Override paintComponent of JTree:
               @Override
               public void paintComponent(final Graphics g) {
                   if (selectedRowBounds != null) {
                       //Set selection Color:
                       g.setColor(selectionColor);
                       //Draw selection rectangle using the width of JTree:
                       g.fillRect(0, (int) selectedRowBounds.getY(), getWidth(), (int) selectedRowBounds.getHeight());
                   }
                   super.paintComponent(g);
               }
           };
           adapter = new TableAdapter(tree);
           table = new Table(adapter);
           customizeTable(table);
           customizeTree(tree, table);
           customizeSplitpane(tree, table);
           adapter.addTableModelListener(this);
           //
           JScrollPane scrollPane = new JScrollPane(splitPane);
           add(scrollPane, BorderLayout.CENTER);
           JPanel header = new JPanel(null);
           tableHeader = table.getTableHeader();
           header.add(tableHeader);
           header.setPreferredSize(new Dimension(0, 16));
           add(header, BorderLayout.NORTH);
           btAdd = new JButton();
           add(btAdd, BorderLayout.SOUTH);
           btAdd.setAction(new AbstractAction("Add") {
               public void actionPerformed(ActionEvent e) {
                   DefaultMutableTreeNode n = new DefaultMutableTreeNode(new TableNode("NEW", "", Color.GREEN));
                   DefaultMutableTreeNode parent = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
                   if (parent != null) {
                       parent.add(n);
                       ((DefaultTreeModel) tree.getModel()).nodeStructureChanged(parent);
                       table.repaint();
                       validate();
                   }
               }
           });
       }
       private void customizeTable(final JTable table) {
           table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
           //connect Selection listeners
           adapter.addTable(table);
           table.setRowHeight(18);
           table.addComponentListener(new ComponentAdapter() {
               @Override
               public void componentResized(ComponentEvent e) {
                   Point loc = table.getLocationOnScreen();
                   System.out.println(loc.x + " " + getLocation().x);
                   tableHeader.setBounds(new Rectangle(loc.x - TreeTable.this.getLocation().x - 3, 0, 1100, 18));
                   tableHeader.setReorderingAllowed(false);
               }
           });
       }
       private void customizeTree(final JTree tree, final JTable table) {
           tree.setRowHeight(table.getRowHeight());
           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();
               }
               public void treeExpanded(final TreeExpansionEvent event) {
                   paintSelectionRect();
               }
           });
           //Add MouseListener if you want to listen to whole line width:
           tree.addMouseListener(new MouseAdapter() {
               @Override
               public void mousePressed(final MouseEvent e) {
                   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);
                   }
               }
           });
       }
       private void customizeSplitpane(final JTree tree, final JTable table) {
           splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, tree, table);
           splitPane.setDividerLocation(150);
           splitPane.setBackground(Color.WHITE);
           splitPane.setContinuousLayout(true);
       }
       //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 void tableChanged(TableModelEvent e) {
           for (TreeTablePanel treeTablePanel : treeTablePanels) {
               treeTablePanel.getTable().repaint();
           }
       }
       public Table getTable() {
           return table;
       }
   }

}</code=java>


<code=java>package treetable; /*

* UIMultiSpan.java
* 
*/

import java.awt.*; import javax.swing.*; import javax.swing.table.*; import javax.swing.plaf.basic.*;

public class UIMultiSpan extends BasicTableUI {

   @Override
   public void paint(Graphics g, JComponent c) {
       Rectangle oldClipBounds = g.getClipBounds();
       Rectangle clipBounds = new Rectangle(oldClipBounds);
       int tableWidth = table.getColumnModel().getTotalColumnWidth();
       clipBounds.width = Math.min(clipBounds.width, tableWidth);
       g.setClip(clipBounds);
       int firstIndex = table.rowAtPoint(new Point(0, clipBounds.y));
       int lastIndex = table.getRowCount() - 1;
       Rectangle rowRect = new Rectangle(0, 0,
               tableWidth, table.getRowHeight() + table.getRowMargin());
       rowRect.y = firstIndex * rowRect.height;
       for (int index = firstIndex; index <= lastIndex; index++) {
           if (rowRect.intersects(clipBounds)) {
               //System.out.println();                  // debug
               //System.out.print("" + index +": ");    // row
               paintRow(g, index);
           }
           rowRect.y += rowRect.height;
       }
       g.setClip(oldClipBounds);
   }
   private void paintRow(Graphics g, int row) {
       Rectangle rect = g.getClipBounds();
       boolean drawn = false;
       int numColumns = table.getColumnCount();
       for (int column = 0; column < numColumns; column++) {
           Rectangle cellRect = table.getCellRect(row, column, true);
           int cellRow, cellColumn;
           cellRow = row;
           cellColumn = column;
           if (cellRect.intersects(rect)) {
               drawn = true;
               paintCell(g, cellRect, cellRow, cellColumn);
           } else {
               if (drawn) {
                   break;
               }
           }
       }
   }
   private void paintCell(Graphics g, Rectangle cellRect, int row, int column) {
       TableCellRenderer renderer = table.getCellRenderer(row, column);
       if (renderer == null) {
           return;
       }
       Component component = table.prepareRenderer(renderer, row, column);
       if (component.getParent() == null) {
           rendererPane.add(component);
       }
       rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y,
               cellRect.width - 1, cellRect.height - 1, true);
       Color c = g.getColor();
       g.setColor(table.getGridColor());
       g.drawRect(cellRect.x - 1, cellRect.y - 1, cellRect.width, cellRect.height);
       g.setColor(c);
   }

}</code=java>