Java Swing写的支持合并单元格的JTable

时间:2023-01-27 10:48:24
 年前在网上参加了一个JavaSwing的招聘上机考试。招聘方要求开发一个类似EXCEL支持单元格合并的JTable。差不多用了5天的时间提交代码,最后被告知测试通过,我提出是否可做兼职,对方回复需要到上海做全职开发,最后也就放弃了。最近公司的一个项目中需要用到以前的代码,偶又重构了一次,设计思想来源于ListSelectionModel。

 

Java Swing写的支持合并单元格的JTable

GridBagModel:抽象模型接口。该接口用于描述表格中单元格的合并状态。
DefaultGridBagTableModel:GridBagModel的默认实现。
GridBagTable:继承自JTable的控制器。通过该类中的方法控制表格单元的合并和拆分。
GridBagTableUI:GridBagTable对应的UI。

TODO:(已合并)行、列的插入,删除操作对应的GridBagModel的修改,不过已留接口。

Java代码 Java Swing写的支持合并单元格的JTable  Java Swing写的支持合并单元格的JTableJava Swing写的支持合并单元格的JTable
  1. package org.dxj.guitools.gridbagtable;   
  2. import java.awt.Component;   
  3. import java.awt.Point;   
  4. import java.awt.Rectangle;   
  5. import java.util.Enumeration;   
  6. import java.util.EventObject;   
  7.   
  8. import javax.swing.DefaultCellEditor;   
  9. import javax.swing.JTable;   
  10. import javax.swing.SwingUtilities;   
  11. import javax.swing.event.TableModelEvent;   
  12. import javax.swing.table.AbstractTableModel;   
  13. import javax.swing.table.TableColumn;   
  14. import javax.swing.table.TableColumnModel;   
  15.   
  16. /**  
  17.  * @author 15860102@qq.com  
  18.  */  
  19. public class GridBagTable extends JTable{   
  20.   
  21.     GridBagModel gridBagModel;   
  22.        
  23.     public GridBagModel getGridBagModel() {   
  24.         return gridBagModel;   
  25.     }   
  26.        
  27.     public void setGridBagModel(GridBagModel gridBagModel){   
  28.         if( gridBagModel != null && gridBagModel != this.gridBagModel )   
  29.             this.gridBagModel = gridBagModel;   
  30.     }   
  31.   
  32.     public GridBagTable(AbstractTableModel dm){   
  33.         super(dm);         
  34.         getTableHeader().setReorderingAllowed(false);   
  35.         gridBagModel = new DefaultGridBagTableModel(dm);           
  36.         getColumnModel().setColumnSelectionAllowed(true);   
  37.     }   
  38.        
  39.      private void updateSubComponentUI(Object componentShell) {   
  40.         if (componentShell == null) {   
  41.             return;   
  42.         }   
  43.         Component component = null;   
  44.         if (componentShell instanceof Component) {   
  45.             component = (Component)componentShell;   
  46.         }   
  47.         if (componentShell instanceof DefaultCellEditor) {   
  48.             component = ((DefaultCellEditor)componentShell).getComponent();   
  49.         }   
  50.   
  51.         if (component != null) {   
  52.             SwingUtilities.updateComponentTreeUI(component);   
  53.         }   
  54.     }   
  55.        
  56.     public void updateUI() {       
  57.         // Update the UIs of the cell renderers, cell editors and header renderers.  
  58.         TableColumnModel cm = getColumnModel();   
  59.         for(int column = 0; column < cm.getColumnCount(); column++) {   
  60.             TableColumn aColumn = cm.getColumn(column);   
  61.         updateSubComponentUI(aColumn.getCellRenderer());   
  62.             updateSubComponentUI(aColumn.getCellEditor());   
  63.         updateSubComponentUI(aColumn.getHeaderRenderer());   
  64.         }   
  65.   
  66.         // Update the UIs of all the default renderers.  
  67.         Enumeration defaultRenderers = defaultRenderersByColumnClass.elements();   
  68.         while (defaultRenderers.hasMoreElements()) {   
  69.             updateSubComponentUI(defaultRenderers.nextElement());   
  70.         }   
  71.   
  72.         // Update the UIs of all the default editors.  
  73.         Enumeration defaultEditors = defaultEditorsByColumnClass.elements();   
  74.         while (defaultEditors.hasMoreElements()) {   
  75.             updateSubComponentUI(defaultEditors.nextElement());   
  76.         }   
  77.   
  78.         // Update the UI of the table header  
  79.         if (tableHeader != null && tableHeader.getParent() == null) {   
  80.             tableHeader.updateUI();   
  81.         }   
  82.         setUI(new GridBagTableUI());   
  83.     }   
  84.        
  85.     public Rectangle getGridCellRect(int row, int column, boolean includeSpacing){   
  86.         return super.getCellRect(row, column, includeSpacing);   
  87.     }   
  88.        
  89.     public Rectangle getCellRect(int row, int column, boolean includeSpacing) {        
  90.         Rectangle cellRect = super.getCellRect(row, column, includeSpacing);   
  91.         int cols = gridBagModel.getColumnGrid(row, column);   
  92.         TableColumnModel cm = getColumnModel();   
  93.         forint n=1; n<cols; n++)   
  94.             cellRect.width += cm.getColumn(column+n).getWidth();   
  95.         int rows = gridBagModel.getRowGrid(row, column);   
  96.         forint n=1; n<rows; n++)   
  97.             cellRect.height += getRowHeight(row+n);   
  98.         return cellRect;            
  99.     }   
  100.        
  101.     public void tableChanged(TableModelEvent e){   
  102.         super.tableChanged(e);   
  103.         //TODO   
  104.     }   
  105.        
  106.     public boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn){   
  107.         if( gridBagModel.mergeCells(startRow, endRow, startColumn, endColumn)){   
  108.             repaint();   
  109.             return true;   
  110.         }      
  111.         return false;   
  112.     }   
  113.         
  114.     public boolean mergeCells(int[] rows, int[] columns){   
  115.         if( gridBagModel.mergeCells(rows, columns)){   
  116.             repaint();   
  117.             return true;   
  118.         }      
  119.         return false;   
  120.     }   
  121.        
  122.     public boolean spliteCellAt(int row, int column){   
  123.         if( gridBagModel.spliteCellAt(row, column)){   
  124.             repaint();   
  125.             return true;   
  126.         }   
  127.         return false;   
  128.     }   
  129.        
  130.     public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {   
  131.         if( gridBagModel.getCellState( rowIndex , columnIndex ) != GridBagModel.COVERED  )   
  132.             super.changeSelection(rowIndex, columnIndex, toggle, extend);   
  133.         Point p;   
  134.         forint row = rowIndex; row >= 0; row-- ){   
  135.             forint col = columnIndex; col >= 0; col-- ){   
  136.                 p = gridBagModel.getGrid(row, col);   
  137.                 //p = ((Point)((Vector)rowVector.get(row)).get(col));  
  138.                 if( col + p.x > columnIndex && row + p.y > rowIndex){   
  139.                     rowIndex = row;   
  140.                     columnIndex = col;   
  141.                     break;   
  142.                 }   
  143.             }   
  144.         }      
  145.         super.changeSelection(rowIndex, columnIndex, toggle, extend);   
  146.         repaint();   
  147.     }   
  148.        
  149.     public boolean editCellAt(int rowIndex, int columnIndex, EventObject e){   
  150.         if( gridBagModel.getCellState( rowIndex , columnIndex ) != GridBagModel.COVERED  )   
  151.             return super.editCellAt(rowIndex, columnIndex, e);      
  152.         Point p;   
  153.         forint row = rowIndex; row >= 0; row-- ){   
  154.             forint col = columnIndex; col >= 0; col-- ){   
  155.                 p = gridBagModel.getGrid(row, col);   
  156.                 if( col + p.x > columnIndex && row + p.y > rowIndex){   
  157.                     rowIndex = row;   
  158.                     columnIndex = col;   
  159.                     break;   
  160.                 }   
  161.             }   
  162.         }          
  163.         return super.editCellAt(rowIndex, columnIndex, e);           
  164.     }   
  165. }  
 
Java代码 Java Swing写的支持合并单元格的JTable  Java Swing写的支持合并单元格的JTableJava Swing写的支持合并单元格的JTable
  1. package org.dxj.guitools.gridbagtable;   
  2.   
  3. import java.awt.Point;   
  4.   
  5. public interface GridBagModel {   
  6.     //格子处于正常状态   
  7.     int DEFAULT = 0;   
  8.     //格子合并了其他的格子   
  9.     int MERGE = 1;   
  10.     //格子被其他格子合并   
  11.     int COVERED = -1;   
  12.        
  13.     /**  
  14.      * @param row 行  
  15.      * @param column 列  
  16.      * @return 该单元格在行、列的跨度  
  17.      */  
  18.     Point getGrid(int row, int column);   
  19.        
  20.     /**  
  21.      * 在Y轴方向的跨度  
  22.      * @param row  
  23.      * @param column  
  24.      * @return  
  25.      */  
  26.     int getRowGrid(int row, int column);   
  27.        
  28.     /**  
  29.      * 在X轴方向的跨度  
  30.      * @param row  
  31.      * @param column  
  32.      * @return  
  33.      */  
  34.     int getColumnGrid(int row, int column);   
  35.   
  36.     /**  
  37.      * @param rows 行集合  
  38.      * @param columns 列集合  
  39.      * @return 单元格集合是否可以合并在一起  
  40.      */  
  41.     boolean canMergeCells(int[] rows, int[] columns);   
  42.        
  43.     /**  
  44.      * 判断该单元格状态  
  45.      * @param row  
  46.      * @param column  
  47.      * @return MERGE|DEFAULT|COVERED  
  48.      */  
  49.     int getCellState(int row, int column);   
  50.        
  51.     /**  
  52.      * 将单元格集合合并  
  53.      * @param startRow 开始行  
  54.      * @param endRow 结束行  
  55.      * @param startColumn 开始列  
  56.      * @param endColumn 结束列  
  57.      * @return 是否合并成功  
  58.      */  
  59.     boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn);   
  60.        
  61.     /**  
  62.      * 将单元格集合合并  
  63.      * @param rows 行集合  
  64.      * @param columns 列集合  
  65.      * @return 是否合并成功  
  66.      */  
  67.     boolean mergeCells(int[] rows, int[] columns);   
  68.        
  69.     /**  
  70.      * 拆分单元格  
  71.      * @param row 行  
  72.      * @param column 列  
  73.      * @return 是否拆分成功  
  74.      */  
  75.     boolean spliteCellAt(int row, int column);   
  76.        
  77.     /**  
  78.      * 清除 所有合并  
  79.      */  
  80.     void clearMergence();   
  81. }  
 
