I am using a JTable in my application and wish to have 2 rows for headings, similar to this:
Is this even possible or will I have to do something else? If so, what? Using Supertitle-titleA, SuperTitle-titleB will take up too much space and make information redundant.
We had the same requirement in our last project. What I have found is an Implementation for a GroupableTableHeader on java2s.com. However, I have pimped it a bit, although I cannot recall what exactly. Beneath is the implementation of the three classes as how we use them.
ColumnGroup.java
import java.awt.Component;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
/**
* ColumnGroup
*
* @version 1.0 20.10.1998
* @author Nobuo Tamemasa
*/
public class ColumnGroup {
protected TableCellRenderer renderer;
protected List<TableColumn> columns;
protected List<ColumnGroup> groups;
protected String text;
protected int margin = 0;
public ColumnGroup(String text) {
this(text, null);
}
public ColumnGroup(String text, TableCellRenderer renderer) {
this.text = text;
this.renderer = renderer;
this.columns = new ArrayList<TableColumn>();
this.groups = new ArrayList<ColumnGroup>();
}
public void add(TableColumn column) {
columns.add(column);
}
public void add(ColumnGroup group) {
groups.add(group);
}
/**
* @param column
* TableColumn
*/
public List<ColumnGroup> getColumnGroups(TableColumn column) {
if (!contains(column)) {
return Collections.emptyList();
}
List<ColumnGroup> result = new ArrayList<ColumnGroup>();
result.add(this);
if (columns.contains(column)) {
return result;
}
for (ColumnGroup columnGroup : groups) {
result.addAll(columnGroup.getColumnGroups(column));
}
return result;
}
private boolean contains(TableColumn column) {
if (columns.contains(column)) {
return true;
}
for (ColumnGroup group : groups) {
if (group.contains(column)) {
return true;
}
}
return false;
}
public TableCellRenderer getHeaderRenderer() {
return renderer;
}
public void setHeaderRenderer(TableCellRenderer renderer) {
this.renderer = renderer;
}
public String getHeaderValue() {
return text;
}
public Dimension getSize(JTable table) {
TableCellRenderer renderer = this.renderer;
if (renderer == null) {
renderer = table.getTableHeader().getDefaultRenderer();
}
Component comp = renderer.getTableCellRendererComponent(table, getHeaderValue() == null || getHeaderValue().trim().isEmpty() ? " "
: getHeaderValue(), false, false, -1, -1);
int height = comp.getPreferredSize().height;
int width = 0;
for (ColumnGroup columnGroup : groups) {
width += columnGroup.getSize(table).width;
}
for (TableColumn tableColumn : columns) {
width += tableColumn.getWidth();
width += margin;
}
return new Dimension(width, height);
}
public void setColumnMargin(int margin) {
this.margin = margin;
for (ColumnGroup columnGroup : groups) {
columnGroup.setColumnMargin(margin);
}
}
}
GroupableTableHeader.java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
/**
* GroupableTableHeader
*
* @version 1.0 20.10.1998
* @author Nobuo Tamemasa
*/
@SuppressWarnings("serial")
public class GroupableTableHeader extends JTableHeader {
@SuppressWarnings("unused")
private static final String uiClassID = "GroupableTableHeaderUI";
protected List<ColumnGroup> columnGroups = new ArrayList<ColumnGroup>();
public GroupableTableHeader(TableColumnModel model) {
super(model);
setUI(new GroupableTableHeaderUI());
setReorderingAllowed(false);
// setDefaultRenderer(new MultiLineHeaderRenderer());
}
@Override
public void updateUI() {
setUI(new GroupableTableHeaderUI());
}
@Override
public void setReorderingAllowed(boolean b) {
super.setReorderingAllowed(false);
}
public void addColumnGroup(ColumnGroup g) {
columnGroups.add(g);
}
public List<ColumnGroup> getColumnGroups(TableColumn col) {
for (ColumnGroup group : columnGroups) {
List<ColumnGroup> groups = group.getColumnGroups(col);
if (!groups.isEmpty()) {
return groups;
}
}
return Collections.emptyList();
}
public void setColumnMargin() {
int columnMargin = getColumnModel().getColumnMargin();
for (ColumnGroup group : columnGroups) {
group.setColumnMargin(columnMargin);
}
}
}
GroupableTableHeaderUI.java
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class GroupableTableHeaderUI extends BasicTableHeaderUI {
protected GroupableTableHeader getHeader() {
return (GroupableTableHeader) header;
}
@Override
public void paint(Graphics g, JComponent c) {
Rectangle clipBounds = g.getClipBounds();
if (header.getColumnModel().getColumnCount() == 0) {
return;
}
int column = 0;
Dimension size = header.getSize();
Rectangle cellRect = new Rectangle(0, 0, size.width, size.height);
Map<ColumnGroup, Rectangle> groupSizeMap = new HashMap<ColumnGroup, Rectangle>();
for (Enumeration<TableColumn> enumeration = header.getColumnModel().getColumns(); enumeration.hasMoreElements();) {
cellRect.height = size.height;
cellRect.y = 0;
TableColumn aColumn = enumeration.nextElement();
List<ColumnGroup> groups = getHeader().getColumnGroups(aColumn);
int groupHeight = 0;
for (ColumnGroup group : groups) {
Rectangle groupRect = groupSizeMap.get(group);
if (groupRect == null) {
groupRect = new Rectangle(cellRect);
Dimension d = group.getSize(header.getTable());
groupRect.width = d.width;
groupRect.height = d.height;
groupSizeMap.put(group, groupRect);
}
paintCell(g, groupRect, group);
groupHeight += groupRect.height;
cellRect.height = size.height - groupHeight;
cellRect.y = groupHeight;
}
cellRect.width = aColumn.getWidth();
if (cellRect.intersects(clipBounds)) {
paintCell(g, cellRect, column);
}
cellRect.x += cellRect.width;
column++;
}
}
private void paintCell(Graphics g, Rectangle cellRect, int columnIndex) {
TableColumn aColumn = header.getColumnModel().getColumn(columnIndex);
TableCellRenderer renderer = aColumn.getHeaderRenderer();
if (renderer == null) {
renderer = getHeader().getDefaultRenderer();
}
Component c = renderer.getTableCellRendererComponent(header.getTable(), aColumn.getHeaderValue(), false, false,
-1, columnIndex);
c.setBackground(UIManager.getColor("control"));
rendererPane.paintComponent(g, c, header, cellRect.x, cellRect.y, cellRect.width, cellRect.height, true);
}
private void paintCell(Graphics g, Rectangle cellRect, ColumnGroup cGroup) {
TableCellRenderer renderer = cGroup.getHeaderRenderer();
if (renderer == null) {
renderer = getHeader().getDefaultRenderer();
}
Component component = renderer.getTableCellRendererComponent(header.getTable(), cGroup.getHeaderValue(), false,
false, -1, -1);
rendererPane
.paintComponent(g, component, header, cellRect.x, cellRect.y, cellRect.width, cellRect.height, true);
}
private int getHeaderHeight() {
int headerHeight = 0;
TableColumnModel columnModel = header.getColumnModel();
for (int column = 0; column < columnModel.getColumnCount(); column++) {
TableColumn aColumn = columnModel.getColumn(column);
TableCellRenderer renderer = aColumn.getHeaderRenderer();
if (renderer == null) {
renderer = getHeader().getDefaultRenderer();
}
Component comp = renderer.getTableCellRendererComponent(header.getTable(), aColumn.getHeaderValue(), false,
false, -1, column);
int cHeight = comp.getPreferredSize().height;
List<ColumnGroup> groups = getHeader().getColumnGroups(aColumn);
for (ColumnGroup group : groups) {
cHeight += group.getSize(header.getTable()).height;
}
headerHeight = Math.max(headerHeight, cHeight);
}
return headerHeight;
}
@Override
public Dimension getPreferredSize(JComponent c) {
int width = 0;
for (Enumeration<TableColumn> enumeration = header.getColumnModel().getColumns(); enumeration.hasMoreElements();) {
TableColumn aColumn = enumeration.nextElement();
width += aColumn.getPreferredWidth();
}
return createHeaderSize(width);
}
private Dimension createHeaderSize(int width) {
TableColumnModel columnModel = header.getColumnModel();
width += columnModel.getColumnMargin() * columnModel.getColumnCount();
if (width > Integer.MAX_VALUE) {
width = Integer.MAX_VALUE;
}
return new Dimension(width, getHeaderHeight());
}
}
Yes, you need to supply your own JTableHeader. The difficult comes in trying to layout it out.
You'll need to look at JTableHeader.getHeaderRect(column), this tells the renderer how to layout the column headers.
You're going to have to take into consideration the height of each column renderer as well as the height of the component you want to use a label renderer (I'd suggest using the Header's column renderer as a bases, but that's up to you)
Take account of space between columns (when more then two columns join to group - right border of group column become invisible). Here is solution:
public Dimension getSize(JTable table) {
Component comp = renderer.getTableCellRendererComponent(
table, getHeaderValue(), false, false,-1, -1);
int height = comp.getPreferredSize().height;
int width = 0;
Enumeration e = v.elements();
int testNum = 0;
while (e.hasMoreElements()) {
Object obj = e.nextElement();
if (obj instanceof TableColumn) {
TableColumn aColumn = (TableColumn)obj;
width += aColumn.getWidth()-table.getIntercellSpacing().width;
width += margin;
} else {
width += ((ColumnGroup)obj).getSize(table).width-table.getIntercellSpacing().width;
}
}
return new Dimension(width+2*table.getIntercellSpacing().width, height);
}
来源:https://stackoverflow.com/questions/11665397/jtable-multiple-header-rows