表头合并单元格. JTable Groupable TableHeader

表头合并单元格. 照例不多说了, 看代码吧.

首先需要定义一个接口, 看看表头是怎么合并的

/**
 * 列头组
 * 
 * 
@author Brad.Wu
 * 
@version 1.0
 
*/

public   interface  Group  {
    
/**
     * 获取所在行
     * 
     * 
@return
     
*/

    
public int getRow();

    
/**
     * 获取所在列
     * 
     * 
@return
     
*/

    
public int getColumn();

    
/**
     * 获取占列个数
     * 
     * 
@return
     
*/

    
public int getColumnSpan();

    
/**
     * 获取占行个数
     * 
     * 
@return
     
*/

    
public int getRowSpan();

    
/**
     * 获取文字
     * 
     * 
@return
     
*/

    
public Object getHeaderValue();
}

 这个和HTML的写法其实一样的. 主要就是每个Cell所在的位置, 占的行列数以及文字.

接下来是一个默认的实现. 其实不写接口也可以, 因为通常不会对表头做动作的.

 

/**
 * 默认Group实现
 * 
 * 
@author Brad.Wu
 * 
@version 1.0
 
*/

public   class  DefaultGroup  implements  Group  {
    
private int row = 0;

    
private int column = 0;

    
private int rowSpan = 1;

    
private int columnSpan = 1;

    
private Object headerValue = null;

    
/*
     * (非 Javadoc)
     * 
     * @see com.eplat.realty.view.component.table.Group#getRow()
     
*/

    
public int getRow() {
        
return this.row;
    }


    
/**
     * 
@param row 要设置的 row。
     
*/

    
public void setRow(int row) {
        
this.row = row;
    }


    
/*
     * (非 Javadoc)
     * 
     * @see com.eplat.realty.view.component.table.Group#getColumn()
     
*/

    
public int getColumn() {
        
return this.column;
    }


    
/**
     * 
@param column 要设置的 column。
     
*/

    
public void setColumn(int column) {
        
this.column = column;
    }


    
/*
     * (非 Javadoc)
     * 
     * @see com.eplat.realty.view.component.table.Group#getColumnSpan()
     
*/

    
public int getColumnSpan() {
        
return this.columnSpan;
    }


    
/**
     * 
@param columnSpan 要设置的 columnSpan。
     
*/

    
public void setColumnSpan(int columnSpan) {
        
this.columnSpan = columnSpan;
    }


    
/*
     * (非 Javadoc)
     * 
     * @see com.eplat.realty.view.component.table.Group#getRowSpan()
     
*/

    
public int getRowSpan() {
        
return this.rowSpan;
    }


    
/**
     * 
@param rowSpan 要设置的 rowSpan。
     
*/

    
public void setRowSpan(int rowSpan) {
        
this.rowSpan = rowSpan;
    }


    
/*
     * (非 Javadoc)
     * 
     * @see com.eplat.realty.view.component.table.Group#getHeaderValue()
     
*/

    
public Object getHeaderValue() {
        
return this.headerValue;
    }


    
/**
     * 
@param headerValue 要设置的 headerValue。
     
*/

    
public void setHeaderValue(Object headerValue) {
        
this.headerValue = headerValue;
    }

}

 

重写一个表头组件

 

import  java.awt.Component;
import  java.awt.Rectangle;
import  java.util.ArrayList;
import  java.util.List;

import  javax.swing.JTable;
import  javax.swing.table.JTableHeader;
import  javax.swing.table.TableCellRenderer;
import  javax.swing.table.TableColumnModel;

/**
 * 可以合并的列头
 * 
 * 
@author Brad.Wu
 * 
@version 1.0
 
*/