Java代码 Java Swing写的支持合并单元格的JTable  Java Swing写的支持合并单元格的JTableJava Swing写的支持合并单元格的JTable
  1. package org.dxj.guitools.gridbagtable;   
  2.   
  3. import java.awt.Point;   
  4. import java.util.Arrays;   
  5. import java.util.List;   
  6. import java.util.Vector;   
  7.   
  8. import javax.swing.event.TableModelEvent;   
  9. import javax.swing.event.TableModelListener;   
  10. import javax.swing.table.AbstractTableModel;   
  11.   
  12. public class DefaultGridBagTableModel implements GridBagModel, TableModelListener{   
  13.     protected AbstractTableModel model;   
  14.     protected List<List<Point>> gridInfo;   
  15.        
  16.     DefaultGridBagTableModel(AbstractTableModel model){   
  17.         gridInfo = new Vector<List<Point>>();   
  18.         setTableModel(model);   
  19.     }   
  20.        
  21.     public void setTableModel(AbstractTableModel model){   
  22.         if( model != null && model != this.model ){   
  23.             ifthis.model != null )   
  24.                 this.model.removeTableModelListener(this);   
  25.             //防止多次添加监听器   
  26.             model.removeTableModelListener(this);   
  27.             model.addTableModelListener(this);   
  28.             this.model = model;   
  29.             clearMergence();   
  30.         }   
  31.     }   
  32.        
  33.     public void clearMergence(){   
  34.         if( gridInfo == null  )   
  35.             gridInfo = new Vector<List<Point>>();   
  36.         else  
  37.             gridInfo.clear();   
  38.            
  39.         if( model == null )   
  40.             return;   
  41.            
  42.         //初始化,每个格子占的格子数为(1,1);   
  43.         for(int row=model.getRowCount(); --row>=0;){   
  44.             List<Point> infos = new Vector<Point>();   
  45.             gridInfo.add(infos);   
  46.             for(int col=model.getColumnCount(); --col>=0;){   
  47.                 infos.add(getDefaultPoint());   
  48.             }   
  49.         }   
  50.     }   
  51.        
  52.     public Point getDefaultPoint(){   
  53.         return new Point(1,1);   
  54.     }   
  55.        
  56.     @Override  
  57.     public boolean canMergeCells(int[] rows, int[] columns) {   
  58.         if( rows == null || columns == null ) return false;   
  59.         Arrays.sort(rows);   
  60.         for(int index=0; index<rows.length-1; index++){   
  61.             if( rows[index+1] - rows[index] > 1 )   
  62.                 return false;   
  63.         }   
  64.         Arrays.sort(columns);   
  65.         for(int index=0; index<columns.length-1; index++){   
  66.             if( columns[index+1] - columns[index] > 1 )   
  67.                 return false;   
  68.         }   
  69.         return true;   
  70.     }   
  71.        
  72.     @Override  
  73.     public int getCellState(int row, int column) {   
  74.         Point grid = getGrid(row, column);   
  75.         if( grid == null ) return DEFAULT;   
  76.         if( grid.x>1 || grid.y>1 )   
  77.             return MERGE;   
  78.         if( grid.x<=0 || grid.y<=0 )   
  79.             return COVERED;   
  80.         return DEFAULT;   
  81.     }   
  82.   
  83.     @Override  
  84.     public int getColumnGrid(int row, int column) {   
  85.         if( gridInfo != null && row >=0 && row < gridInfo.size() ){   
  86.             List<Point> gridRow = gridInfo.get(row);   
  87.             if( gridRow != null && column >=0 && column < gridRow.size() ){   
  88.                 Point point = gridRow.get(column);   
  89.                 if( point != null )   
  90.                     return point.x;   
  91.             }      
  92.         }   
  93.         return 1;   
  94.     }   
  95.   
  96.     @Override  
  97.     public Point getGrid(int row, int column) {   
  98.         if( gridInfo != null && row >=0 && row < gridInfo.size() ){   
  99.             List<Point> gridRow = gridInfo.get(row);   
  100.             if( gridRow != null && column >=0 && column < gridRow.size() ){   
  101.                 return gridRow.get(column);   
  102.             }      
  103.         }   
  104.         return getDefaultPoint();   
  105.     }   
  106.   
  107.     @Override  
  108.     public int getRowGrid(int row, int column) {   
  109.         if( gridInfo != null && row >=0 && row < gridInfo.size() ){   
  110.             List<Point> gridRow = gridInfo.get(row);   
  111.             if( gridRow != null && column >=0 && column < gridRow.size() ){   
  112.                 Point point = gridRow.get(column);   
  113.                 if( point != null )   
  114.                     return point.y;   
  115.             }      
  116.         }   
  117.         return 1;   
  118.     }   
  119.   
  120.     protected boolean setGrid(int row, int column, Point grid) {   
  121.         if( gridInfo != null && row >=0 && row < gridInfo.size() ){   
  122.             List<Point> gridRow = gridInfo.get(row);   
  123.             if( gridRow != null && column >=0 && column < gridRow.size() ){   
  124.                 Point point = gridRow.get(column);   
  125.                 if( point != null ){   
  126.                     point.setLocation(grid);   
  127.                 }   
  128.                 else{   
  129.                     gridRow.set(column, grid.getLocation());   
  130.                 }   
  131.                 return true;   
  132.             }      
  133.         }   
  134.         return false;   
  135.     }   
  136.   
  137.     @Override  
  138.     public boolean spliteCellAt(int row, int column) {   
  139.         if( gridInfo != null && row >=0 && row < gridInfo.size() ){   
  140.             List<Point> gridRow = gridInfo.get(row);   
  141.             if( gridRow != null && column >=0 && column < gridRow.size() ){   
  142.                 Point point = gridRow.get(column);   
  143.                 if( point != null ){   
  144.                     point = point.getLocation();   
  145.                     for(int a=0; a<point.y; a++){   
  146.                         for(int b=0; b<point.x; b++){   
  147.                             setGrid(row+a, column+b, getDefaultPoint());   
  148.                         }   
  149.                     }   
  150.                 }   
  151.                 else{   
  152.                     gridRow.set(column, getDefaultPoint());   
  153.                 }   
  154.                 return true;   
  155.             }      
  156.         }   
  157.         return false;   
  158.     }   
  159.        
  160.     @Override  
  161.     /**  
  162.      * table中发生行的添加和删除的时候需要修改该模型  
  163.      */  
  164.     public void tableChanged(TableModelEvent e) {   
  165.         //TODO   
  166.     }   
  167.        
  168.     @Override  
  169.     public boolean mergeCells(int[] rows, int[] columns) {   
  170.         if( !canMergeCells(rows, columns) )   
  171.             return false;   
  172.         Arrays.sort(rows);   
  173.         Arrays.sort(columns);   
  174.         return mergeCells(rows[0],rows[rows.length-1],columns[0],columns[columns.length-1]);   
  175.     }   
  176.   
  177.     @Override  
  178.     public boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn) {   
  179.         setGrid(startRow, startColumn, new Point(endColumn-startColumn+1, endRow-startRow+1));    
  180.         for(int row=startRow; row<=endRow; row++){   
  181.             for(int col=startColumn; col<=endColumn; col++){   
  182.                 if(row==startRow&&col==startColumn)   
  183.                     continue;   
  184.                 else  
  185.                     setGrid(row, col, new Point(COVERED,COVERED));    
  186.             }   
  187.         }   
  188.         return true;   
  189.     }   
  190.        
  191.     public String toString(){   
  192.         if( gridInfo == null )   
  193.             return "";   
  194.         StringBuffer sb = new StringBuffer();   
  195.         for(List<Point> rowInfo : gridInfo ){   
  196.             for(Point grid : rowInfo){   
  197.                 sb.append("["+grid.x+","+grid.y+"], ");   
  198.             }   
  199.             sb.append("\n");   
  200.         }   
  201.         return sb.toString();   
  202.     }   
  203. }  
 
