|
Java™ by example!
|
|
|
How do I create a JTable with multiple row headers?
Courtesy of Nobuo Tamemasa (http://www2.gol.com/users/tame/swing/examples/JTableExamples1.htm7)
 MultipleRowHeaderExample.java:
 import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.table.*; import javax.swing.event.*; /* ---------------------------------------------- * | SNo. | * ---------------------------------------------- * | | 1 | * | Name |----------------------------------- * | | 2 | * ---------------------------------------------- * | | 1 | * | |----------------------------------- * | Language | 2 | * | |----------------------------------- * | | 3 | * ---------------------------------------------- */ /** * @version 1.0 03/06/99 */ public class MultipleRowHeaderExample extends JFrame { Object[][] data; Object[] column; JTable table; MultiSpanCellTable fixedTable; public MultipleRowHeaderExample() { super( "Multiple Row Header Example" ); setSize( 400, 150 ); data = new Object[][]{ {"SNo." ,"" }, {"Name" ,"1"}, {"" ,"2"}, {"Language","1"}, {"" ,"2"}, {"" ,"3"}}; column = new Object[]{"",""}; AttributiveCellTableModel fixedModel = new AttributiveCellTableModel(data, column) { public boolean CellEditable(int row, int col) { return false; } }; CellSpan cellAtt =(CellSpan)fixedModel.getCellAttribute(); cellAtt.combine(new int[] {0} ,new int[] {0,1}); cellAtt.combine(new int[] {1,2} ,new int[] {0}); cellAtt.combine(new int[] {3,4,5},new int[] {0}); DefaultTableModel model = new DefaultTableModel(data.length, 3); fixedTable = new MultiSpanCellTable( fixedModel ); table = new JTable( model ); fixedTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); fixedTable.setDefaultRenderer(Object.class, new RowHeaderRenderer(fixedTable)); fixedTable.setGridColor(table.getTableHeader().getBackground()); JScrollPane scroll = new JScrollPane( table ); JViewport viewport = new JViewport(); viewport.setView(fixedTable); viewport.setPreferredSize(fixedTable.getPreferredSize()); scroll.setRowHeaderView(viewport); getContentPane().add(scroll, BorderLayout.CENTER); } public static void main(String[] args) { MultipleRowHeaderExample frame = new MultipleRowHeaderExample(); frame.addWindowListener( new WindowAdapter() { public void windowClosing( WindowEvent e ) { System.exit(0); } }); frame.setVisible(true); } class RowHeaderRenderer extends JLabel implements TableCellRenderer { RowHeaderRenderer(JTable table) { JTableHeader header = table.getTableHeader(); setOpaque(true); setBorder(UIManager.getBorder("TableHeader.cellBorder")); setHorizontalAlignment(CENTER); setForeground(header.getForeground()); setBackground(header.getBackground()); setFont(header.getFont()); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { setText((value == null) ? "" : value.toString()); return this; } } }
|
AttributiveCellTableModel.java:
 import java.util.*; import java.awt.*; import javax.swing.*; import javax.swing.table.*; import javax.swing.event.*; /** * @version 1.0 11/22/98 */ public class AttributiveCellTableModel extends DefaultTableModel { protected CellAttribute cellAtt; public AttributiveCellTableModel() { this((Vector)null, 0); } public AttributiveCellTableModel(int numRows, int numColumns) { Vector names = new Vector(numColumns); names.setSize(numColumns); setColumnIdentifiers(names); dataVector = new Vector(); setNumRows(numRows); cellAtt = new DefaultCellAttribute(numRows,numColumns); } public AttributiveCellTableModel(Vector columnNames, int numRows) { setColumnIdentifiers(columnNames); dataVector = new Vector(); setNumRows(numRows); cellAtt = new DefaultCellAttribute(numRows,columnNames.size()); } public AttributiveCellTableModel(Object[] columnNames, int numRows) { this(convertToVector(columnNames), numRows); } public AttributiveCellTableModel(Vector data, Vector columnNames) { setDataVector(data, columnNames); } public AttributiveCellTableModel(Object[][] data, Object[] columnNames) { setDataVector(data, columnNames); } public void setDataVector(Vector newData, Vector columnNames) { if (newData == null) throw new IllegalArgumentException("setDataVector() - Null parameter"); dataVector = new Vector(0); setColumnIdentifiers(columnNames); dataVector = newData; // cellAtt = new DefaultCellAttribute(dataVector.size(), columnIdentifiers.size()); newRowsAdded(new TableModelEvent(this, 0, getRowCount()-1, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT)); } public void addColumn(Object columnName, Vector columnData) { if (columnName == null) throw new IllegalArgumentException("addColumn() - null parameter"); columnIdentifiers.addElement(columnName); int index = 0; Enumeration enumeration = dataVector.elements(); while (enumeration.hasMoreElements()) { Object value; if ((columnData != null) && (index < columnData.size())) value = columnData.elementAt(index); else value = null; ((Vector)enumeration.nextElement()).addElement(value); index++; } // cellAtt.addColumn();
fireTableStructureChanged(); } public void addRow(Vector rowData) { Vector newData = null; if (rowData == null) { newData = new Vector(getColumnCount()); } else { rowData.setSize(getColumnCount()); } dataVector.addElement(newData); // cellAtt.addRow(); newRowsAdded(new TableModelEvent(this, getRowCount()-1, getRowCount()-1, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT)); } public void insertRow(int row, Vector rowData) { if (rowData == null) { rowData = new Vector(getColumnCount()); } else { rowData.setSize(getColumnCount()); } dataVector.insertElementAt(rowData, row); // cellAtt.insertRow(row); newRowsAdded(new TableModelEvent(this, row, row, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT)); } public CellAttribute getCellAttribute() { return cellAtt; } public void setCellAttribute(CellAttribute newCellAtt) { int numColumns = getColumnCount(); int numRows = getRowCount(); if ((newCellAtt.getSize().width != numColumns) || (newCellAtt.getSize().height != numRows)) { newCellAtt.setSize(new Dimension(numRows, numColumns)); } cellAtt = newCellAtt; fireTableDataChanged(); } /* public void changeCellAttribute(int row, int column, Object command) { cellAtt.changeAttribute(row, column, command); } public void changeCellAttribute(int[] rows, int[] columns, Object command) { cellAtt.changeAttribute(rows, columns, command); } */ }
|
DefaultCellAttribute.java:
 import java.awt.*; /** * @version 1.0 11/22/98 */ public class DefaultCellAttribute // implements CellAttribute ,CellSpan { implements CellAttribute ,CellSpan ,ColoredCell ,CellFont { // // !!!! CAUTION !!!!! // these values must be synchronized to Table data // protected int rowSize; protected int columnSize; protected int[][][] span; // CellSpan protected Color[][] foreground; // ColoredCell protected Color[][] background; // protected Font[][] font; // CellFont public DefaultCellAttribute() { this(1,1); } public DefaultCellAttribute(int numRows, int numColumns) { setSize(new Dimension(numColumns, numRows)); } protected void initValue() { for(int i=0; i<span.length;i++) { for(int j=0; j<span[i].length; j++) { span[i][j][CellSpan.COLUMN] = 1; span[i][j][CellSpan.ROW] = 1; } } } // // CellSpan // public int[] getSpan(int row, int column) { if (isOutOfBounds(row, column)) { int[] ret_code = {1,1}; return ret_code; } return span[row][column]; } public void setSpan(int[] span, int row, int column) { if (isOutOfBounds(row, column)) return; this.span[row][column] = span; } public boolean isVisible(int row, int column) { if (isOutOfBounds(row, column)) return false; if ((span[row][column][CellSpan.COLUMN] < 1) ||(span[row][column][CellSpan.ROW] < 1)) return false; return true; } public void combine(int[] rows, int[] columns) { if (isOutOfBounds(rows, columns)) return; int rowSpan = rows.length; int columnSpan = columns.length; int startRow = rows[0]; int startColumn = columns[0]; for (int i=0;i<rowSpan;i++) { for (int j=0;j<columnSpan;j++) { if ((span[startRow +i][startColumn +j][CellSpan.COLUMN] != 1) ||(span[startRow +i][startColumn +j][CellSpan.ROW] != 1)) { //System.out.println("can't combine"); return ; } } } for (int i=0,ii=0;i<rowSpan;i++,ii--) { for (int j=0,jj=0;j<columnSpan;j++,jj--) { span[startRow +i][startColumn +j][CellSpan.COLUMN] = jj; span[startRow +i][startColumn +j][CellSpan.ROW] = ii; //System.out.println("r " +ii +" c " +jj); } } span[startRow][startColumn][CellSpan.COLUMN] = columnSpan; span[startRow][startColumn][CellSpan.ROW] = rowSpan; } public void split(int row, int column) { if (isOutOfBounds(row, column)) return; int columnSpan = span[row][column][CellSpan.COLUMN]; int rowSpan = span[row][column][CellSpan.ROW]; for (int i=0;i<rowSpan;i++) { for (int j=0;j<columnSpan;j++) { span[row +i][column +j][CellSpan.COLUMN] = 1; span[row +i][column +j][CellSpan.ROW] = 1; } } } // // ColoredCell // public Color getForeground(int row, int column) { if (isOutOfBounds(row, column)) return null; return foreground[row][column]; } public void setForeground(Color color, int row, int column) { if (isOutOfBounds(row, column)) return; foreground[row][column] = color; } public void setForeground(Color color, int[] rows, int[] columns) { if (isOutOfBounds(rows, columns)) return; setValues(foreground, color, rows, columns); } public Color getBackground(int row, int column) { if (isOutOfBounds(row, column)) return null; return background[row][column]; } public void setBackground(Color color, int row, int column) { if (isOutOfBounds(row, column)) return; background[row][column] = color; } public void setBackground(Color color, int[] rows, int[] columns) { if (isOutOfBounds(rows, columns)) return; setValues(background, color, rows, columns); } // // // CellFont // public Font getFont(int row, int column) { if (isOutOfBounds(row, column)) return null; return font[row][column]; } public void setFont(Font font, int row, int column) { if (isOutOfBounds(row, column)) return; this.font[row][column] = font; } public void setFont(Font font, int[] rows, int[] columns) { if (isOutOfBounds(rows, columns)) return; setValues(this.font, font, rows, columns); } // // // CellAttribute // public void addColumn() { int[][][] oldSpan = span; int numRows = oldSpan.length; int numColumns = oldSpan[0].length; span = new int[numRows][numColumns + 1][2]; System.arraycopy(oldSpan,0,span,0,numRows); for (int i=0;i<numRows;i++) { span[i][numColumns][CellSpan.COLUMN] = 1; span[i][numColumns][CellSpan.ROW] = 1; } } public void addRow() { int[][][] oldSpan = span; int numRows = oldSpan.length; int numColumns = oldSpan[0].length; span = new int[numRows + 1][numColumns][2]; System.arraycopy(oldSpan,0,span,0,numRows); for (int i=0;i<numColumns;i++) { span[numRows][i][CellSpan.COLUMN] = 1; span[numRows][i][CellSpan.ROW] = 1; } } public void insertRow(int row) { int[][][] oldSpan = span; int numRows = oldSpan.length; int numColumns = oldSpan[0].length; span = new int[numRows + 1][numColumns][2]; if (0 < row) { System.arraycopy(oldSpan,0,span,0,row-1); } System.arraycopy(oldSpan,0,span,row,numRows - row); for (int i=0;i<numColumns;i++) { span[row][i][CellSpan.COLUMN] = 1; span[row][i][CellSpan.ROW] = 1; } } public Dimension getSize() { return new Dimension(rowSize, columnSize); } public void setSize(Dimension size) { columnSize = size.width; rowSize = size.height; span = new int[rowSize][columnSize][2]; // 2: COLUMN,ROW foreground = new Color[rowSize][columnSize]; background = new Color[rowSize][columnSize]; font = new Font[rowSize][columnSize]; initValue(); } /* public void changeAttribute(int row, int column, Object command) { } public void changeAttribute(int[] rows, int[] columns, Object command) { } */ protected boolean isOutOfBounds(int row, int column) { if ((row < 0)||(rowSize <= row) ||(column < 0)||(columnSize <= column)) { return true; } return false; } protected boolean isOutOfBounds(int[] rows, int[] columns) { for (int i=0;i<rows.length;i++) { if ((rows[i] < 0)||(rowSize <= rows[i])) return true; } for (int i=0;i<columns.length;i++) { if ((columns[i] < 0)||(columnSize <= columns[i])) return true; } return false; } protected void setValues(Object[][] target, Object value, int[] rows, int[] columns) { for (int i=0;i<rows.length;i++) { int row = rows[i]; for (int j=0;j<columns.length;j++) { int column = columns[j]; target[row][column] = value; } } } }
|
CellAttribute.java:
 import java.awt.*; /** * @version 1.0 11/22/98 */ public interface CellAttribute { public void addColumn(); public void addRow(); public void insertRow(int row); public Dimension getSize(); public void setSize(Dimension size); }
|
ColoredCell.java:
 import java.awt.*; /** * @version 1.0 11/22/98 */ public interface ColoredCell { public Color getForeground(int row, int column); public void setForeground(Color color, int row, int column); public void setForeground(Color color, int[] rows, int[] columns); public Color getBackground(int row, int column); public void setBackground(Color color, int row, int column); public void setBackground(Color color, int[] rows, int[] columns); }
|
CellFont.java:
 import java.awt.*; /** * @version 1.0 11/22/98 */ public interface CellFont { public Font getFont(int row, int column); public void setFont(Font font, int row, int column); public void setFont(Font font, int[] rows, int[] columns); }
|
CellSpan.java:
 /** * @version 1.0 11/22/98 */ public interface CellSpan { public final int ROW = 0; public final int COLUMN = 1; public int[] getSpan(int row, int column); public void setSpan(int[] span, int row, int column); public boolean isVisible(int row, int column); public void combine(int[] rows, int[] columns); public void split(int row, int column);
}
|
MultiSpanCellTable.java:
 import java.util.*; import java.awt.*; import javax.swing.*; import javax.swing.table.*; import javax.swing.plaf.basic.*; import javax.swing.event.*; /** * @version 1.0 11/26/98 */ public class MultiSpanCellTable extends JTable { public MultiSpanCellTable(TableModel model) { super(model); setUI(new MultiSpanCellTableUI()); getTableHeader().setReorderingAllowed(false); setCellSelectionEnabled(true); setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); } 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; } CellSpan cellAtt = (CellSpan)((AttributiveCellTableModel)getModel()).getCellAttribute(); if (! cellAtt.isVisible(row,column)) { int temp_row = row; int temp_column = column; row += cellAtt.getSpan(temp_row,temp_column)[CellSpan.ROW]; column += cellAtt.getSpan(temp_row,temp_column)[CellSpan.COLUMN]; } int[] n = cellAtt.getSpan(row,column); int index = 0; int columnMargin = getColumnModel().getColumnMargin(); Rectangle cellFrame = new Rectangle(); int aCellHeight = rowHeight + rowMargin; cellFrame.y = row * aCellHeight; cellFrame.height = n[CellSpan.ROW] * 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++; } for (int i=0;i< n[CellSpan.COLUMN]-1;i++) { 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 int[] rowColumnAtPoint(Point point) { int[] retValue = {-1,-1}; int row = point.y / (rowHeight + rowMargin); if ((row <0)||(getRowCount() <= row)) return retValue; int column = getColumnModel().getColumnIndexAtX(point.x); CellSpan cellAtt = (CellSpan)((AttributiveCellTableModel)getModel()).getCellAttribute(); if (cellAtt.isVisible(row,column)) { retValue[CellSpan.COLUMN] = column; retValue[CellSpan.ROW ] = row; return retValue; } retValue[CellSpan.COLUMN] = column + cellAtt.getSpan(row,column)[CellSpan.COLUMN]; retValue[CellSpan.ROW ] = row + cellAtt.getSpan(row,column)[CellSpan.ROW]; return retValue; } public int rowAtPoint(Point point) { return rowColumnAtPoint(point)[CellSpan.ROW]; } public int columnAtPoint(Point point) { return rowColumnAtPoint(point)[CellSpan.COLUMN]; } public void columnSelectionChanged(ListSelectionEvent e) { repaint(); } public void valueChanged(ListSelectionEvent e) { int firstIndex = e.getFirstIndex(); int lastIndex = e.getLastIndex(); if (firstIndex == -1 && lastIndex == -1) { // Selection cleared. repaint(); } Rectangle dirtyRegion = getCellRect(firstIndex, 0, false); int numCoumns = getColumnCount(); int index = firstIndex; for (int i=0;i<numCoumns;i++) { dirtyRegion.add(getCellRect(index, i, false)); } index = lastIndex; for (int i=0;i<numCoumns;i++) { dirtyRegion.add(getCellRect(index, i, false)); } repaint(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width, dirtyRegion.height); } }
|
MultiSpanCellTableUI.java:
 import java.util.*; import java.awt.*; import javax.swing.*; import javax.swing.table.*; import javax.swing.plaf.basic.*; /** * @version 1.0 11/26/98 */ public class MultiSpanCellTableUI extends BasicTableUI { 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; AttributiveCellTableModel tableModel = (AttributiveCellTableModel)table.getModel(); CellSpan cellAtt = (CellSpan)tableModel.getCellAttribute(); int numColumns = table.getColumnCount(); for (int column = 0; column < numColumns; column++) { Rectangle cellRect = table.getCellRect(row,column,true); int cellRow,cellColumn; if (cellAtt.isVisible(row,column)) { cellRow = row; cellColumn = column; // System.out.print(" "+column+" "); // debug } else { cellRow = row + cellAtt.getSpan(row,column)[CellSpan.ROW]; cellColumn = column + cellAtt.getSpan(row,column)[CellSpan.COLUMN]; // System.out.print(" ("+column+")"); // debug } 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) { int spacingHeight = table.getRowMargin(); int spacingWidth = table.getColumnModel().getColumnMargin(); Color c = g.getColor(); g.setColor(table.getGridColor()); g.drawRect(cellRect.x,cellRect.y,cellRect.width-1,cellRect.height-1); g.setColor(c); cellRect.setBounds(cellRect.x + spacingWidth/2, cellRect.y + spacingHeight/2, cellRect.width - spacingWidth, cellRect.height - spacingHeight); if (table.isEditing() && table.getEditingRow()==row && table.getEditingColumn()==column) { Component component = table.getEditorComponent(); component.setBounds(cellRect); component.validate(); } else { TableCellRenderer renderer = table.getCellRenderer(row, column); 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, cellRect.height, true); } } }
|
Further Information
Author of answer:
Comments to this answer are only viewable by members. Login or become a member!
|
|
|
|
|