@SuppressWarnings(
" serial " )
public   class  GroupableTableHeader  extends  JTableHeader  {
    
private int rowCount = 0;

    
private int columnCount = 0;

    
private List<Group> groups = new ArrayList<Group>();

    
public GroupableTableHeader() {
                                           
// 这个是必须的, 因为如果可以拖动列的位置, 那么一切都完蛋了.
                                           
// 如果你想实现这个功能, 那么只能你自己去做了, 我可不想做这个, 看上去超烦的
        this.setReorderingAllowed(false);
    }


    
/*
     * (非 Javadoc)
     * 
     * @see javax.swing.table.JTableHeader#updateUI()
     
*/

    @Override
    
public void updateUI() {
        setUI(
new GroupableTableHeaderUI());
    }


    
/*
     * 获取指定行列的位置
     
*/

    
public Rectangle getHeaderRect(int row, int column) {
        Rectangle r 
= new Rectangle();
        TableColumnModel cm 
= getColumnModel();

        Group group 
= this.getGroup(row, column);
        r.height 
= getHeight();

        
if (column < 0{
            
// x = width = 0;
            if (!getComponentOrientation().isLeftToRight()) {
                r.x 
= getWidthInRightToLeft();
            }

        }
 else if (column >= cm.getColumnCount()) {
            
if (getComponentOrientation().isLeftToRight()) {
                r.x 
= getWidth();
            }

        }
 else {
            
for (int i = 0; i < group.getColumn(); i++{
                r.x 
+= cm.getColumn(i).getWidth();
            }

            
for (int i = group.getColumn(), j = group.getColumn() + group.getColumnSpan() - 1; i < j; i++{
                r.width 
+= cm.getColumn(i).getWidth();
            }

            
if (!getComponentOrientation().isLeftToRight()) {
                r.x 
= getWidthInRightToLeft() - r.x - r.width;
            }

            
// r.width = cm.getColumn(column).getWidth();
        }

        
return r;
    }


    
/**
     * 获取Group的Y位置
     * 
     * 
@param group
     * 
@return
     
*/

    
public int getYOfGroup(Group group) {
        
int row = group.getRow();
        TableCellRenderer renderer 
= this.getDefaultRenderer();
        Component comp 
= renderer.getTableCellRendererComponent(getTable(), group.getHeaderValue(),
                
falsefalse, group.getRow(), group.getColumn());
        
return row * comp.getPreferredSize().height;
    }


    
/**
     * 获取Group的高度
     * 
     * 
@param group
     * 
@return
     
*/

    
public int getHeightOfGroup(Group group) {
        
int rowSpan = group.getRowSpan();
        TableCellRenderer renderer 
= this.getDefaultRenderer();
        Component comp 
= renderer.getTableCellRendererComponent(getTable(), group.getHeaderValue(),
                
falsefalse, group.getRow(), group.getColumn());
        
return rowSpan * comp.getPreferredSize().height;
    }


    
private int getWidthInRightToLeft() {
        
if ((table != null&& (table.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF)) {
            
return table.getWidth();
        }

        
return super.getWidth();
    }


    
/**
     * 增加Group
     * 
     * 
@param group
     
*/

    
public void addGroup(Group group) {
        groups.add(group);
        
int row = group.getRow();
        
int rowSpan = group.getRowSpan();
        rowCount 
= Math.max(rowCount, row + rowSpan);
        
int column = group.getColumn();
        
int columnSpan = group.getColumnSpan();
        columnCount 
= Math.max(columnCount, column + columnSpan);
    }


    
/**
     * 移除所有Group
     
*/

    
public void removeAllGroups() {
        groups.clear();
    }


    
/**
     * 获取所有的Group
     * 
     * 
@return
     
*/

    
public List<Group> getGroups() {
        List
<Group> list = new ArrayList<Group>();
        list.addAll(groups);
        
return list;
    }


    
/**
     * 获取指定列上的Group
     * 
     * 
@param columnIndex
     * 
@return
     
*/

    
public List<Group> getGroupsAtColumn(int columnIndex) {
        List
<Group> list = new ArrayList<Group>();
        
for (Group group : groups) {
            
int minColumnIndex = group.getColumn();
            
int maxColumnIndex = minColumnIndex + group.getColumnSpan() - 1;
            
if (minColumnIndex <= columnIndex && maxColumnIndex >= columnIndex) {
                list.add(group);
            }

        }

        
return list;
    }


    
/**
     * 获取指定行上的所有Group
     * 
     * 
@param rowIndex
     * 
@return
     
*/

    
public List<Group> getGroupsAtRow(int rowIndex) {
        List
<Group> list = new ArrayList<Group>();
        
for (Group group : groups) {
            
int minRowIndex = group.getRow();
            
int maxRowIndex = minRowIndex + group.getRowSpan() - 1;
            
if (minRowIndex <= rowIndex && maxRowIndex >= rowIndex) {
                list.add(group);
            }

        }

        
return list;
    }


    
/**
     * 获取行数
     * 
     * 
@return
     
*/

    
public int getRowCount() {
        
return this.rowCount;
    }


    
/**
     * 
@return Returns the columnCount.
     
*/

    
public int getColumnCount() {
        
return this.columnCount;
    }


    
/*
     * (非 Javadoc)
     * 
     * @see javax.swing.table.JTableHeader#setTable(javax.swing.JTable)
     
*/

    @Override
    
public void setTable(JTable table) {
        
super.setColumnModel(table.getColumnModel());
        
super.setTable(table);
    }


    
/**
     * 获取指定行列的Group
     * 
     * 
@param row
     * 
@param column
     * 
@return
     
*/

    
public Group getGroup(int row, int column) {
        
for (Group group : groups) {
            
int rowIndex = group.getRow();
            
int columnIndex = group.getColumn();
            
int rowSpan = group.getRowSpan();
            
int columnSpan = group.getColumnSpan();
            
if (rowIndex <= row && rowIndex + rowSpan > row && columnIndex <= column
                    
&& columnIndex + columnSpan > column)
                
return group;
        }

        
return null;
    }


    
/**
     * (非 Javadoc)
     * 
@see javax.swing.table.JTableHeader#createDefaultRenderer()
     
*/

    @Override
    
protected TableCellRenderer createDefaultRenderer() {
        
return new TableHeaderRenderer();
    }

}

里面用到的TableHeaderRenderer, 没什么花头的, 贴出来看看

import  java.awt.Color;
import  java.awt.Component;
import  java.awt.Dimension;
import  java.awt.Rectangle;
import  java.io.Serializable;

import  javax.swing.JLabel;
import  javax.swing.JTable;
import  javax.swing.UIManager;
import  javax.swing.table.DefaultTableCellRenderer;
import  javax.swing.table.JTableHeader;
import  javax.swing.table.TableCellRenderer;

import  com.eplat.realty.view.Constants;
import  com.eplat.realty.view.component.label.M2Label;

/**
 * Created at 2006-9-5 16:02:46

 * 表头描述器
 * 
 * 
@author Brad.Wu
 * 
@version 1.0
 
*/

@SuppressWarnings(
" serial " )
public   class  TableHeaderRenderer  extends  M2Label  implements  TableCellRenderer, Serializable  {
    
/**
     * Creates a default table cell renderer.
     
*/

    
public TableHeaderRenderer() {
        setOpaque(
true);
        setHorizontalAlignment(JLabel.CENTER);
    }


    
// implements javax.swing.table.TableCellRenderer
    /**
     * Returns the default table cell renderer.
     * 
     * 
@param table the JTable
     * 
@param value the value to assign to the cell at [row, column]
     * 
@param isSelected true if cell is selected
     * 
@param hasFocus true if cell has focus
     * 
@param row the row of the cell to render
     * 
@param column the column of the cell to render
     * 
@return the default table cell renderer
     
*/

    
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
            
boolean hasFocus, int row, int column) {

        
if (table != null{
            JTableHeader header 
= table.getTableHeader();
            
if (header != null{
                setForeground(header.getForeground());
                setBackground(header.getBackground());
                setFont(header.getFont());
            }

        }


        setBorder(UIManager.getBorder(
"TableHeader.cellBorder"));

        setValue(value);

        Dimension dim 
= getPreferredSize();
        
if (dim.height < Constants.TABLE_ROW_HEIGHT)
            setPreferredSize(
new Dimension(getPreferredSize().width, Constants.TABLE_ROW_HEIGHT));
        
return this;
    }


    
/*
     * The following methods are overridden as a performance measure to to prune code-paths are
     * often called in the case of renders but which we know are unnecessary. Great care should be
     * taken when writing your own renderer to weigh the benefits and drawbacks of overriding
     * methods like these.
     
*/


    
/**
     * Overridden for performance reasons. See the Implementation Note for
     * more information.
     
*/

    
public boolean isOpaque() {
        Color back 
= getBackground();
        Component p 
= getParent();
        
if (p != null{
            p 
= p.getParent();
        }

        
// p should now be the JTable.
        boolean colorMatch = (back != null&& (p != null&& back.equals(p.getBackground())
                
&& p.isOpaque();
        
return !colorMatch && super.isOpaque();
    }


    
/**
     * Overridden for performance reasons. See the Implementation Note for
     * more information.
     * 
     * 
@since 1.5
     
*/

    
public void invalidate() {
    }


    
/**
     * Overridden for performance reasons. See the Implementation Note for
     * more information.
     
*/

    
public void validate() {
    }


    
/**
     * Overridden for performance reasons. See the Implementation Note for
     * more information.
     
*/

    
public void revalidate() {
    }


    
/**
     * Overridden for performance reasons. See the Implementation Note for
     * more information.
     
*/

    
public void repaint(long tm, int x, int y, int width, int height) {
    }


    
/**
     * Overridden for performance reasons. See the Implementation Note for
     * more information.
     
*/

    
public void repaint(Rectangle r) {
    }


    
/**
     * Overridden for performance reasons. See the Implementation Note for
     * more information.
     * 
     * 
@since 1.5
     
*/

    
public void repaint() {
    }


    
/**
     * Overridden for performance reasons. See the Implementation Note for
     * more information.
     
*/

    
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
        
// Strings get interned...
        if (propertyName == "text"{
            
super.firePropertyChange(propertyName, oldValue, newValue);
        }

    }


    
/**
     * Overridden for performance reasons. See the Implementation Note for
     * more information.
     
*/

    
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
    }


    
/**
     * Sets the String object for the cell being rendered to value.
     * 
     * 
@param value the string value for this cell; if value is null it sets the text
     *            value to an empty string
     * 
@see JLabel#setText
     
*/

    
protected void setValue(Object value) {
        setText((value 
== null? "" : value.toString());
    }


    
/**
     * A subclass of DefaultTableCellRenderer that implements UIResource.
     * DefaultTableCellRenderer doesn't implement UIResource directly
     * so that applications can safely override the cellRenderer property with
     * DefaultTableCellRenderer subclasses.
     * 


     * Warning: Serialized objects of this class will not be compatible with
     * future Swing releases. The current serialization support is appropriate for short term
     * storage or RMI between applications running the same version of Swing. As of 1.4, support for
     * long term storage of all JavaBeansTM has been added to
     * the java.beans package. Please see {@link java.beans.XMLEncoder}.
     
*/
    
public static class UIResource extends DefaultTableCellRenderer implements
            javax.swing.plaf.UIResource 
{
    }


}

最重要的UI登场, 哈哈

 

import  java.awt.Component;
import  java.awt.Dimension;
import  java.awt.Graphics;
import  java.awt.Point;
import  java.awt.Rectangle;
import  java.util.Enumeration;
import  java.util.List;

import  javax.swing.JComponent;
import  javax.swing.plaf.basic.BasicTableHeaderUI;
import  javax.swing.table.TableCellRenderer;
import  javax.swing.table.TableColumn;
import  javax.swing.table.TableColumnModel;

/**
 * 可合并列头UI
 * 
 * 
@author Brad.Wu
 * 
@version 1.0
 
*/

public   class  GroupableTableHeaderUI  extends  BasicTableHeaderUI  {

    
/*
     * (非 Javadoc)
     * 
     * @see javax.swing.plaf.basic.BasicTableHeaderUI#paint(java.awt.Graphics,
     *      javax.swing.JComponent)
     
*/

    @Override
    
public void paint(Graphics g, JComponent c) {
        
if (header.getColumnModel().getColumnCount() <= 0{
            
return;
        }

        
boolean ltr = header.getComponentOrientation().isLeftToRight();

        Rectangle clip 
= g.getClipBounds();
        Point left 
= clip.getLocation();
        Point right 
= new Point(clip.x + clip.width - 1, clip.y);
        TableColumnModel cm 
= header.getColumnModel();
        
int cMin = header.columnAtPoint(ltr ? left : right);
        
int cMax = header.columnAtPoint(ltr ? right : left);
        
// This should never happen.
        if (cMin == -1{
            cMin 
= 0;
        }

        
// If the table does not have enough columns to fill the view we'll get -1.
        
// Replace this with the index of the last column.
        if (cMax == -1{
            cMax 
= cm.getColumnCount() - 1;
        }


        
// TableColumn draggedColumn = header.getDraggedColumn();
        int columnWidth;
        
// Rectangle cellRect = header.getHeaderRect(ltr ? cMin : cMax);
        TableColumn aColumn;
        
// if (ltr) {
        
// for (int column = cMin; column <= cMax; column++) {
        
// aColumn = cm.getColumn(column);
        
// columnWidth = aColumn.getWidth();
        
// cellRect.width = columnWidth;
        
// // if (aColumn != draggedColumn) {
        
// paintCell(g, cellRect, column);
        
// // }
        
// cellRect.x += columnWidth;
        
// }
        
// } else {
        
// for (int column = cMax; column >= cMin; column--) {
        
// aColumn = cm.getColumn(column);
        
// columnWidth = aColumn.getWidth();
        
// cellRect.width = columnWidth;
        
// // if (aColumn != draggedColumn) {
        
// paintCell(g, cellRect, column);
        
// // }
        
// cellRect.x += columnWidth;
        
// }
        
// }
        GroupableTableHeader gHeader = (GroupableTableHeader) header;
        
for (int row = 0, rowCount = gHeader.getRowCount(); row < rowCount; row++{
            Rectangle cellRect 
= gHeader.getHeaderRect(row, ltr ? cMin : cMax);
            
if (ltr) {
                
for (int column = cMin; column <= cMax; column++{
                    Group group 
= gHeader.getGroup(row, column);
                    cellRect.width 
= 0;
                    
for (int from = group.getColumn(), to = from + group.getColumnSpan() - 1; from <= to; from++{
                        aColumn 
= cm.getColumn(from);
                        columnWidth 
= aColumn.getWidth();
                        cellRect.width 
+= columnWidth;
                    }

                    cellRect.y 
= gHeader.getYOfGroup(group);
                    cellRect.height 
= gHeader.getHeightOfGroup(group);
                    paintCell(g, cellRect, row, column);
                    cellRect.x 
+= cellRect.width;
                    column 
+= group.getColumnSpan() - 1;
                }

            }
 else {
                
for (int column = cMax; column >= cMin; column--{
                    Group group 
= gHeader.getGroup(row, column);
                    cellRect.width 
= 0;
                    
for (int from = group.getColumn(), to = from + group.getColumnSpan() - 1; from <= to; from++{
                        aColumn 
= cm.getColumn(from);
                        columnWidth 
= aColumn.getWidth();
                        cellRect.width 
+= columnWidth;
                    }

                    paintCell(g, cellRect, row, column);
                    cellRect.x 
+= cellRect.width;
                    column 
-= group.getColumnSpan() - 1;
                }

            }

        }

        
// Remove all components in the rendererPane.
        rendererPane.removeAll();
    }


    
/**
     * 描画指定行列
     * 
     * 
@param g
     * 
@param cellRect
     * 
@param rowIndex
     * 
@param columnIndex
     
*/

    
private void paintCell(Graphics g, Rectangle cellRect, int rowIndex, int columnIndex) {
        Component component 
= getHeaderRenderer(rowIndex, columnIndex);
        rendererPane.paintComponent(g, component, header, cellRect.x, cellRect.y, cellRect.width,
                cellRect.height, 
true);
    }


    
/**
     * 获取指定行列的描述组件
     * 
     * 
@param rowIndex
     * 
@param columnIndex
     * 
@return
     
*/

    
private Component getHeaderRenderer(int rowIndex, int columnIndex) {
        GroupableTableHeader gHeader 
= (GroupableTableHeader) header;
        Group group 
= gHeader.getGroup(rowIndex, columnIndex);
        TableCellRenderer renderer 
= header.getDefaultRenderer();
        
return renderer.getTableCellRendererComponent(header.getTable(), group.getHeaderValue(),
                
falsefalse-1, columnIndex);
    }


    
/**
     * 获取列头的高度
     * 
     * 
@return
     
*/

    
private int getHeaderHeight() {
        
int height = 0;
        
int tempHeight = 0;
        GroupableTableHeader gHeader 
= (GroupableTableHeader) header;
        TableColumnModel cm 
= header.getColumnModel();
        
for (int column = 0, columnCount = cm.getColumnCount(); column < columnCount; column++{
            tempHeight 
= 0;
            List
<Group> groups = gHeader.getGroupsAtColumn(column);
            
for (Group group : groups) {
                TableCellRenderer renderer 
= gHeader.getDefaultRenderer();
                Component comp 
= renderer.getTableCellRendererComponent(header.getTable(), group
                        .getHeaderValue(), 
falsefalse-1, column);
                
int rendererHeight = comp.getPreferredSize().height;
                tempHeight 
+= rendererHeight;
            }

            height 
= Math.max(height, tempHeight);
        }

        
return height;
    }


    
private Dimension createHeaderSize(long width) {
        
// TableColumnModel columnModel = header.getColumnModel();
        
// None of the callers include the intercell spacing, do it here.
        if (width > Integer.MAX_VALUE) {
            width 
= Integer.MAX_VALUE;
        }

        
return new Dimension((int) width, getHeaderHeight());
    }


    
/**
     * Return the minimum size of the header. The minimum width is the sum of the minimum widths of
     * each column (plus inter-cell spacing).
     
*/

    
public Dimension getMinimumSize(JComponent c) {
        
long width = 0;
        Enumeration enumeration 
= header.getColumnModel().getColumns();
        
while (enumeration.hasMoreElements()) {
            TableColumn aColumn 
= (TableColumn) enumeration.nextElement();
            width 
= width + aColumn.getMinWidth();
        }

        
return createHeaderSize(width);
    }


    
/**
     * Return the preferred size of the header. The preferred height is the maximum of the preferred
     * heights of all of the components provided by the header renderers. The preferred width is the
     * sum of the preferred widths of each column (plus inter-cell spacing).
     
*/

    
public Dimension getPreferredSize(JComponent c) {
        
long width = 0;
        Enumeration enumeration 
= header.getColumnModel().getColumns();
        
while (enumeration.hasMoreElements()) {
            TableColumn aColumn 
= (TableColumn) enumeration.nextElement();
            width 
= width + aColumn.getPreferredWidth();
        }

        
return createHeaderSize(width);
    }


    
/**
     * Return the maximum size of the header. The maximum width is the sum of the maximum widths of
     * each column (plus inter-cell spacing).
     
*/

    
public Dimension getMaximumSize(JComponent c) {
        
long width = 0;
        Enumeration enumeration 
= header.getColumnModel().getColumns();
        
while (enumeration.hasMoreElements()) {
            TableColumn aColumn 
= (TableColumn) enumeration.nextElement();
            width 
= width + aColumn.getMaxWidth();
        }

        
return createHeaderSize(width);
    }

}

最后顺便贴一个测试程序, 可以看看怎么用法. 文档我也没有写过
import  java.awt.BorderLayout;
import  java.awt.HeadlessException;

import  javax.swing.JFrame;
import  javax.swing.JScrollPane;
import  javax.swing.JTable;
import  javax.swing.table.DefaultTableModel;

/**
 * 合并列头测试
 * 
 * 
@author Brad.Wu
 * 
@version 1.0
 
*/

@SuppressWarnings(
" serial " )
public   class  GroupableTableHeaderTest  extends  JFrame  {

    
/**
     * 
@param args
     
*/

    
public static void main(String[] args) {
        GroupableTableHeaderTest test 
= new GroupableTableHeaderTest();
        test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        test.setSize(
800600);
        test.setVisible(
true);
    }


    
private DefaultTableModel tableModel = new DefaultTableModel() {

        
/*
         * (非 Javadoc)
         * 
         * @see javax.swing.table.DefaultTableModel#getColumnCount()
         
*/

        @Override
        
public int getColumnCount() {
            
return 6;
        }


        
/*
         * (非 Javadoc)
         * 
         * @see javax.swing.table.DefaultTableModel#getRowCount()
         
*/

        @Override
        
public int getRowCount() {
            
return 2;
        }


    }
;

    
private JTable table = new JTable(tableModel);

    
private JScrollPane scroll = new JScrollPane(table);

    
/**
     * 
@throws HeadlessException
     
*/

    
public GroupableTableHeaderTest() throws HeadlessException {
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        GroupableTableHeader tableHeader 
= new GroupableTableHeader();
        table.setTableHeader(tableHeader);

        DefaultGroup group 
= new DefaultGroup();
        group.setRow(
0);
        group.setRowSpan(
2);
        group.setColumn(
0);
        group.setHeaderValue(
"楼层");
        tableHeader.addGroup(group);

        group 
= new DefaultGroup();
        group.setRow(
0);
        group.setRowSpan(
2);
        group.setColumn(
1);
        group.setHeaderValue(
"水平/垂直系数");
        tableHeader.addGroup(group);

        group 
= new DefaultGroup();
        group.setRow(
0);
        group.setColumn(
2);
        group.setColumnSpan(
2);
        group.setHeaderValue(
"A & B");
        tableHeader.addGroup(group);

        group 
= new DefaultGroup();
        group.setRow(
1);
        group.setColumn(
2);
        group.setHeaderValue(
"Column A");
        tableHeader.addGroup(group);

        group 
= new DefaultGroup();
        group.setRow(
1);
        group.setColumn(
3);
        group.setHeaderValue(
"Column B");
        tableHeader.addGroup(group);

        group 
= new DefaultGroup();
        group.setRow(
0);
        group.setColumn(
4);
        group.setColumnSpan(
2);
        group.setHeaderValue(
"C & D");
        tableHeader.addGroup(group);

        group 
= new DefaultGroup();
        group.setRow(
1);
        group.setColumn(
4);
        group.setHeaderValue(
"Column C");
        tableHeader.addGroup(group);

        group 
= new DefaultGroup();
        group.setRow(
1);
        group.setColumn(
5);
        group.setHeaderValue(
"Column D");
        tableHeader.addGroup(group);

        getContentPane().add(scroll, BorderLayout.CENTER);
    }

}

OK了, 看看效果把.

你可能感兴趣的:(Swing)