Java代码 Java Swing写的支持合并单元格的JTable  Java Swing写的支持合并单元格的JTableJava Swing写的支持合并单元格的JTable
  1. package org.dxj.guitools.gridbagtable;   
  2.   
  3. import java.awt.Color;   
  4. import java.awt.Component;   
  5. import java.awt.Dimension;   
  6. import java.awt.Graphics;   
  7. import java.awt.Point;   
  8. import java.awt.Rectangle;   
  9. import java.util.Enumeration;   
  10.   
  11. import javax.swing.BorderFactory;   
  12. import javax.swing.JComponent;   
  13. import javax.swing.JTable;   
  14. import javax.swing.UIManager;   
  15. import javax.swing.plaf.basic.BasicTableUI;   
  16. import javax.swing.table.JTableHeader;   
  17. import javax.swing.table.TableCellRenderer;   
  18. import javax.swing.table.TableColumn;   
  19. import javax.swing.table.TableColumnModel;   
  20.   
  21. public class GridBagTableUI extends BasicTableUI   
  22. {   
  23.     public Dimension getPreferredSize(JComponent c) {   
  24.         long width = 0;   
  25.         Enumeration<TableColumn> enumeration = table.getColumnModel().getColumns();   
  26.         while (enumeration.hasMoreElements()) {   
  27.             TableColumn aColumn = (TableColumn)enumeration.nextElement();   
  28.             width = width + aColumn.getPreferredWidth();   
  29.         }   
  30.         return createTableSize(width);   
  31.     }   
  32.        
  33.     private Dimension createTableSize(long width) {   
  34.         int height = 0;   
  35.         int rowCount = table.getRowCount();   
  36.         if (rowCount > 0 && table.getColumnCount() > 0) {   
  37.             Rectangle r = table.getCellRect(rowCount-10true);   
  38.             height = r.y + r.height;   
  39.         }   
  40.         // Width is always positive. The call to abs() is a workaround for  
  41.         // a bug in the 1.1.6 JIT on Windows.  
  42.         long tmp = Math.abs(width);   
  43.             if (tmp > Integer.MAX_VALUE) {   
  44.                 tmp = Integer.MAX_VALUE;   
  45.             }   
  46.         return new Dimension((int)tmp, height);   
  47.         }   
  48.        
  49.     public void paint(Graphics g, JComponent c) {   
  50.         Rectangle clip = g.getClipBounds();   
  51.   
  52.         Rectangle bounds = table.getBounds();   
  53.         // account for the fact that the graphics has already been translated  
  54.         // into the table's bounds   
  55.         bounds.x = bounds.y = 0;   
  56.   
  57.     if (table.getRowCount() <= 0 || table.getColumnCount() <= 0 ||   
  58.                 // this check prevents us from painting the entire table  
  59.                 // when the clip doesn't intersect our bounds at all  
  60.                 !bounds.intersects(clip)) {   
  61.   
  62.             paintDropLines(g);   
  63.         return;   
  64.     }   
  65.   
  66.         boolean ltr = table.getComponentOrientation().isLeftToRight();   
  67.   
  68.     Point upperLeft = clip.getLocation();   
  69.         if (!ltr) {   
  70.             upperLeft.x++;   
  71.         }   
  72.   
  73.     Point lowerRight = new Point(clip.x + clip.width - (ltr ? 1 : 0),   
  74.                                      clip.y + clip.height);   
  75.   
  76.         int rMin = table.rowAtPoint(upperLeft);   
  77.         int rMax = table.rowAtPoint(lowerRight);   
  78.         // This should never happen (as long as our bounds intersect the clip,  
  79.         // which is why we bail above if that is the case).  
  80.         if (rMin == -1) {   
  81.         rMin = 0;   
  82.         }   
  83.         // If the table does not have enough rows to fill the view we'll get -1.  
  84.         // (We could also get -1 if our bounds don't intersect the clip,  
  85.         // which is why we bail above if that is the case).  
  86.         // Replace this with the index of the last row.  
  87.         if (rMax == -1) {   
  88.         rMax = table.getRowCount()-1;   
  89.         }   
  90.   
  91.         int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight);    
  92.         int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft);           
  93.         // This should never happen.   
  94.         if (cMin == -1) {   
  95.         cMin = 0;   
  96.         }   
  97.     // If the table does not have enough columns to fill the view we'll get -1.  
  98.         // Replace this with the index of the last column.  
  99.         if (cMax == -1) {   
  100.         cMax = table.getColumnCount()-1;   
  101.         }   
  102.   
  103.         // Paint the grid.   
  104.         //paintGrid(g, rMin, rMax, cMin, cMax);  
  105.   
  106.         // Paint the cells.   
  107.     paintCells(g, rMin, rMax, cMin, cMax);   
  108.   
  109.         paintDropLines(g);   
  110.     }   
  111.        
  112.     private void paintDropLines(Graphics g) {   
  113.         JTable.DropLocation loc = table.getDropLocation();   
  114.         if (loc == null) {   
  115.             return;   
  116.         }   
  117.   
  118.         Color color = UIManager.getColor("Table.dropLineColor");   
  119.         Color shortColor = UIManager.getColor("Table.dropLineShortColor");   
  120.         if (color == null && shortColor == null) {   
  121.             return;   
  122.         }   
  123.   
  124.         Rectangle rect;   
  125.   
  126.         rect = getHDropLineRect(loc);   
  127.         if (rect != null) {   
  128.             int x = rect.x;   
  129.             int w = rect.width;   
  130.             if (color != null) {   
  131.                 extendRect(rect, true);   
  132.                 g.setColor(color);   
  133.                 g.fillRect(rect.x, rect.y, rect.width, rect.height);   
  134.             }   
  135.             if (!loc.isInsertColumn() && shortColor != null) {   
  136.                 g.setColor(shortColor);   
  137.                 g.fillRect(x, rect.y, w, rect.height);   
  138.             }   
  139.         }   
  140.   
  141.         rect = getVDropLineRect(loc);   
  142.         if (rect != null) {   
  143.             int y = rect.y;   
  144.             int h = rect.height;   
  145.             if (color != null) {   
  146.                 extendRect(rect, false);   
  147.                 g.setColor(color);   
  148.                 g.fillRect(rect.x, rect.y, rect.width, rect.height);   
  149.             }   
  150.             if (!loc.isInsertRow() && shortColor != null) {   
  151.                 g.setColor(shortColor);   
  152.                 g.fillRect(rect.x, y, rect.width, h);   
  153.             }   
  154.         }   
  155.     }   
  156.        
  157.     /*  
  158.      * Paints the grid lines within <I>aRect</I>, using the grid 
  159.      * color set with <I>setGridColor</I>. Paints vertical lines 
  160.      * if <code>getShowVerticalLines()</code> returns true and paints 
  161.      * horizontal lines if <code>getShowHorizontalLines()</code> 
  162.      * returns true.  
  163.      */  
  164.     private void paintGrid(Graphics g, int rMin, int rMax, int cMin, int cMax) {   
  165.         g.setColor(table.getGridColor());   
  166.   
  167.         Rectangle minCell = table.getCellRect(rMin, cMin, true);   
  168.         Rectangle maxCell = table.getCellRect(rMax, cMax, true);   
  169.         Rectangle damagedArea = minCell.union( maxCell );   
  170.   
  171.         if (table.getShowHorizontalLines()) {   
  172.             int tableWidth = damagedArea.x + damagedArea.width;   
  173.             int y = damagedArea.y;   
  174.             for (int row = rMin; row <= rMax; row++) {   
  175.                 y += table.getRowHeight(row);   
  176.                 g.drawLine(damagedArea.x, y - 1, tableWidth - 1, y - 1);   
  177.             }   
  178.         }   
  179.         if (table.getShowVerticalLines()) {   
  180.             TableColumnModel cm = table.getColumnModel();   
  181.             int tableHeight = damagedArea.y + damagedArea.height;   
  182.             int x;   
  183.             if (table.getComponentOrientation().isLeftToRight()) {   
  184.                 x = damagedArea.x;   
  185.                 for (int column = cMin; column <= cMax; column++) {   
  186.                     int w = cm.getColumn(column).getWidth();   
  187.                     x += w;   
  188.                     g.drawLine(x - 10, x - 1, tableHeight - 1);   
  189.                 }   
  190.             } else {   
  191.                 x = damagedArea.x;   
  192.                 for (int column = cMax; column >= cMin; column--) {   
  193.                     int w = cm.getColumn(column).getWidth();   
  194.                     x += w;   
  195.                     g.drawLine(x - 10, x - 1, tableHeight - 1);   
  196.                 }   
  197.             }   
  198.         }   
  199.     }   
  200.        
  201.     private void paintCells(Graphics g, int rMin, int rMax, int cMin, int cMax) {   
  202.         JTableHeader header = table.getTableHeader();   
  203.         TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn();   
  204.   
  205.         TableColumnModel cm = table.getColumnModel();   
  206.         int columnMargin = cm.getColumnMargin();   
  207.             Rectangle cellRect;   
  208.         TableColumn aColumn;   
  209.         int columnWidth;   
  210.         if (table.getComponentOrientation().isLeftToRight()) {   
  211.             for(int row = rMin; row <= rMax; row++) {   
  212.                 if( table instanceof GridBagTable )   
  213.                     cellRect = ((GridBagTable)table).getGridCellRect(row, cMin, false);   
  214.                 else  
  215.                     cellRect = table.getCellRect(row, cMin, false);   
  216.                 for(int column = cMin; column <= cMax; column++) {   
  217.                     aColumn = cm.getColumn(column);   
  218.                     columnWidth = aColumn.getWidth();                                    
  219.                     //TODO   
  220.                     cellRect.width = columnWidth - columnMargin;   
  221.                     int oldHeight = cellRect.height;   
  222.                     if( table instanceof GridBagTable ){   
  223.                         if(((GridBagTable)table).getGridBagModel().getCellState( row, column) == GridBagModel.COVERED ) {   
  224.                             cellRect.width = 0;   
  225.                             cellRect.height = 0;   
  226.                         }   
  227.                         else{   
  228.                             int h = ((GridBagTable)table).getGridBagModel().getColumnGrid(row, column);   
  229.                             if( h >1 ){   
  230.                                 forint n=1; n<h; n++)   
  231.                                     cellRect.width += cm.getColumn(column+n).getWidth();   
  232.                             }   
  233.                             int v = ((GridBagTable)table).getGridBagModel().getRowGrid(row, column);   
  234.                             if( v >1 ){   
  235.                                 forint n=1; n<v; n++)   
  236.                                     cellRect.height += table.getRowHeight(row+n);   
  237.                             }   
  238.                         }              
  239.                     }                                                                          
  240.                     if (aColumn != draggedColumn) {   
  241.                         paintCell(g, cellRect, row, column);   
  242.                     }                          
  243.                     cellRect.height = oldHeight;                       
  244.                     cellRect.x += columnWidth;   
  245.                 }   
  246.             }   
  247.         } else {   
  248.             for(int row = rMin; row <= rMax; row++) {   
  249.                     cellRect = table.getCellRect(row, cMin, false);   
  250.                     aColumn = cm.getColumn(cMin);   
  251.                     if (aColumn != draggedColumn) {   
  252.                         columnWidth = aColumn.getWidth();   
  253.                         cellRect.width = columnWidth - columnMargin;   
  254.                         paintCell(g, cellRect, row, cMin);   
  255.                     }   
  256.                     for(int column = cMin+1; column <= cMax; column++) {   
  257.                         aColumn = cm.getColumn(column);   
  258.                         columnWidth = aColumn.getWidth();   
  259. //                      TODO   
  260.                         cellRect.width = columnWidth - columnMargin;   
  261.                         cellRect.x -= columnWidth;   
  262.                         if (aColumn != draggedColumn) {   
  263.                             paintCell(g, cellRect, row, column);   
  264.                         }   
  265.                 }   
  266.             }   
  267.         }   
  268.   
  269.             // Paint the dragged column if we are dragging.  
  270.             if (draggedColumn != null) {   
  271.             paintDraggedArea(g, rMin, rMax, draggedColumn, header.getDraggedDistance());   
  272.         }   
  273.   
  274.         // Remove any renderers that may be left in the rendererPane.  
  275.         rendererPane.removeAll();   
  276.         }   
  277.        
  278.     private void paintCell(Graphics g, Rectangle cellRect, int row, int column) {   
  279.         if (table.isEditing() && table.getEditingRow()==row &&   
  280.                                  table.getEditingColumn()==column) {   
  281.             Component component = table.getEditorComponent();   
  282.         component.setBounds(cellRect);   
  283.             component.validate();   
  284.         }   
  285.         else {   
  286.             TableCellRenderer renderer = table.getCellRenderer(row, column);   
  287.             Component component = table.prepareRenderer(renderer, row, column);   
  288.                
  289.             if( component instanceof JComponent ){   
  290.                 ((JComponent)component).setBorder(BorderFactory.createLineBorder(Color.gray));   
  291.             }   
  292.             rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y,   
  293.                                         cellRect.width, cellRect.height, true);              
  294.         }   
  295.     }   
  296.        
  297.     private Rectangle getHDropLineRect(JTable.DropLocation loc) {   
  298.         if (!loc.isInsertRow()) {   
  299.             return null;   
  300.         }   
  301.   
  302.         int row = loc.getRow();   
  303.         int col = loc.getColumn();   
  304.         if (col >= table.getColumnCount()) {   
  305.             col--;   
  306.         }   
  307.   
  308.         Rectangle rect = table.getCellRect(row, col, true);   
  309.   
  310.         if (row >= table.getRowCount()) {   
  311.             row--;   
  312.             Rectangle prevRect = table.getCellRect(row, col, true);   
  313.             rect.y = prevRect.y + prevRect.height;   
  314.         }   
  315.   
  316.         if (rect.y == 0) {   
  317.             rect.y = -1;   
  318.         } else {   
  319.             rect.y -= 2;   
  320.         }   
  321.   
  322.         rect.height = 3;   
  323.   
  324.         return rect;   
  325.     }   
  326.        
  327.     private void paintDraggedArea(Graphics g, int rMin, int rMax, TableColumn draggedColumn, int distance) {   
  328.         int draggedColumnIndex = viewIndexForColumn(draggedColumn);   
  329.   
  330.         Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true);   
  331.     Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true);   
  332.   
  333.     Rectangle vacatedColumnRect = minCell.union(maxCell);   
  334.   
  335.     // Paint a gray well in place of the moving column.  
  336.     g.setColor(table.getParent().getBackground());   
  337.     g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,   
  338.            vacatedColumnRect.width, vacatedColumnRect.height);   
  339.   
  340.     // Move to the where the cell has been dragged.  
  341.     vacatedColumnRect.x += distance;   
  342.   
  343.     // Fill the background.   
  344.     g.setColor(table.getBackground());   
  345.     g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,   
  346.            vacatedColumnRect.width, vacatedColumnRect.height);   
  347.   
  348.     // Paint the vertical grid lines if necessary.  
  349.     if (table.getShowVerticalLines()) {   
  350.         g.setColor(table.getGridColor());   
  351.         int x1 = vacatedColumnRect.x;   
  352.         int y1 = vacatedColumnRect.y;   
  353.         int x2 = x1 + vacatedColumnRect.width - 1;   
  354.         int y2 = y1 + vacatedColumnRect.height - 1;   
  355.         // Left   
  356.         g.drawLine(x1-1, y1, x1-1, y2);   
  357.         // Right   
  358.         g.drawLine(x2, y1, x2, y2);   
  359.     }   
  360.   
  361.     for(int row = rMin; row <= rMax; row++) {   
  362.         // Render the cell value   
  363.         Rectangle r = table.getCellRect(row, draggedColumnIndex, false);   
  364.         r.x += distance;   
  365.         paintCell(g, r, row, draggedColumnIndex);   
  366.   
  367.         // Paint the (lower) horizontal grid line if necessary.  
  368.         if (table.getShowHorizontalLines()) {   
  369.         g.setColor(table.getGridColor());   
  370.         Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true);   
  371.         rcr.x += distance;   
  372.         int x1 = rcr.x;   
  373.         int y1 = rcr.y;   
  374.         int x2 = x1 + rcr.width - 1;   
  375.         int y2 = y1 + rcr.height - 1;   
  376.         g.drawLine(x1, y2, x2, y2);   
  377.         }   
  378.     }   
  379.     }   
  380.        
  381.     private int viewIndexForColumn(TableColumn aColumn) {   
  382.         TableColumnModel cm = table.getColumnModel();   
  383.         for (int column = 0; column < cm.getColumnCount(); column++) {   
  384.             if (cm.getColumn(column) == aColumn) {   
  385.                 return column;   
  386.             }   
  387.         }   
  388.         return -1;   
  389.     }   
  390.        
  391.     private Rectangle extendRect(Rectangle rect, boolean horizontal) {   
  392.         if (rect == null) {   
  393.             return rect;   
  394.         }   
  395.   
  396.         if (horizontal) {   
  397.             rect.x = 0;   
  398.             rect.width = table.getWidth();   
  399.         } else {   
  400.             rect.y = 0;   
  401.   
  402.             if (table.getRowCount() != 0) {   
  403.                 Rectangle lastRect = table.getCellRect(table.getRowCount() - 10true);   
  404.                 rect.height = lastRect.y + lastRect.height;   
  405.             } else {   
  406.                 rect.height = table.getHeight();   
  407.             }   
  408.         }   
  409.   
  410.         return rect;   
  411.     }   
  412.        
  413.     private Rectangle getVDropLineRect(JTable.DropLocation loc) {   
  414.         if (!loc.isInsertColumn()) {   
  415.             return null;   
  416.         }   
  417.   
  418.         boolean ltr = table.getComponentOrientation().isLeftToRight();   
  419.         int col = loc.getColumn();   
  420.         Rectangle rect = table.getCellRect(loc.getRow(), col, true);   
  421.   
  422.         if (col >= table.getColumnCount()) {   
  423.             col--;   
  424.             rect = table.getCellRect(loc.getRow(), col, true);   
  425.             if (ltr) {   
  426.                 rect.x = rect.x + rect.width;   
  427.             }   
  428.         } else if (!ltr) {   
  429.             rect.x = rect.x + rect.width;   
  430.         }   
  431.   
  432.         if (rect.x == 0) {   
  433.             rect.x = -1;   
  434.         } else {   
  435.             rect.x -= 2;   
  436.         }   
  437.            
  438.         rect.width = 3;   
  439.   
  440.         return rect;   
  441.     }   
  442.   
  443. }  // End of Class BasicTableUI  

  测试代码:

Java代码 Java Swing写的支持合并单元格的JTable  Java Swing写的支持合并单元格的JTableJava Swing写的支持合并单元格的JTable
  1. import java.awt.BorderLayout;   
  2. import java.awt.event.ActionEvent;   
  3. import java.awt.event.ActionListener;   
  4.   
  5. import javax.swing.JButton;   
  6. import javax.swing.JFrame;   
  7. import javax.swing.JScrollPane;   
  8. import javax.swing.table.DefaultTableModel;   
  9.   
  10. import com.jrf.jgrid.guitools.gridbagtable.GridBagTable;   
  11.   
  12.   
  13. public class Test implements ActionListener{   
  14.        
  15.     GridBagTable table;   
  16.     public Test()   
  17.     {   
  18.         JFrame d = new JFrame();   
  19.         DefaultTableModel model = new DefaultTableModel(5,5);   
  20.            
  21.         table = new GridBagTable(model);   
  22.         table.setRowHeight(20);   
  23.            
  24.         JScrollPane pane = new JScrollPane(table);   
  25.         d.getContentPane().add(pane, BorderLayout.CENTER);   
  26.         JButton btn = new JButton("合并/拆分");   
  27.         d.getContentPane().add(btn, BorderLayout.NORTH);   
  28.         btn.addActionListener(this);   
  29.         d.setBounds(00400400);   
  30.         d.setVisible(true);   
  31.     }   
  32.        
  33.     public static void main(String[] fsd){   
  34.         new Test();   
  35.     }   
  36.        
  37.     public void actionPerformed(ActionEvent e) {   
  38.         table.mergeCells(table.getSelectedRows(), table.getSelectedColumns());   
  39.     }   
  40. }  年前在网上参加了一个JavaSwing的招聘上机考试。招聘方要求开发一个类似EXCEL支持单元格合并的JTable。差不多用了5天的时间提交代码,最后被告知测试通过,我提出是否可做兼职,对方回复需要到上海做全职开发,最后也就放弃了。最近公司的一个项目中需要用到以前的代码,偶又重构了一次,设计思想来源于ListSelectionModel。

     

    Java Swing写的支持合并单元格的JTable

    GridBagModel:抽象模型接口。该接口用于描述表格中单元格的合并状态。
    DefaultGridBagTableModel:GridBagModel的默认实现。
    GridBagTable:继承自JTable的控制器。通过该类中的方法控制表格单元的合并和拆分。
    GridBagTableUI:GridBagTable对应的UI。

    TODO:(已合并)行、列的插入,删除操作对应的GridBagModel的修改,不过已留接口。

    Java代码 Java Swing写的支持合并单元格的JTable  Java Swing写的支持合并单元格的JTableJava Swing写的支持合并单元格的JTable
    1. package org.dxj.guitools.gridbagtable;   
    2. import java.awt.Component;   
    3. import java.awt.Point;   
    4. import java.awt.Rectangle;   
    5. import java.util.Enumeration;   
    6. import java.util.EventObject;   
    7.   
    8. import javax.swing.DefaultCellEditor;   
    9. import javax.swing.JTable;   
    10. import javax.swing.SwingUtilities;   
    11. import javax.swing.event.TableModelEvent;   
    12. import javax.swing.table.AbstractTableModel;   
    13. import javax.swing.table.TableColumn;   
    14. import javax.swing.table.TableColumnModel;   
    15.   
    16. /**  
    17.  * @author 15860102@qq.com  
    18.  */  
    19. public class GridBagTable extends JTable{   
    20.   
    21.     GridBagModel gridBagModel;   
    22.        
    23.     public GridBagModel getGridBagModel() {   
    24.         return gridBagModel;   
    25.     }   
    26.        
    27.     public void setGridBagModel(GridBagModel gridBagModel){   
    28.         if( gridBagModel != null && gridBagModel != this.gridBagModel )   
    29.             this.gridBagModel = gridBagModel;   
    30.     }   
    31.   
    32.     public GridBagTable(AbstractTableModel dm){   
    33.         super(dm);         
    34.         getTableHeader().setReorderingAllowed(false);   
    35.         gridBagModel = new DefaultGridBagTableModel(dm);           
    36.         getColumnModel().setColumnSelectionAllowed(true);   
    37.     }   
    38.        
    39.      private void updateSubComponentUI(Object componentShell) {   
    40.         if (componentShell == null) {   
    41.             return;   
    42.         }   
    43.         Component component = null;   
    44.         if (componentShell instanceof Component) {   
    45.             component = (Component)componentShell;   
    46.         }   
    47.         if (componentShell instanceof DefaultCellEditor) {   
    48.             component = ((DefaultCellEditor)componentShell).getComponent();   
    49.         }   
    50.   
    51.         if (component != null) {   
    52.             SwingUtilities.updateComponentTreeUI(component);   
    53.         }   
    54.     }   
    55.        
    56.     public void updateUI() {       
    57.         // Update the UIs of the cell renderers, cell editors and header renderers.  
    58.         TableColumnModel cm = getColumnModel();   
    59.         for(int column = 0; column < cm.getColumnCount(); column++) {   
    60.             TableColumn aColumn = cm.getColumn(column);   
    61.         updateSubComponentUI(aColumn.getCellRenderer());   
    62.             updateSubComponentUI(aColumn.getCellEditor());   
    63.         updateSubComponentUI(aColumn.getHeaderRenderer());   
    64.         }   
    65.   
    66.         // Update the UIs of all the default renderers.  
    67.         Enumeration defaultRenderers = defaultRenderersByColumnClass.elements();   
    68.         while (defaultRenderers.hasMoreElements()) {   
    69.             updateSubComponentUI(defaultRenderers.nextElement());   
    70.         }   
    71.   
    72.         // Update the UIs of all the default editors.  
    73.         Enumeration defaultEditors = defaultEditorsByColumnClass.elements();   
    74.         while (defaultEditors.hasMoreElements()) {   
    75.             updateSubComponentUI(defaultEditors.nextElement());   
    76.         }   
    77.   
    78.         // Update the UI of the table header  
    79.         if (tableHeader != null && tableHeader.getParent() == null) {   
    80.             tableHeader.updateUI();   
    81.         }   
    82.         setUI(new GridBagTableUI());   
    83.     }   
    84.        
    85.     public Rectangle getGridCellRect(int row, int column, boolean includeSpacing){   
    86.         return super.getCellRect(row, column, includeSpacing);   
    87.     }   
    88.        
    89.     public Rectangle getCellRect(int row, int column, boolean includeSpacing) {        
    90.         Rectangle cellRect = super.getCellRect(row, column, includeSpacing);   
    91.         int cols = gridBagModel.getColumnGrid(row, column);   
    92.         TableColumnModel cm = getColumnModel();   
    93.         forint n=1; n<cols; n++)   
    94.             cellRect.width += cm.getColumn(column+n).getWidth();   
    95.         int rows = gridBagModel.getRowGrid(row, column);   
    96.         forint n=1; n<rows; n++)   
    97.             cellRect.height += getRowHeight(row+n);   
    98.         return cellRect;            
    99.     }   
    100.        
    101.     public void tableChanged(TableModelEvent e){   
    102.         super.tableChanged(e);   
    103.         //TODO   
    104.     }   
    105.        
    106.     public boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn){   
    107.         if( gridBagModel.mergeCells(startRow, endRow, startColumn, endColumn)){   
    108.             repaint();   
    109.             return true;   
    110.         }      
    111.         return false;   
    112.     }   
    113.         
    114.     public boolean mergeCells(int[] rows, int[] columns){   
    115.         if( gridBagModel.mergeCells(rows, columns)){   
    116.             repaint();   
    117.             return true;   
    118.         }      
    119.         return false;   
    120.     }   
    121.        
    122.     public boolean spliteCellAt(int row, int column){   
    123.         if( gridBagModel.spliteCellAt(row, column)){   
    124.             repaint();   
    125.             return true;   
    126.         }   
    127.         return false;   
    128.     }   
    129.        
    130.     public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {   
    131.         if( gridBagModel.getCellState( rowIndex , columnIndex ) != GridBagModel.COVERED  )   
    132.             super.changeSelection(rowIndex, columnIndex, toggle, extend);   
    133.         Point p;   
    134.         forint row = rowIndex; row >= 0; row-- ){   
    135.             forint col = columnIndex; col >= 0; col-- ){   
    136.                 p = gridBagModel.getGrid(row, col);   
    137.                 //p = ((Point)((Vector)rowVector.get(row)).get(col));  
    138.                 if( col + p.x > columnIndex && row + p.y > rowIndex){   
    139.                     rowIndex = row;   
    140.                     columnIndex = col;   
    141.                     break;   
    142.                 }   
    143.             }   
    144.         }      
    145.         super.changeSelection(rowIndex, columnIndex, toggle, extend);   
    146.         repaint();   
    147.     }   
    148.        
    149.     public boolean editCellAt(int rowIndex, int columnIndex, EventObject e){   
    150.         if( gridBagModel.getCellState( rowIndex , columnIndex ) != GridBagModel.COVERED  )   
    151.             return super.editCellAt(rowIndex, columnIndex, e);      
    152.         Point p;   
    153.         forint row = rowIndex; row >= 0; row-- ){   
    154.             forint col = columnIndex; col >= 0; col-- ){   
    155.                 p = gridBagModel.getGrid(row, col);   
    156.                 if( col + p.x > columnIndex && row + p.y > rowIndex){   
    157.                     rowIndex = row;   
    158.                     columnIndex = col;   
    159.                     break;   
    160.                 }   
    161.             }   
    162.         }          
    163.         return super.editCellAt(rowIndex, columnIndex, e);           
    164.     }   
    165. }  
     
    Java代码 Java Swing写的支持合并单元格的JTable  Java Swing写的支持合并单元格的JTableJava Swing写的支持合并单元格的JTable
    1. package org.dxj.guitools.gridbagtable;   
    2.   
    3. import java.awt.Point;   
    4.   
    5. public interface GridBagModel {   
    6.     //格子处于正常状态   
    7.     int DEFAULT = 0;   
    8.     //格子合并了其他的格子   
    9.     int MERGE = 1;   
    10.     //格子被其他格子合并   
    11.     int COVERED = -1;   
    12.        
    13.     /**  
    14.      * @param row 行  
    15.      * @param column 列  
    16.      * @return 该单元格在行、列的跨度  
    17.      */  
    18.     Point getGrid(int row, int column);   
    19.        
    20.     /**  
    21.      * 在Y轴方向的跨度  
    22.      * @param row  
    23.      * @param column  
    24.      * @return  
    25.      */  
    26.     int getRowGrid(int row, int column);   
    27.        
    28.     /**  
    29.      * 在X轴方向的跨度  
    30.      * @param row  
    31.      * @param column  
    32.      * @return  
    33.      */  
    34.     int getColumnGrid(int row, int column);   
    35.   
    36.     /**  
    37.      * @param rows 行集合  
    38.      * @param columns 列集合  
    39.      * @return 单元格集合是否可以合并在一起  
    40.      */  
    41.     boolean canMergeCells(int[] rows, int[] columns);   
    42.        
    43.     /**  
    44.      * 判断该单元格状态  
    45.      * @param row  
    46.      * @param column  
    47.      * @return MERGE|DEFAULT|COVERED  
    48.      */  
    49.     int getCellState(int row, int column);   
    50.        
    51.     /**  
    52.      * 将单元格集合合并  
    53.      * @param startRow 开始行  
    54.      * @param endRow 结束行  
    55.      * @param startColumn 开始列  
    56.      * @param endColumn 结束列  
    57.      * @return 是否合并成功  
    58.      */  
    59.     boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn);   
    60.        
    61.     /**  
    62.      * 将单元格集合合并  
    63.      * @param rows 行集合  
    64.      * @param columns 列集合  
    65.      * @return 是否合并成功  
    66.      */  
    67.     boolean mergeCells(int[] rows, int[] columns);   
    68.        
    69.     /**  
    70.      * 拆分单元格  
    71.      * @param row 行  
    72.      * @param column 列  
    73.      * @return 是否拆分成功  
    74.      */  
    75.     boolean spliteCellAt(int row, int column);   
    76.        
    77.     /**  
    78.      * 清除 所有合并  
    79.      */  
    80.     void clearMergence();   
    81. }  
     
    Java代码 Java Swing写的支持合并单元格的JTable  Java Swing写的支持合并单元格的JTableJava Swing写的支持合并单元格的JTable
    1. package org.dxj.guitools.gridbagtable;   
    2.   
    3. import java.awt.Point;   
    4. import java.util.Arrays;   
    5. import java.util.List;   
    6. import java.util.Vector;   
    7.   
    8. import javax.swing.event.TableModelEvent;   
    9. import javax.swing.event.TableModelListener;   
    10. import javax.swing.table.AbstractTableModel;   
    11.   
    12. public class DefaultGridBagTableModel implements GridBagModel, TableModelListener{   
    13.     protected AbstractTableModel model;   
    14.     protected List<List<Point>> gridInfo;   
    15.        
    16.     DefaultGridBagTableModel(AbstractTableModel model){   
    17.         gridInfo = new Vector<List<Point>>();   
    18.         setTableModel(model);   
    19.     }   
    20.        
    21.     public void setTableModel(AbstractTableModel model){   
    22.         if( model != null && model != this.model ){   
    23.             ifthis.model != null )   
    24.                 this.model.removeTableModelListener(this);   
    25.             //防止多次添加监听器   
    26.             model.removeTableModelListener(this);   
    27.             model.addTableModelListener(this);   
    28.             this.model = model;   
    29.             clearMergence();   
    30.         }   
    31.     }   
    32.        
    33.     public void clearMergence(){   
    34.         if( gridInfo == null  )   
    35.             gridInfo = new Vector<List<Point>>();   
    36.         else  
    37.             gridInfo.clear();   
    38.            
    39.         if( model == null )   
    40.             return;   
    41.            
    42.         //初始化,每个格子占的格子数为(1,1);   
    43.         for(int row=model.getRowCount(); --row>=0;){   
    44.             List<Point> infos = new Vector<Point>();   
    45.             gridInfo.add(infos);   
    46.             for(int col=model.getColumnCount(); --col>=0;){   
    47.                 infos.add(getDefaultPoint());   
    48.             }   
    49.         }   
    50.     }   
    51.        
    52.     public Point getDefaultPoint(){   
    53.         return new Point(1,1);   
    54.     }   
    55.        
    56.     @Override  
    57.     public boolean canMergeCells(int[] rows, int[] columns) {   
    58.         if( rows == null || columns == null ) return false;   
    59.         Arrays.sort(rows);   
    60.         for(int index=0; index<rows.length-1; index++){   
    61.             if( rows[index+1] - rows[index] > 1 )   
    62.                 return false;   
    63.         }   
    64.         Arrays.sort(columns);   
    65.         for(int index=0; index<columns.length-1; index++){   
    66.             if( columns[index+1] - columns[index] > 1 )   
    67.                 return false;   
    68.         }   
    69.         return true;   
    70.     }   
    71.        
    72.     @Override  
    73.     public int getCellState(int row, int column) {   
    74.         Point grid = getGrid(row, column);   
    75.         if( grid == null ) return DEFAULT;   
    76.         if( grid.x>1 || grid.y>1 )   
    77.             return MERGE;   
    78.         if( grid.x<=0 || grid.y<=0 )   
    79.             return COVERED;   
    80.         return DEFAULT;   
    81.     }   
    82.   
    83.     @Override  
    84.     public int getColumnGrid(int row, int column) {   
    85.         if( gridInfo != null && row >=0 && row < gridInfo.size() ){   
    86.             List<Point> gridRow = gridInfo.get(row);   
    87.             if( gridRow != null && column >=0 && column < gridRow.size() ){   
    88.                 Point point = gridRow.get(column);   
    89.                 if( point != null )   
    90.                     return point.x;   
    91.             }      
    92.         }   
    93.         return 1;   
    94.     }   
    95.   
    96.     @Override  
    97.     public Point getGrid(int row, int column) {   
    98.         if( gridInfo != null && row >=0 && row < gridInfo.size() ){   
    99.             List<Point> gridRow = gridInfo.get(row);   
    100.             if( gridRow != null && column >=0 && column < gridRow.size() ){   
    101.                 return gridRow.get(column);   
    102.             }      
    103.         }   
    104.         return getDefaultPoint();   
    105.     }   
    106.   
    107.     @Override  
    108.     public int getRowGrid(int row, int column) {   
    109.         if( gridInfo != null && row >=0 && row < gridInfo.size() ){   
    110.             List<Point> gridRow = gridInfo.get(row);   
    111.             if( gridRow != null && column >=0 && column < gridRow.size() ){   
    112.                 Point point = gridRow.get(column);   
    113.                 if( point != null )   
    114.                     return point.y;   
    115.             }      
    116.         }   
    117.         return 1;   
    118.     }   
    119.   
    120.     protected boolean setGrid(int row, int column, Point grid) {   
    121.         if( gridInfo != null && row >=0 && row < gridInfo.size() ){   
    122.             List<Point> gridRow = gridInfo.get(row);   
    123.             if( gridRow != null && column >=0 && column < gridRow.size() ){   
    124.                 Point point = gridRow.get(column);   
    125.                 if( point != null ){   
    126.                     point.setLocation(grid);   
    127.                 }   
    128.                 else{   
    129.                     gridRow.set(column, grid.getLocation());   
    130.                 }   
    131.                 return true;   
    132.             }      
    133.         }   
    134.         return false;   
    135.     }   
    136.   
    137.     @Override  
    138.     public boolean spliteCellAt(int row, int column) {   
    139.         if( gridInfo != null && row >=0 && row < gridInfo.size() ){   
    140.             List<Point> gridRow = gridInfo.get(row);   
    141.             if( gridRow != null && column >=0 && column < gridRow.size() ){   
    142.                 Point point = gridRow.get(column);   
    143.                 if( point != null ){   
    144.                     point = point.getLocation();   
    145.                     for(int a=0; a<point.y; a++){   
    146.                         for(int b=0; b<point.x; b++){   
    147.                             setGrid(row+a, column+b, getDefaultPoint());   
    148.                         }   
    149.                     }   
    150.                 }   
    151.                 else{   
    152.                     gridRow.set(column, getDefaultPoint());   
    153.                 }   
    154.                 return true;   
    155.             }      
    156.         }   
    157.         return false;   
    158.     }   
    159.        
    160.     @Override  
    161.     /**  
    162.      * table中发生行的添加和删除的时候需要修改该模型  
    163.      */  
    164.     public void tableChanged(TableModelEvent e) {   
    165.         //TODO   
    166.     }   
    167.        
    168.     @Override  
    169.     public boolean mergeCells(int[] rows, int[] columns) {   
    170.         if( !canMergeCells(rows, columns) )   
    171.             return false;   
    172.         Arrays.sort(rows);   
    173.         Arrays.sort(columns);   
    174.         return mergeCells(rows[0],rows[rows.length-1],columns[0],columns[columns.length-1]);   
    175.     }   
    176.   
    177.     @Override  
    178.     public boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn) {   
    179.         setGrid(startRow, startColumn, new Point(endColumn-startColumn+1, endRow-startRow+1));    
    180.         for(int row=startRow; row<=endRow; row++){   
    181.             for(int col=startColumn; col<=endColumn; col++){   
    182.                 if(row==startRow&&col==startColumn)   
    183.                     continue;   
    184.                 else  
    185.                     setGrid(row, col, new Point(COVERED,COVERED));    
    186.             }   
    187.         }   
    188.         return true;   
    189.     }   
    190.        
    191.     public String toString(){   
    192.         if( gridInfo == null )   
    193.             return "";   
    194.         StringBuffer sb = new StringBuffer();   
    195.         for(List<Point> rowInfo : gridInfo ){   
    196.             for(Point grid : rowInfo){   
    197.                 sb.append("["+grid.x+","+grid.y+"], ");   
    198.             }   
    199.             sb.append("\n");   
    200.         }   
    201.         return sb.toString();   
    202.     }   
    203. }  
     
    Java代码 Java Swing写的支持合并单元格的JTable  Java Swing写的支持合并单元格的JTableJava Swing写的支持合并单元格的JTable
    1. package org.dxj.guitools.gridbagtable;   
    2.   
    3. import java.awt.Color;   
    4. import java.awt.Component;   
    5. import java.awt.Dimension;   
    6. import java.awt.Graphics;   
    7. import java.awt.Point;   
    8. import java.awt.Rectangle;   
    9. import java.util.Enumeration;   
    10.   
    11. import javax.swing.BorderFactory;   
    12. import javax.swing.JComponent;   
    13. import javax.swing.JTable;   
    14. import javax.swing.UIManager;   
    15. import javax.swing.plaf.basic.BasicTableUI;   
    16. import javax.swing.table.JTableHeader;   
    17. import javax.swing.table.TableCellRenderer;   
    18. import javax.swing.table.TableColumn;   
    19. import javax.swing.table.TableColumnModel;   
    20.   
    21. public class GridBagTableUI extends BasicTableUI   
    22. {   
    23.     public Dimension getPreferredSize(JComponent c) {   
    24.         long width = 0;   
    25.         Enumeration<TableColumn> enumeration = table.getColumnModel().getColumns();   
    26.         while (enumeration.hasMoreElements()) {   
    27.             TableColumn aColumn = (TableColumn)enumeration.nextElement();   
    28.             width = width + aColumn.getPreferredWidth();   
    29.         }   
    30.         return createTableSize(width);   
    31.     }   
    32.        
    33.     private Dimension createTableSize(long width) {   
    34.         int height = 0;   
    35.         int rowCount = table.getRowCount();   
    36.         if (rowCount > 0 && table.getColumnCount() > 0) {   
    37.             Rectangle r = table.getCellRect(rowCount-10true);   
    38.             height = r.y + r.height;   
    39.         }   
    40.         // Width is always positive. The call to abs() is a workaround for  
    41.         // a bug in the 1.1.6 JIT on Windows.  
    42.         long tmp = Math.abs(width);   
    43.             if (tmp > Integer.MAX_VALUE) {   
    44.                 tmp = Integer.MAX_VALUE;   
    45.             }   
    46.         return new Dimension((int)tmp, height);   
    47.         }   
    48.        
    49.     public void paint(Graphics g, JComponent c) {   
    50.         Rectangle clip = g.getClipBounds();   
    51.   
    52.         Rectangle bounds = table.getBounds();   
    53.         // account for the fact that the graphics has already been translated  
    54.         // into the table's bounds   
    55.         bounds.x = bounds.y = 0;   
    56.   
    57.     if (table.getRowCount() <= 0 || table.getColumnCount() <= 0 ||   
    58.                 // this check prevents us from painting the entire table  
    59.                 // when the clip doesn't intersect our bounds at all  
    60.                 !bounds.intersects(clip)) {   
    61.   
    62.             paintDropLines(g);   
    63.         return;   
    64.     }   
    65.   
    66.         boolean ltr = table.getComponentOrientation().isLeftToRight();   
    67.   
    68.     Point upperLeft = clip.getLocation();   
    69.         if (!ltr) {   
    70.             upperLeft.x++;   
    71.         }   
    72.   
    73.     Point lowerRight = new Point(clip.x + clip.width - (ltr ? 1 : 0),   
    74.                                      clip.y + clip.height);   
    75.   
    76.         int rMin = table.rowAtPoint(upperLeft);   
    77.         int rMax = table.rowAtPoint(lowerRight);   
    78.         // This should never happen (as long as our bounds intersect the clip,  
    79.         // which is why we bail above if that is the case).  
    80.         if (rMin == -1) {   
    81.         rMin = 0;   
    82.         }   
    83.         // If the table does not have enough rows to fill the view we'll get -1.  
    84.         // (We could also get -1 if our bounds don't intersect the clip,  
    85.         // which is why we bail above if that is the case).  
    86.         // Replace this with the index of the last row.  
    87.         if (rMax == -1) {   
    88.         rMax = table.getRowCount()-1;   
    89.         }   
    90.   
    91.         int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight);    
    92.         int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft);           
    93.         // This should never happen.   
    94.         if (cMin == -1) {   
    95.         cMin = 0;   
    96.         }   
    97.     // If the table does not have enough columns to fill the view we'll get -1.  
    98.         // Replace this with the index of the last column.  
    99.         if (cMax == -1) {   
    100.         cMax = table.getColumnCount()-1;   
    101.         }   
    102.   
    103.         // Paint the grid.   
    104.         //paintGrid(g, rMin, rMax, cMin, cMax);  
    105.   
    106.         // Paint the cells.   
    107.     paintCells(g, rMin, rMax, cMin, cMax);   
    108.   
    109.         paintDropLines(g);   
    110.     }   
    111.        
    112.     private void paintDropLines(Graphics g) {   
    113.         JTable.DropLocation loc = table.getDropLocation();   
    114.         if (loc == null) {   
    115.             return;   
    116.         }   
    117.   
    118.         Color color = UIManager.getColor("Table.dropLineColor");   
    119.         Color shortColor = UIManager.getColor("Table.dropLineShortColor");   
    120.         if (color == null && shortColor == null) {   
    121.             return;   
    122.         }   
    123.   
    124.         Rectangle rect;   
    125.   
    126.         rect = getHDropLineRect(loc);   
    127.         if (rect != null) {   
    128.             int x = rect.x;   
    129.             int w = rect.width;   
    130.             if (color != null) {   
    131.                 extendRect(rect, true);   
    132.                 g.setColor(color);   
    133.                 g.fillRect(rect.x, rect.y, rect.width, rect.height);   
    134.             }   
    135.             if (!loc.isInsertColumn() && shortColor != null) {   
    136.                 g.setColor(shortColor);   
    137.                 g.fillRect(x, rect.y, w, rect.height);   
    138.             }   
    139.         }   
    140.   
    141.         rect = getVDropLineRect(loc);   
    142.         if (rect != null) {   
    143.             int y = rect.y;   
    144.             int h = rect.height;   
    145.             if (color != null) {   
    146.                 extendRect(rect, false);   
    147.                 g.setColor(color);   
    148.                 g.fillRect(rect.x, rect.y, rect.width, rect.height);   
    149.             }   
    150.             if (!loc.isInsertRow() && shortColor != null) {   
    151.                 g.setColor(shortColor);   
    152.                 g.fillRect(rect.x, y, rect.width, h);   
    153.             }   
    154.         }   
    155.     }   
    156.        
    157.     /*  
    158.      * Paints the grid lines within <I>aRect</I>, using the grid 
    159.      * color set with <I>setGridColor</I>. Paints vertical lines 
    160.      * if <code>getShowVerticalLines()</code> returns true and paints 
    161.      * horizontal lines if <code>getShowHorizontalLines()</code> 
    162.      * returns true.  
    163.      */  
    164.     private void paintGrid(Graphics g, int rMin, int rMax, int cMin, int cMax) {   
    165.         g.setColor(table.getGridColor());   
    166.   
    167.         Rectangle minCell = table.getCellRect(rMin, cMin, true);   
    168.         Rectangle maxCell = table.getCellRect(rMax, cMax, true);   
    169.         Rectangle damagedArea = minCell.union( maxCell );   
    170.   
    171.         if (table.getShowHorizontalLines()) {   
    172.             int tableWidth = damagedArea.x + damagedArea.width;   
    173.             int y = damagedArea.y;   
    174.             for (int row = rMin; row <= rMax; row++) {   
    175.                 y += table.getRowHeight(row);   
    176.                 g.drawLine(damagedArea.x, y - 1, tableWidth - 1, y - 1);   
    177.             }   
    178.         }   
    179.         if (table.getShowVerticalLines()) {   
    180.             TableColumnModel cm = table.getColumnModel();   
    181.             int tableHeight = damagedArea.y + damagedArea.height;   
    182.             int x;   
    183.             if (table.getComponentOrientation().isLeftToRight()) {   
    184.                 x = damagedArea.x;   
    185.                 for (int column = cMin; column <= cMax; column++) {   
    186.                     int w = cm.getColumn(column).getWidth();   
    187.                     x += w;   
    188.                     g.drawLine(x - 10, x - 1, tableHeight - 1);   
    189.                 }   
    190.             } else {   
    191.                 x = damagedArea.x;   
    192.                 for (int column = cMax; column >= cMin; column--) {   
    193.                     int w = cm.getColumn(column).getWidth();   
    194.                     x += w;   
    195.                     g.drawLine(x - 10, x - 1, tableHeight - 1);   
    196.                 }   
    197.             }   
    198.         }   
    199.     }   
    200.        
    201.     private void paintCells(Graphics g, int rMin, int rMax, int cMin, int cMax) {   
    202.         JTableHeader header = table.getTableHeader();   
    203.         TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn();   
    204.   
    205.         TableColumnModel cm = table.getColumnModel();   
    206.         int columnMargin = cm.getColumnMargin();   
    207.             Rectangle cellRect;   
    208.         TableColumn aColumn;   
    209.         int columnWidth;   
    210.         if (table.getComponentOrientation().isLeftToRight()) {   
    211.             for(int row = rMin; row <= rMax; row++) {   
    212.                 if( table instanceof GridBagTable )   
    213.                     cellRect = ((GridBagTable)table).getGridCellRect(row, cMin, false);   
    214.                 else  
    215.                     cellRect = table.getCellRect(row, cMin, false);   
    216.                 for(int column = cMin; column <= cMax; column++) {   
    217.                     aColumn = cm.getColumn(column);   
    218.                     columnWidth = aColumn.getWidth();                                    
    219.                     //TODO   
    220.                     cellRect.width = columnWidth - columnMargin;   
    221.                     int oldHeight = cellRect.height;   
    222.                     if( table instanceof GridBagTable ){   
    223.                         if(((GridBagTable)table).getGridBagModel().getCellState( row, column) == GridBagModel.COVERED ) {   
    224.                             cellRect.width = 0;   
    225.                             cellRect.height = 0;   
    226.                         }   
    227.                         else{   
    228.                             int h = ((GridBagTable)table).getGridBagModel().getColumnGrid(row, column);   
    229.                             if( h >1 ){   
    230.                                 forint n=1; n<h; n++)   
    231.                                     cellRect.width += cm.getColumn(column+n).getWidth();   
    232.                             }   
    233.                             int v = ((GridBagTable)table).getGridBagModel().getRowGrid(row, column);   
    234.                             if( v >1 ){   
    235.                                 forint n=1; n<v; n++)   
    236.                                     cellRect.height += table.getRowHeight(row+n);   
    237.                             }   
    238.                         }              
    239.                     }                                                                          
    240.                     if (aColumn != draggedColumn) {   
    241.                         paintCell(g, cellRect, row, column);   
    242.                     }                          
    243.                     cellRect.height = oldHeight;                       
    244.                     cellRect.x += columnWidth;   
    245.                 }   
    246.             }   
    247.         } else {   
    248.             for(int row = rMin; row <= rMax; row++) {   
    249.                     cellRect = table.getCellRect(row, cMin, false);   
    250.                     aColumn = cm.getColumn(cMin);   
    251.                     if (aColumn != draggedColumn) {   
    252.                         columnWidth = aColumn.getWidth();   
    253.                         cellRect.width = columnWidth - columnMargin;   
    254.                         paintCell(g, cellRect, row, cMin);   
    255.                     }   
    256.                     for(int column = cMin+1; column <= cMax; column++) {   
    257.                         aColumn = cm.getColumn(column);   
    258.                         columnWidth = aColumn.getWidth();   
    259. //                      TODO   
    260.                         cellRect.width = columnWidth - columnMargin;   
    261.                         cellRect.x -= columnWidth;   
    262.                         if (aColumn != draggedColumn) {   
    263.                             paintCell(g, cellRect, row, column);   
    264.                         }   
    265.                 }   
    266.             }   
    267.         }   
    268.   
    269.             // Paint the dragged column if we are dragging.  
    270.             if (draggedColumn != null) {   
    271.             paintDraggedArea(g, rMin, rMax, draggedColumn, header.getDraggedDistance());   
    272.         }   
    273.   
    274.         // Remove any renderers that may be left in the rendererPane.  
    275.         rendererPane.removeAll();   
    276.         }   
    277.        
    278.     private void paintCell(Graphics g, Rectangle cellRect, int row, int column) {   
    279.         if (table.isEditing() && table.getEditingRow()==row &&   
    280.                                  table.getEditingColumn()==column) {   
    281.             Component component = table.getEditorComponent();   
    282.         component.setBounds(cellRect);   
    283.             component.validate();   
    284.         }   
    285.         else {   
    286.             TableCellRenderer renderer = table.getCellRenderer(row, column);   
    287.             Component component = table.prepareRenderer(renderer, row, column);   
    288.                
    289.             if( component instanceof JComponent ){   
    290.                 ((JComponent)component).setBorder(BorderFactory.createLineBorder(Color.gray));   
    291.             }   
    292.             rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y,   
    293.                                         cellRect.width, cellRect.height, true);              
    294.         }   
    295.     }   
    296.        
    297.     private Rectangle getHDropLineRect(JTable.DropLocation loc) {   
    298.         if (!loc.isInsertRow()) {   
    299.             return null;   
    300.         }   
    301.   
    302.         int row = loc.getRow();   
    303.         int col = loc.getColumn();   
    304.         if (col >= table.getColumnCount()) {   
    305.             col--;   
    306.         }   
    307.   
    308.         Rectangle rect = table.getCellRect(row, col, true);   
    309.   
    310.         if (row >= table.getRowCount()) {   
    311.             row--;   
    312.             Rectangle prevRect = table.getCellRect(row, col, true);   
    313.             rect.y = prevRect.y + prevRect.height;   
    314.         }   
    315.   
    316.         if (rect.y == 0) {   
    317.             rect.y = -1;   
    318.         } else {   
    319.             rect.y -= 2;   
    320.         }   
    321.   
    322.         rect.height = 3;   
    323.   
    324.         return rect;   
    325.     }   
    326.        
    327.     private void paintDraggedArea(Graphics g, int rMin, int rMax, TableColumn draggedColumn, int distance) {   
    328.         int draggedColumnIndex = viewIndexForColumn(draggedColumn);   
    329.   
    330.         Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true);   
    331.     Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true);   
    332.   
    333.     Rectangle vacatedColumnRect = minCell.union(maxCell);   
    334.   
    335.     // Paint a gray well in place of the moving column.  
    336.     g.setColor(table.getParent().getBackground());   
    337.     g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,   
    338.            vacatedColumnRect.width, vacatedColumnRect.height);   
    339.   
    340.     // Move to the where the cell has been dragged.  
    341.     vacatedColumnRect.x += distance;   
    342.   
    343.     // Fill the background.   
    344.     g.setColor(table.getBackground());   
    345.     g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,   
    346.            vacatedColumnRect.width, vacatedColumnRect.height);   
    347.   
    348.     // Paint the vertical grid lines if necessary.  
    349.     if (table.getShowVerticalLines()) {   
    350.         g.setColor(table.getGridColor());   
    351.         int x1 = vacatedColumnRect.x;   
    352.         int y1 = vacatedColumnRect.y;   
    353.         int x2 = x1 + vacatedColumnRect.width - 1;   
    354.         int y2 = y1 + vacatedColumnRect.height - 1;   
    355.         // Left   
    356.         g.drawLine(x1-1, y1, x1-1, y2);   
    357.         // Right   
    358.         g.drawLine(x2, y1, x2, y2);   
    359.     }   
    360.   
    361.     for(int row = rMin; row <= rMax; row++) {   
    362.         // Render the cell value   
    363.         Rectangle r = table.getCellRect(row, draggedColumnIndex, false);   
    364.         r.x += distance;   
    365.         paintCell(g, r, row, draggedColumnIndex);   
    366.   
    367.         // Paint the (lower) horizontal grid line if necessary.  
    368.         if (table.getShowHorizontalLines()) {   
    369.         g.setColor(table.getGridColor());   
    370.         Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true);   
    371.         rcr.x += distance;   
    372.         int x1 = rcr.x;   
    373.         int y1 = rcr.y;   
    374.         int x2 = x1 + rcr.width - 1;   
    375.         int y2 = y1 + rcr.height - 1;   
    376.         g.drawLine(x1, y2, x2, y2);   
    377.         }   
    378.     }   
    379.     }   
    380.        
    381.     private int viewIndexForColumn(TableColumn aColumn) {   
    382.         TableColumnModel cm = table.getColumnModel();   
    383.         for (int column = 0; column < cm.getColumnCount(); column++) {   
    384.             if (cm.getColumn(column) == aColumn) {   
    385.                 return column;   
    386.             }   
    387.         }   
    388.         return -1;   
    389.     }   
    390.        
    391.     private Rectangle extendRect(Rectangle rect, boolean horizontal) {   
    392.         if (rect == null) {   
    393.             return rect;   
    394.         }   
    395.   
    396.         if (horizontal) {   
    397.             rect.x = 0;   
    398.             rect.width = table.getWidth();   
    399.         } else {   
    400.             rect.y = 0;   
    401.   
    402.             if (table.getRowCount() != 0) {   
    403.                 Rectangle lastRect = table.getCellRect(table.getRowCount() - 10true);   
    404.                 rect.height = lastRect.y + lastRect.height;   
    405.             } else {   
    406.                 rect.height = table.getHeight();   
    407.             }   
    408.         }   
    409.   
    410.         return rect;   
    411.     }   
    412.        
    413.     private Rectangle getVDropLineRect(JTable.DropLocation loc) {   
    414.         if (!loc.isInsertColumn()) {   
    415.             return null;   
    416.         }   
    417.   
    418.         boolean ltr = table.getComponentOrientation().isLeftToRight();   
    419.         int col = loc.getColumn();   
    420.         Rectangle rect = table.getCellRect(loc.getRow(), col, true);   
    421.   
    422.         if (col >= table.getColumnCount()) {   
    423.             col--;   
    424.             rect = table.getCellRect(loc.getRow(), col, true);   
    425.             if (ltr) {   
    426.                 rect.x = rect.x + rect.width;   
    427.             }   
    428.         } else if (!ltr) {   
    429.             rect.x = rect.x + rect.width;   
    430.         }   
    431.   
    432.         if (rect.x == 0) {   
    433.             rect.x = -1;   
    434.         } else {   
    435.             rect.x -= 2;   
    436.         }   
    437.            
    438.         rect.width = 3;   
    439.   
    440.         return rect;   
    441.     }   
    442.   
    443. }  // End of Class BasicTableUI  

      测试代码:

    Java代码 Java Swing写的支持合并单元格的JTable  Java Swing写的支持合并单元格的JTableJava Swing写的支持合并单元格的JTable
    1. import java.awt.BorderLayout;   
    2. import java.awt.event.ActionEvent;   
    3. import java.awt.event.ActionListener;   
    4.   
    5. import javax.swing.JButton;   
    6. import javax.swing.JFrame;   
    7. import javax.swing.JScrollPane;   
    8. import javax.swing.table.DefaultTableModel;   
    9.   
    10. import com.jrf.jgrid.guitools.gridbagtable.GridBagTable;   
    11.   
    12.   
    13. public class Test implements ActionListener{   
    14.        
    15.     GridBagTable table;   
    16.     public Test()   
    17.     {   
    18.         JFrame d = new JFrame();   
    19.         DefaultTableModel model = new DefaultTableModel(5,5);   
    20.            
    21.         table = new GridBagTable(model);   
    22.         table.setRowHeight(20);   
    23.            
    24.         JScrollPane pane = new JScrollPane(table);   
    25.         d.getContentPane().add(pane, BorderLayout.CENTER);   
    26.         JButton btn = new JButton("合并/拆分");   
    27.         d.getContentPane().add(btn, BorderLayout.NORTH);   
    28.         btn.addActionListener(this);   
    29.         d.setBounds(00400400);   
    30.         d.setVisible(true);   
    31.     }   
    32.        
    33.     public static void main(String[] fsd){   
    34.         new Test();   
    35.     }   
    36.        
    37.     public void actionPerformed(ActionEvent e) {   
    38.         table.mergeCells(table.getSelectedRows(), table.getSelectedColumns());   
    39.     }   
    40. }