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

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

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

Java代码
  1. packageorg.dxj.guitools.gridbagtable;
  2. importjava.awt.Component;
  3. importjava.awt.Point;
  4. importjava.awt.Rectangle;
  5. importjava.util.Enumeration;
  6. importjava.util.EventObject;
  7. importjavax.swing.DefaultCellEditor;
  8. importjavax.swing.JTable;
  9. importjavax.swing.SwingUtilities;
  10. importjavax.swing.event.TableModelEvent;
  11. importjavax.swing.table.AbstractTableModel;
  12. importjavax.swing.table.TableColumn;
  13. importjavax.swing.table.TableColumnModel;
  14. /**
  15. *@author15860102@qq.com
  16. */
  17. publicclassGridBagTableextendsJTable{
  18. GridBagModelgridBagModel;
  19. publicGridBagModelgetGridBagModel(){
  20. returngridBagModel;
  21. }
  22. publicvoidsetGridBagModel(GridBagModelgridBagModel){
  23. if(gridBagModel!=null&&gridBagModel!=this.gridBagModel)
  24. this.gridBagModel=gridBagModel;
  25. }
  26. publicGridBagTable(AbstractTableModeldm){
  27. super(dm);
  28. getTableHeader().setReorderingAllowed(false);
  29. gridBagModel=newDefaultGridBagTableModel(dm);
  30. getColumnModel().setColumnSelectionAllowed(true);
  31. }
  32. privatevoidupdateSubComponentUI(ObjectcomponentShell){
  33. if(componentShell==null){
  34. return;
  35. }
  36. Componentcomponent=null;
  37. if(componentShellinstanceofComponent){
  38. component=(Component)componentShell;
  39. }
  40. if(componentShellinstanceofDefaultCellEditor){
  41. component=((DefaultCellEditor)componentShell).getComponent();
  42. }
  43. if(component!=null){
  44. SwingUtilities.updateComponentTreeUI(component);
  45. }
  46. }
  47. publicvoidupdateUI(){
  48. //UpdatetheUIsofthecellrenderers,celleditorsandheaderrenderers.
  49. TableColumnModelcm=getColumnModel();
  50. for(intcolumn=0;column<cm.getColumnCount();column++){
  51. TableColumnaColumn=cm.getColumn(column);
  52. updateSubComponentUI(aColumn.getCellRenderer());
  53. updateSubComponentUI(aColumn.getCellEditor());
  54. updateSubComponentUI(aColumn.getHeaderRenderer());
  55. }
  56. //UpdatetheUIsofallthedefaultrenderers.
  57. EnumerationdefaultRenderers=defaultRenderersByColumnClass.elements();
  58. while(defaultRenderers.hasMoreElements()){
  59. updateSubComponentUI(defaultRenderers.nextElement());
  60. }
  61. //UpdatetheUIsofallthedefaulteditors.
  62. EnumerationdefaultEditors=defaultEditorsByColumnClass.elements();
  63. while(defaultEditors.hasMoreElements()){
  64. updateSubComponentUI(defaultEditors.nextElement());
  65. }
  66. //UpdatetheUIofthetableheader
  67. if(tableHeader!=null&&tableHeader.getParent()==null){
  68. tableHeader.updateUI();
  69. }
  70. setUI(newGridBagTableUI());
  71. }
  72. publicRectanglegetGridCellRect(introw,intcolumn,booleanincludeSpacing){
  73. returnsuper.getCellRect(row,column,includeSpacing);
  74. }
  75. publicRectanglegetCellRect(introw,intcolumn,booleanincludeSpacing){
  76. RectanglecellRect=super.getCellRect(row,column,includeSpacing);
  77. intcols=gridBagModel.getColumnGrid(row,column);
  78. TableColumnModelcm=getColumnModel();
  79. for(intn=1;n<cols;n++)
  80. cellRect.width+=cm.getColumn(column+n).getWidth();
  81. introws=gridBagModel.getRowGrid(row,column);
  82. for(intn=1;n<rows;n++)
  83. cellRect.height+=getRowHeight(row+n);
  84. returncellRect;
  85. }
  86. publicvoidtableChanged(TableModelEvente){
  87. super.tableChanged(e);
  88. //TODO
  89. }
  90. publicbooleanmergeCells(intstartRow,intendRow,intstartColumn,intendColumn){
  91. if(gridBagModel.mergeCells(startRow,endRow,startColumn,endColumn)){
  92. repaint();
  93. returntrue;
  94. }
  95. returnfalse;
  96. }
  97. publicbooleanmergeCells(int[]rows,int[]columns){
  98. if(gridBagModel.mergeCells(rows,columns)){
  99. repaint();
  100. returntrue;
  101. }
  102. returnfalse;
  103. }
  104. publicbooleanspliteCellAt(introw,intcolumn){
  105. if(gridBagModel.spliteCellAt(row,column)){
  106. repaint();
  107. returntrue;
  108. }
  109. returnfalse;
  110. }
  111. publicvoidchangeSelection(introwIndex,intcolumnIndex,booleantoggle,booleanextend){
  112. if(gridBagModel.getCellState(rowIndex,columnIndex)!=GridBagModel.COVERED)
  113. super.changeSelection(rowIndex,columnIndex,toggle,extend);
  114. Pointp;
  115. for(introw=rowIndex;row>=0;row--){
  116. for(intcol=columnIndex;col>=0;col--){
  117. p=gridBagModel.getGrid(row,col);
  118. //p=((Point)((Vector)rowVector.get(row)).get(col));
  119. if(col+p.x>columnIndex&&row+p.y>rowIndex){
  120. rowIndex=row;
  121. columnIndex=col;
  122. break;
  123. }
  124. }
  125. }
  126. super.changeSelection(rowIndex,columnIndex,toggle,extend);
  127. repaint();
  128. }
  129. publicbooleaneditCellAt(introwIndex,intcolumnIndex,EventObjecte){
  130. if(gridBagModel.getCellState(rowIndex,columnIndex)!=GridBagModel.COVERED)
  131. returnsuper.editCellAt(rowIndex,columnIndex,e);
  132. Pointp;
  133. for(introw=rowIndex;row>=0;row--){
  134. for(intcol=columnIndex;col>=0;col--){
  135. p=gridBagModel.getGrid(row,col);
  136. if(col+p.x>columnIndex&&row+p.y>rowIndex){
  137. rowIndex=row;
  138. columnIndex=col;
  139. break;
  140. }
  141. }
  142. }
  143. returnsuper.editCellAt(rowIndex,columnIndex,e);
  144. }
  145. }

package org.dxj.guitools.gridbagtable; import java.awt.Component; import java.awt.Point; import java.awt.Rectangle; import java.util.Enumeration; import java.util.EventObject; import javax.swing.DefaultCellEditor; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.event.TableModelEvent; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; /** * @author 15860102@qq.com */ public class GridBagTable extends JTable{ GridBagModel gridBagModel; public GridBagModel getGridBagModel() { return gridBagModel; } public void setGridBagModel(GridBagModel gridBagModel){ if( gridBagModel != null && gridBagModel != this.gridBagModel ) this.gridBagModel = gridBagModel; } public GridBagTable(AbstractTableModel dm){ super(dm); getTableHeader().setReorderingAllowed(false); gridBagModel = new DefaultGridBagTableModel(dm); getColumnModel().setColumnSelectionAllowed(true); } private void updateSubComponentUI(Object componentShell) { if (componentShell == null) { return; } Component component = null; if (componentShell instanceof Component) { component = (Component)componentShell; } if (componentShell instanceof DefaultCellEditor) { component = ((DefaultCellEditor)componentShell).getComponent(); } if (component != null) { SwingUtilities.updateComponentTreeUI(component); } } public void updateUI() { // Update the UIs of the cell renderers, cell editors and header renderers. TableColumnModel cm = getColumnModel(); for(int column = 0; column < cm.getColumnCount(); column++) { TableColumn aColumn = cm.getColumn(column); updateSubComponentUI(aColumn.getCellRenderer()); updateSubComponentUI(aColumn.getCellEditor()); updateSubComponentUI(aColumn.getHeaderRenderer()); } // Update the UIs of all the default renderers. Enumeration defaultRenderers = defaultRenderersByColumnClass.elements(); while (defaultRenderers.hasMoreElements()) { updateSubComponentUI(defaultRenderers.nextElement()); } // Update the UIs of all the default editors. Enumeration defaultEditors = defaultEditorsByColumnClass.elements(); while (defaultEditors.hasMoreElements()) { updateSubComponentUI(defaultEditors.nextElement()); } // Update the UI of the table header if (tableHeader != null && tableHeader.getParent() == null) { tableHeader.updateUI(); } setUI(new GridBagTableUI()); } public Rectangle getGridCellRect(int row, int column, boolean includeSpacing){ return super.getCellRect(row, column, includeSpacing); } public Rectangle getCellRect(int row, int column, boolean includeSpacing) { Rectangle cellRect = super.getCellRect(row, column, includeSpacing); int cols = gridBagModel.getColumnGrid(row, column); TableColumnModel cm = getColumnModel(); for( int n=1; n<cols; n++) cellRect.width += cm.getColumn(column+n).getWidth(); int rows = gridBagModel.getRowGrid(row, column); for( int n=1; n<rows; n++) cellRect.height += getRowHeight(row+n); return cellRect; } public void tableChanged(TableModelEvent e){ super.tableChanged(e); //TODO } public boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn){ if( gridBagModel.mergeCells(startRow, endRow, startColumn, endColumn)){ repaint(); return true; } return false; } public boolean mergeCells(int[] rows, int[] columns){ if( gridBagModel.mergeCells(rows, columns)){ repaint(); return true; } return false; } public boolean spliteCellAt(int row, int column){ if( gridBagModel.spliteCellAt(row, column)){ repaint(); return true; } return false; } public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) { if( gridBagModel.getCellState( rowIndex , columnIndex ) != GridBagModel.COVERED ) super.changeSelection(rowIndex, columnIndex, toggle, extend); Point p; for( int row = rowIndex; row >= 0; row-- ){ for( int col = columnIndex; col >= 0; col-- ){ p = gridBagModel.getGrid(row, col); //p = ((Point)((Vector)rowVector.get(row)).get(col)); if( col + p.x > columnIndex && row + p.y > rowIndex){ rowIndex = row; columnIndex = col; break; } } } super.changeSelection(rowIndex, columnIndex, toggle, extend); repaint(); } public boolean editCellAt(int rowIndex, int columnIndex, EventObject e){ if( gridBagModel.getCellState( rowIndex , columnIndex ) != GridBagModel.COVERED ) return super.editCellAt(rowIndex, columnIndex, e); Point p; for( int row = rowIndex; row >= 0; row-- ){ for( int col = columnIndex; col >= 0; col-- ){ p = gridBagModel.getGrid(row, col); if( col + p.x > columnIndex && row + p.y > rowIndex){ rowIndex = row; columnIndex = col; break; } } } return super.editCellAt(rowIndex, columnIndex, e); } }

Java代码
  1. packageorg.dxj.guitools.gridbagtable;
  2. importjava.awt.Point;
  3. publicinterfaceGridBagModel{
  4. //格子处于正常状态
  5. intDEFAULT=0;
  6. //格子合并了其他的格子
  7. intMERGE=1;
  8. //格子被其他格子合并
  9. intCOVERED=-1;
  10. /**
  11. *@paramrow行
  12. *@paramcolumn列
  13. *@return该单元格在行、列的跨度
  14. */
  15. PointgetGrid(introw,intcolumn);
  16. /**
  17. *在Y轴方向的跨度
  18. *@paramrow
  19. *@paramcolumn
  20. *@return
  21. */
  22. intgetRowGrid(introw,intcolumn);
  23. /**
  24. *在X轴方向的跨度
  25. *@paramrow
  26. *@paramcolumn
  27. *@return
  28. */
  29. intgetColumnGrid(introw,intcolumn);
  30. /**
  31. *@paramrows行集合
  32. *@paramcolumns列集合
  33. *@return单元格集合是否可以合并在一起
  34. */
  35. booleancanMergeCells(int[]rows,int[]columns);
  36. /**
  37. *判断该单元格状态
  38. *@paramrow
  39. *@paramcolumn
  40. *@returnMERGE|DEFAULT|COVERED
  41. */
  42. intgetCellState(introw,intcolumn);
  43. /**
  44. *将单元格集合合并
  45. *@paramstartRow开始行
  46. *@paramendRow结束行
  47. *@paramstartColumn开始列
  48. *@paramendColumn结束列
  49. *@return是否合并成功
  50. */
  51. booleanmergeCells(intstartRow,intendRow,intstartColumn,intendColumn);
  52. /**
  53. *将单元格集合合并
  54. *@paramrows行集合
  55. *@paramcolumns列集合
  56. *@return是否合并成功
  57. */
  58. booleanmergeCells(int[]rows,int[]columns);
  59. /**
  60. *拆分单元格
  61. *@paramrow行
  62. *@paramcolumn列
  63. *@return是否拆分成功
  64. */
  65. booleanspliteCellAt(introw,intcolumn);
  66. /**
  67. *清除所有合并
  68. */
  69. voidclearMergence();
  70. }

package org.dxj.guitools.gridbagtable; import java.awt.Point; public interface GridBagModel { //格子处于正常状态 int DEFAULT = 0; //格子合并了其他的格子 int MERGE = 1; //格子被其他格子合并 int COVERED = -1; /** * @param row 行 * @param column 列 * @return 该单元格在行、列的跨度 */ Point getGrid(int row, int column); /** * 在Y轴方向的跨度 * @param row * @param column * @return */ int getRowGrid(int row, int column); /** * 在X轴方向的跨度 * @param row * @param column * @return */ int getColumnGrid(int row, int column); /** * @param rows 行集合 * @param columns 列集合 * @return 单元格集合是否可以合并在一起 */ boolean canMergeCells(int[] rows, int[] columns); /** * 判断该单元格状态 * @param row * @param column * @return MERGE|DEFAULT|COVERED */ int getCellState(int row, int column); /** * 将单元格集合合并 * @param startRow 开始行 * @param endRow 结束行 * @param startColumn 开始列 * @param endColumn 结束列 * @return 是否合并成功 */ boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn); /** * 将单元格集合合并 * @param rows 行集合 * @param columns 列集合 * @return 是否合并成功 */ boolean mergeCells(int[] rows, int[] columns); /** * 拆分单元格 * @param row 行 * @param column 列 * @return 是否拆分成功 */ boolean spliteCellAt(int row, int column); /** * 清除 所有合并 */ void clearMergence(); }

Java代码
  1. packageorg.dxj.guitools.gridbagtable;
  2. importjava.awt.Point;
  3. importjava.util.Arrays;
  4. importjava.util.List;
  5. importjava.util.Vector;
  6. importjavax.swing.event.TableModelEvent;
  7. importjavax.swing.event.TableModelListener;
  8. importjavax.swing.table.AbstractTableModel;
  9. publicclassDefaultGridBagTableModelimplementsGridBagModel,TableModelListener{
  10. protectedAbstractTableModelmodel;
  11. protectedList<List<Point>>gridInfo;
  12. DefaultGridBagTableModel(AbstractTableModelmodel){
  13. gridInfo=newVector<List<Point>>();
  14. setTableModel(model);
  15. }
  16. publicvoidsetTableModel(AbstractTableModelmodel){
  17. if(model!=null&&model!=this.model){
  18. if(this.model!=null)
  19. this.model.removeTableModelListener(this);
  20. //防止多次添加监听器
  21. model.removeTableModelListener(this);
  22. model.addTableModelListener(this);
  23. this.model=model;
  24. clearMergence();
  25. }
  26. }
  27. publicvoidclearMergence(){
  28. if(gridInfo==null)
  29. gridInfo=newVector<List<Point>>();
  30. else
  31. gridInfo.clear();
  32. if(model==null)
  33. return;
  34. //初始化,每个格子占的格子数为(1,1);
  35. for(introw=model.getRowCount();--row>=0;){
  36. List<Point>infos=newVector<Point>();
  37. gridInfo.add(infos);
  38. for(intcol=model.getColumnCount();--col>=0;){
  39. infos.add(getDefaultPoint());
  40. }
  41. }
  42. }
  43. publicPointgetDefaultPoint(){
  44. returnnewPoint(1,1);
  45. }
  46. @Override
  47. publicbooleancanMergeCells(int[]rows,int[]columns){
  48. if(rows==null||columns==null)returnfalse;
  49. Arrays.sort(rows);
  50. for(intindex=0;index<rows.length-1;index++){
  51. if(rows[index+1]-rows[index]>1)
  52. returnfalse;
  53. }
  54. Arrays.sort(columns);
  55. for(intindex=0;index<columns.length-1;index++){
  56. if(columns[index+1]-columns[index]>1)
  57. returnfalse;
  58. }
  59. returntrue;
  60. }
  61. @Override
  62. publicintgetCellState(introw,intcolumn){
  63. Pointgrid=getGrid(row,column);
  64. if(grid==null)returnDEFAULT;
  65. if(grid.x>1||grid.y>1)
  66. returnMERGE;
  67. if(grid.x<=0||grid.y<=0)
  68. returnCOVERED;
  69. returnDEFAULT;
  70. }
  71. @Override
  72. publicintgetColumnGrid(introw,intcolumn){
  73. if(gridInfo!=null&&row>=0&&row<gridInfo.size()){
  74. List<Point>gridRow=gridInfo.get(row);
  75. if(gridRow!=null&&column>=0&&column<gridRow.size()){
  76. Pointpoint=gridRow.get(column);
  77. if(point!=null)
  78. returnpoint.x;
  79. }
  80. }
  81. return1;
  82. }
  83. @Override
  84. publicPointgetGrid(introw,intcolumn){
  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. returngridRow.get(column);
  89. }
  90. }
  91. returngetDefaultPoint();
  92. }
  93. @Override
  94. publicintgetRowGrid(introw,intcolumn){
  95. if(gridInfo!=null&&row>=0&&row<gridInfo.size()){
  96. List<Point>gridRow=gridInfo.get(row);
  97. if(gridRow!=null&&column>=0&&column<gridRow.size()){
  98. Pointpoint=gridRow.get(column);
  99. if(point!=null)
  100. returnpoint.y;
  101. }
  102. }
  103. return1;
  104. }
  105. protectedbooleansetGrid(introw,intcolumn,Pointgrid){
  106. if(gridInfo!=null&&row>=0&&row<gridInfo.size()){
  107. List<Point>gridRow=gridInfo.get(row);
  108. if(gridRow!=null&&column>=0&&column<gridRow.size()){
  109. Pointpoint=gridRow.get(column);
  110. if(point!=null){
  111. point.setLocation(grid);
  112. }
  113. else{
  114. gridRow.set(column,grid.getLocation());
  115. }
  116. returntrue;
  117. }
  118. }
  119. returnfalse;
  120. }
  121. @Override
  122. publicbooleanspliteCellAt(introw,intcolumn){
  123. if(gridInfo!=null&&row>=0&&row<gridInfo.size()){
  124. List<Point>gridRow=gridInfo.get(row);
  125. if(gridRow!=null&&column>=0&&column<gridRow.size()){
  126. Pointpoint=gridRow.get(column);
  127. if(point!=null){
  128. point=point.getLocation();
  129. for(inta=0;a<point.y;a++){
  130. for(intb=0;b<point.x;b++){
  131. setGrid(row+a,column+b,getDefaultPoint());
  132. }
  133. }
  134. }
  135. else{
  136. gridRow.set(column,getDefaultPoint());
  137. }
  138. returntrue;
  139. }
  140. }
  141. returnfalse;
  142. }
  143. @Override
  144. /**
  145. *table中发生行的添加和删除的时候需要修改该模型
  146. */
  147. publicvoidtableChanged(TableModelEvente){
  148. //TODO
  149. }
  150. @Override
  151. publicbooleanmergeCells(int[]rows,int[]columns){
  152. if(!canMergeCells(rows,columns))
  153. returnfalse;
  154. Arrays.sort(rows);
  155. Arrays.sort(columns);
  156. returnmergeCells(rows[0],rows[rows.length-1],columns[0],columns[columns.length-1]);
  157. }
  158. @Override
  159. publicbooleanmergeCells(intstartRow,intendRow,intstartColumn,intendColumn){
  160. setGrid(startRow,startColumn,newPoint(endColumn-startColumn+1,endRow-startRow+1));
  161. for(introw=startRow;row<=endRow;row++){
  162. for(intcol=startColumn;col<=endColumn;col++){
  163. if(row==startRow&&col==startColumn)
  164. continue;
  165. else
  166. setGrid(row,col,newPoint(COVERED,COVERED));
  167. }
  168. }
  169. returntrue;
  170. }
  171. publicStringtoString(){
  172. if(gridInfo==null)
  173. return"";
  174. StringBuffersb=newStringBuffer();
  175. for(List<Point>rowInfo:gridInfo){
  176. for(Pointgrid:rowInfo){
  177. sb.append("["+grid.x+","+grid.y+"],");
  178. }
  179. sb.append("\n");
  180. }
  181. returnsb.toString();
  182. }
  183. }

package org.dxj.guitools.gridbagtable; import java.awt.Point; import java.util.Arrays; import java.util.List; import java.util.Vector; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.AbstractTableModel; public class DefaultGridBagTableModel implements GridBagModel, TableModelListener{ protected AbstractTableModel model; protected List<List<Point>> gridInfo; DefaultGridBagTableModel(AbstractTableModel model){ gridInfo = new Vector<List<Point>>(); setTableModel(model); } public void setTableModel(AbstractTableModel model){ if( model != null && model != this.model ){ if( this.model != null ) this.model.removeTableModelListener(this); //防止多次添加监听器 model.removeTableModelListener(this); model.addTableModelListener(this); this.model = model; clearMergence(); } } public void clearMergence(){ if( gridInfo == null ) gridInfo = new Vector<List<Point>>(); else gridInfo.clear(); if( model == null ) return; //初始化,每个格子占的格子数为(1,1); for(int row=model.getRowCount(); --row>=0;){ List<Point> infos = new Vector<Point>(); gridInfo.add(infos); for(int col=model.getColumnCount(); --col>=0;){ infos.add(getDefaultPoint()); } } } public Point getDefaultPoint(){ return new Point(1,1); } @Override public boolean canMergeCells(int[] rows, int[] columns) { if( rows == null || columns == null ) return false; Arrays.sort(rows); for(int index=0; index<rows.length-1; index++){ if( rows[index+1] - rows[index] > 1 ) return false; } Arrays.sort(columns); for(int index=0; index<columns.length-1; index++){ if( columns[index+1] - columns[index] > 1 ) return false; } return true; } @Override public int getCellState(int row, int column) { Point grid = getGrid(row, column); if( grid == null ) return DEFAULT; if( grid.x>1 || grid.y>1 ) return MERGE; if( grid.x<=0 || grid.y<=0 ) return COVERED; return DEFAULT; } @Override public int getColumnGrid(int row, int column) { if( gridInfo != null && row >=0 && row < gridInfo.size() ){ List<Point> gridRow = gridInfo.get(row); if( gridRow != null && column >=0 && column < gridRow.size() ){ Point point = gridRow.get(column); if( point != null ) return point.x; } } return 1; } @Override public Point getGrid(int row, int column) { if( gridInfo != null && row >=0 && row < gridInfo.size() ){ List<Point> gridRow = gridInfo.get(row); if( gridRow != null && column >=0 && column < gridRow.size() ){ return gridRow.get(column); } } return getDefaultPoint(); } @Override public int getRowGrid(int row, int column) { if( gridInfo != null && row >=0 && row < gridInfo.size() ){ List<Point> gridRow = gridInfo.get(row); if( gridRow != null && column >=0 && column < gridRow.size() ){ Point point = gridRow.get(column); if( point != null ) return point.y; } } return 1; } protected boolean setGrid(int row, int column, Point grid) { if( gridInfo != null && row >=0 && row < gridInfo.size() ){ List<Point> gridRow = gridInfo.get(row); if( gridRow != null && column >=0 && column < gridRow.size() ){ Point point = gridRow.get(column); if( point != null ){ point.setLocation(grid); } else{ gridRow.set(column, grid.getLocation()); } return true; } } return false; } @Override public boolean spliteCellAt(int row, int column) { if( gridInfo != null && row >=0 && row < gridInfo.size() ){ List<Point> gridRow = gridInfo.get(row); if( gridRow != null && column >=0 && column < gridRow.size() ){ Point point = gridRow.get(column); if( point != null ){ point = point.getLocation(); for(int a=0; a<point.y; a++){ for(int b=0; b<point.x; b++){ setGrid(row+a, column+b, getDefaultPoint()); } } } else{ gridRow.set(column, getDefaultPoint()); } return true; } } return false; } @Override /** * table中发生行的添加和删除的时候需要修改该模型 */ public void tableChanged(TableModelEvent e) { //TODO } @Override public boolean mergeCells(int[] rows, int[] columns) { if( !canMergeCells(rows, columns) ) return false; Arrays.sort(rows); Arrays.sort(columns); return mergeCells(rows[0],rows[rows.length-1],columns[0],columns[columns.length-1]); } @Override public boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn) { setGrid(startRow, startColumn, new Point(endColumn-startColumn+1, endRow-startRow+1)); for(int row=startRow; row<=endRow; row++){ for(int col=startColumn; col<=endColumn; col++){ if(row==startRow&&col==startColumn) continue; else setGrid(row, col, new Point(COVERED,COVERED)); } } return true; } public String toString(){ if( gridInfo == null ) return ""; StringBuffer sb = new StringBuffer(); for(List<Point> rowInfo : gridInfo ){ for(Point grid : rowInfo){ sb.append("["+grid.x+","+grid.y+"], "); } sb.append("\n"); } return sb.toString(); } }

Java代码
  1. packageorg.dxj.guitools.gridbagtable;
  2. importjava.awt.Color;
  3. importjava.awt.Component;
  4. importjava.awt.Dimension;
  5. importjava.awt.Graphics;
  6. importjava.awt.Point;
  7. importjava.awt.Rectangle;
  8. importjava.util.Enumeration;
  9. importjavax.swing.BorderFactory;
  10. importjavax.swing.JComponent;
  11. importjavax.swing.JTable;
  12. importjavax.swing.UIManager;
  13. importjavax.swing.plaf.basic.BasicTableUI;
  14. importjavax.swing.table.JTableHeader;
  15. importjavax.swing.table.TableCellRenderer;
  16. importjavax.swing.table.TableColumn;
  17. importjavax.swing.table.TableColumnModel;
  18. publicclassGridBagTableUIextendsBasicTableUI
  19. {
  20. publicDimensiongetPreferredSize(JComponentc){
  21. longwidth=0;
  22. Enumeration<TableColumn>enumeration=table.getColumnModel().getColumns();
  23. while(enumeration.hasMoreElements()){
  24. TableColumnaColumn=(TableColumn)enumeration.nextElement();
  25. width=width+aColumn.getPreferredWidth();
  26. }
  27. returncreateTableSize(width);
  28. }
  29. privateDimensioncreateTableSize(longwidth){
  30. intheight=0;
  31. introwCount=table.getRowCount();
  32. if(rowCount>0&&table.getColumnCount()>0){
  33. Rectangler=table.getCellRect(rowCount-1,0,true);
  34. height=r.y+r.height;
  35. }
  36. //Widthisalwayspositive.Thecalltoabs()isaworkaroundfor
  37. //abuginthe1.1.6JITonWindows.
  38. longtmp=Math.abs(width);
  39. if(tmp>Integer.MAX_VALUE){
  40. tmp=Integer.MAX_VALUE;
  41. }
  42. returnnewDimension((int)tmp,height);
  43. }
  44. publicvoidpaint(Graphicsg,JComponentc){
  45. Rectangleclip=g.getClipBounds();
  46. Rectanglebounds=table.getBounds();
  47. //accountforthefactthatthegraphicshasalreadybeentranslated
  48. //intothetable'sbounds
  49. bounds.x=bounds.y=0;
  50. if(table.getRowCount()<=0||table.getColumnCount()<=0||
  51. //thischeckpreventsusfrompaintingtheentiretable
  52. //whentheclipdoesn'tintersectourboundsatall
  53. !bounds.intersects(clip)){
  54. paintDropLines(g);
  55. return;
  56. }
  57. booleanltr=table.getComponentOrientation().isLeftToRight();
  58. PointupperLeft=clip.getLocation();
  59. if(!ltr){
  60. upperLeft.x++;
  61. }
  62. PointlowerRight=newPoint(clip.x+clip.width-(ltr?1:0),
  63. clip.y+clip.height);
  64. intrMin=table.rowAtPoint(upperLeft);
  65. intrMax=table.rowAtPoint(lowerRight);
  66. //Thisshouldneverhappen(aslongasourboundsintersecttheclip,
  67. //whichiswhywebailaboveifthatisthecase).
  68. if(rMin==-1){
  69. rMin=0;
  70. }
  71. //Ifthetabledoesnothaveenoughrowstofilltheviewwe'llget-1.
  72. //(Wecouldalsoget-1ifourboundsdon'tintersecttheclip,
  73. //whichiswhywebailaboveifthatisthecase).
  74. //Replacethiswiththeindexofthelastrow.
  75. if(rMax==-1){
  76. rMax=table.getRowCount()-1;
  77. }
  78. intcMin=table.columnAtPoint(ltr?upperLeft:lowerRight);
  79. intcMax=table.columnAtPoint(ltr?lowerRight:upperLeft);
  80. //Thisshouldneverhappen.
  81. if(cMin==-1){
  82. cMin=0;
  83. }
  84. //Ifthetabledoesnothaveenoughcolumnstofilltheviewwe'llget-1.
  85. //Replacethiswiththeindexofthelastcolumn.
  86. if(cMax==-1){
  87. cMax=table.getColumnCount()-1;
  88. }
  89. //Paintthegrid.
  90. //paintGrid(g,rMin,rMax,cMin,cMax);
  91. //Paintthecells.
  92. paintCells(g,rMin,rMax,cMin,cMax);
  93. paintDropLines(g);
  94. }
  95. privatevoidpaintDropLines(Graphicsg){
  96. JTable.DropLocationloc=table.getDropLocation();
  97. if(loc==null){
  98. return;
  99. }
  100. Colorcolor=UIManager.getColor("Table.dropLineColor");
  101. ColorshortColor=UIManager.getColor("Table.dropLineShortColor");
  102. if(color==null&&shortColor==null){
  103. return;
  104. }
  105. Rectanglerect;
  106. rect=getHDropLineRect(loc);
  107. if(rect!=null){
  108. intx=rect.x;
  109. intw=rect.width;
  110. if(color!=null){
  111. extendRect(rect,true);
  112. g.setColor(color);
  113. g.fillRect(rect.x,rect.y,rect.width,rect.height);
  114. }
  115. if(!loc.isInsertColumn()&&shortColor!=null){
  116. g.setColor(shortColor);
  117. g.fillRect(x,rect.y,w,rect.height);
  118. }
  119. }
  120. rect=getVDropLineRect(loc);
  121. if(rect!=null){
  122. inty=rect.y;
  123. inth=rect.height;
  124. if(color!=null){
  125. extendRect(rect,false);
  126. g.setColor(color);
  127. g.fillRect(rect.x,rect.y,rect.width,rect.height);
  128. }
  129. if(!loc.isInsertRow()&&shortColor!=null){
  130. g.setColor(shortColor);
  131. g.fillRect(rect.x,y,rect.width,h);
  132. }
  133. }
  134. }
  135. /*
  136. *Paintsthegridlineswithin<I>aRect</I>,usingthegrid
  137. *colorsetwith<I>setGridColor</I>.Paintsverticallines
  138. *if<code>getShowVerticalLines()</code>returnstrueandpaints
  139. *horizontallinesif<code>getShowHorizontalLines()</code>
  140. *returnstrue.
  141. */
  142. privatevoidpaintGrid(Graphicsg,intrMin,intrMax,intcMin,intcMax){
  143. g.setColor(table.getGridColor());
  144. RectangleminCell=table.getCellRect(rMin,cMin,true);
  145. RectanglemaxCell=table.getCellRect(rMax,cMax,true);
  146. RectangledamagedArea=minCell.union(maxCell);
  147. if(table.getShowHorizontalLines()){
  148. inttableWidth=damagedArea.x+damagedArea.width;
  149. inty=damagedArea.y;
  150. for(introw=rMin;row<=rMax;row++){
  151. y+=table.getRowHeight(row);
  152. g.drawLine(damagedArea.x,y-1,tableWidth-1,y-1);
  153. }
  154. }
  155. if(table.getShowVerticalLines()){
  156. TableColumnModelcm=table.getColumnModel();
  157. inttableHeight=damagedArea.y+damagedArea.height;
  158. intx;
  159. if(table.getComponentOrientation().isLeftToRight()){
  160. x=damagedArea.x;
  161. for(intcolumn=cMin;column<=cMax;column++){
  162. intw=cm.getColumn(column).getWidth();
  163. x+=w;
  164. g.drawLine(x-1,0,x-1,tableHeight-1);
  165. }
  166. }else{
  167. x=damagedArea.x;
  168. for(intcolumn=cMax;column>=cMin;column--){
  169. intw=cm.getColumn(column).getWidth();
  170. x+=w;
  171. g.drawLine(x-1,0,x-1,tableHeight-1);
  172. }
  173. }
  174. }
  175. }
  176. privatevoidpaintCells(Graphicsg,intrMin,intrMax,intcMin,intcMax){
  177. JTableHeaderheader=table.getTableHeader();
  178. TableColumndraggedColumn=(header==null)?null:header.getDraggedColumn();
  179. TableColumnModelcm=table.getColumnModel();
  180. intcolumnMargin=cm.getColumnMargin();
  181. RectanglecellRect;
  182. TableColumnaColumn;
  183. intcolumnWidth;
  184. if(table.getComponentOrientation().isLeftToRight()){
  185. for(introw=rMin;row<=rMax;row++){
  186. if(tableinstanceofGridBagTable)
  187. cellRect=((GridBagTable)table).getGridCellRect(row,cMin,false);
  188. else
  189. cellRect=table.getCellRect(row,cMin,false);
  190. for(intcolumn=cMin;column<=cMax;column++){
  191. aColumn=cm.getColumn(column);
  192. columnWidth=aColumn.getWidth();
  193. //TODO
  194. cellRect.width=columnWidth-columnMargin;
  195. intoldHeight=cellRect.height;
  196. if(tableinstanceofGridBagTable){
  197. if(((GridBagTable)table).getGridBagModel().getCellState(row,column)==GridBagModel.COVERED){
  198. cellRect.width=0;
  199. cellRect.height=0;
  200. }
  201. else{
  202. inth=((GridBagTable)table).getGridBagModel().getColumnGrid(row,column);
  203. if(h>1){
  204. for(intn=1;n<h;n++)
  205. cellRect.width+=cm.getColumn(column+n).getWidth();
  206. }
  207. intv=((GridBagTable)table).getGridBagModel().getRowGrid(row,column);
  208. if(v>1){
  209. for(intn=1;n<v;n++)
  210. cellRect.height+=table.getRowHeight(row+n);
  211. }
  212. }
  213. }
  214. if(aColumn!=draggedColumn){
  215. paintCell(g,cellRect,row,column);
  216. }
  217. cellRect.height=oldHeight;
  218. cellRect.x+=columnWidth;
  219. }
  220. }
  221. }else{
  222. for(introw=rMin;row<=rMax;row++){
  223. cellRect=table.getCellRect(row,cMin,false);
  224. aColumn=cm.getColumn(cMin);
  225. if(aColumn!=draggedColumn){
  226. columnWidth=aColumn.getWidth();
  227. cellRect.width=columnWidth-columnMargin;
  228. paintCell(g,cellRect,row,cMin);
  229. }
  230. for(intcolumn=cMin+1;column<=cMax;column++){
  231. aColumn=cm.getColumn(column);
  232. columnWidth=aColumn.getWidth();
  233. //TODO
  234. cellRect.width=columnWidth-columnMargin;
  235. cellRect.x-=columnWidth;
  236. if(aColumn!=draggedColumn){
  237. paintCell(g,cellRect,row,column);
  238. }
  239. }
  240. }
  241. }
  242. //Paintthedraggedcolumnifwearedragging.
  243. if(draggedColumn!=null){
  244. paintDraggedArea(g,rMin,rMax,draggedColumn,header.getDraggedDistance());
  245. }
  246. //RemoveanyrenderersthatmaybeleftintherendererPane.
  247. rendererPane.removeAll();
  248. }
  249. privatevoidpaintCell(Graphicsg,RectanglecellRect,introw,intcolumn){
  250. if(table.isEditing()&&table.getEditingRow()==row&&
  251. table.getEditingColumn()==column){
  252. Componentcomponent=table.getEditorComponent();
  253. component.setBounds(cellRect);
  254. component.validate();
  255. }
  256. else{
  257. TableCellRendererrenderer=table.getCellRenderer(row,column);
  258. Componentcomponent=table.prepareRenderer(renderer,row,column);
  259. if(componentinstanceofJComponent){
  260. ((JComponent)component).setBorder(BorderFactory.createLineBorder(Color.gray));
  261. }
  262. rendererPane.paintComponent(g,component,table,cellRect.x,cellRect.y,
  263. cellRect.width,cellRect.height,true);
  264. }
  265. }
  266. privateRectanglegetHDropLineRect(JTable.DropLocationloc){
  267. if(!loc.isInsertRow()){
  268. returnnull;
  269. }
  270. introw=loc.getRow();
  271. intcol=loc.getColumn();
  272. if(col>=table.getColumnCount()){
  273. col--;
  274. }
  275. Rectanglerect=table.getCellRect(row,col,true);
  276. if(row>=table.getRowCount()){
  277. row--;
  278. RectangleprevRect=table.getCellRect(row,col,true);
  279. rect.y=prevRect.y+prevRect.height;
  280. }
  281. if(rect.y==0){
  282. rect.y=-1;
  283. }else{
  284. rect.y-=2;
  285. }
  286. rect.height=3;
  287. returnrect;
  288. }
  289. privatevoidpaintDraggedArea(Graphicsg,intrMin,intrMax,TableColumndraggedColumn,intdistance){
  290. intdraggedColumnIndex=viewIndexForColumn(draggedColumn);
  291. RectangleminCell=table.getCellRect(rMin,draggedColumnIndex,true);
  292. RectanglemaxCell=table.getCellRect(rMax,draggedColumnIndex,true);
  293. RectanglevacatedColumnRect=minCell.union(maxCell);
  294. //Paintagraywellinplaceofthemovingcolumn.
  295. g.setColor(table.getParent().getBackground());
  296. g.fillRect(vacatedColumnRect.x,vacatedColumnRect.y,
  297. vacatedColumnRect.width,vacatedColumnRect.height);
  298. //Movetothewherethecellhasbeendragged.
  299. vacatedColumnRect.x+=distance;
  300. //Fillthebackground.
  301. g.setColor(table.getBackground());
  302. g.fillRect(vacatedColumnRect.x,vacatedColumnRect.y,
  303. vacatedColumnRect.width,vacatedColumnRect.height);
  304. //Painttheverticalgridlinesifnecessary.
  305. if(table.getShowVerticalLines()){
  306. g.setColor(table.getGridColor());
  307. intx1=vacatedColumnRect.x;
  308. inty1=vacatedColumnRect.y;
  309. intx2=x1+vacatedColumnRect.width-1;
  310. inty2=y1+vacatedColumnRect.height-1;
  311. //Left
  312. g.drawLine(x1-1,y1,x1-1,y2);
  313. //Right
  314. g.drawLine(x2,y1,x2,y2);
  315. }
  316. for(introw=rMin;row<=rMax;row++){
  317. //Renderthecellvalue
  318. Rectangler=table.getCellRect(row,draggedColumnIndex,false);
  319. r.x+=distance;
  320. paintCell(g,r,row,draggedColumnIndex);
  321. //Paintthe(lower)horizontalgridlineifnecessary.
  322. if(table.getShowHorizontalLines()){
  323. g.setColor(table.getGridColor());
  324. Rectanglercr=table.getCellRect(row,draggedColumnIndex,true);
  325. rcr.x+=distance;
  326. intx1=rcr.x;
  327. inty1=rcr.y;
  328. intx2=x1+rcr.width-1;
  329. inty2=y1+rcr.height-1;
  330. g.drawLine(x1,y2,x2,y2);
  331. }
  332. }
  333. }
  334. privateintviewIndexForColumn(TableColumnaColumn){
  335. TableColumnModelcm=table.getColumnModel();
  336. for(intcolumn=0;column<cm.getColumnCount();column++){
  337. if(cm.getColumn(column)==aColumn){
  338. returncolumn;
  339. }
  340. }
  341. return-1;
  342. }
  343. privateRectangleextendRect(Rectanglerect,booleanhorizontal){
  344. if(rect==null){
  345. returnrect;
  346. }
  347. if(horizontal){
  348. rect.x=0;
  349. rect.width=table.getWidth();
  350. }else{
  351. rect.y=0;
  352. if(table.getRowCount()!=0){
  353. RectanglelastRect=table.getCellRect(table.getRowCount()-1,0,true);
  354. rect.height=lastRect.y+lastRect.height;
  355. }else{
  356. rect.height=table.getHeight();
  357. }
  358. }
  359. returnrect;
  360. }
  361. privateRectanglegetVDropLineRect(JTable.DropLocationloc){
  362. if(!loc.isInsertColumn()){
  363. returnnull;
  364. }
  365. booleanltr=table.getComponentOrientation().isLeftToRight();
  366. intcol=loc.getColumn();
  367. Rectanglerect=table.getCellRect(loc.getRow(),col,true);
  368. if(col>=table.getColumnCount()){
  369. col--;
  370. rect=table.getCellRect(loc.getRow(),col,true);
  371. if(ltr){
  372. rect.x=rect.x+rect.width;
  373. }
  374. }elseif(!ltr){
  375. rect.x=rect.x+rect.width;
  376. }
  377. if(rect.x==0){
  378. rect.x=-1;
  379. }else{
  380. rect.x-=2;
  381. }
  382. rect.width=3;
  383. returnrect;
  384. }
  385. }//EndofClassBasicTableUI

package org.dxj.guitools.gridbagtable; import java.awt.Color; 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 javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.UIManager; import javax.swing.plaf.basic.BasicTableUI; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; public class GridBagTableUI extends BasicTableUI { public Dimension getPreferredSize(JComponent c) { long width = 0; Enumeration<TableColumn> enumeration = table.getColumnModel().getColumns(); while (enumeration.hasMoreElements()) { TableColumn aColumn = (TableColumn)enumeration.nextElement(); width = width + aColumn.getPreferredWidth(); } return createTableSize(width); } private Dimension createTableSize(long width) { int height = 0; int rowCount = table.getRowCount(); if (rowCount > 0 && table.getColumnCount() > 0) { Rectangle r = table.getCellRect(rowCount-1, 0, true); height = r.y + r.height; } // Width is always positive. The call to abs() is a workaround for // a bug in the 1.1.6 JIT on Windows. long tmp = Math.abs(width); if (tmp > Integer.MAX_VALUE) { tmp = Integer.MAX_VALUE; } return new Dimension((int)tmp, height); } public void paint(Graphics g, JComponent c) { Rectangle clip = g.getClipBounds(); Rectangle bounds = table.getBounds(); // account for the fact that the graphics has already been translated // into the table's bounds bounds.x = bounds.y = 0; if (table.getRowCount() <= 0 || table.getColumnCount() <= 0 || // this check prevents us from painting the entire table // when the clip doesn't intersect our bounds at all !bounds.intersects(clip)) { paintDropLines(g); return; } boolean ltr = table.getComponentOrientation().isLeftToRight(); Point upperLeft = clip.getLocation(); if (!ltr) { upperLeft.x++; } Point lowerRight = new Point(clip.x + clip.width - (ltr ? 1 : 0), clip.y + clip.height); int rMin = table.rowAtPoint(upperLeft); int rMax = table.rowAtPoint(lowerRight); // This should never happen (as long as our bounds intersect the clip, // which is why we bail above if that is the case). if (rMin == -1) { rMin = 0; } // If the table does not have enough rows to fill the view we'll get -1. // (We could also get -1 if our bounds don't intersect the clip, // which is why we bail above if that is the case). // Replace this with the index of the last row. if (rMax == -1) { rMax = table.getRowCount()-1; } int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight); int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft); // 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 = table.getColumnCount()-1; } // Paint the grid. //paintGrid(g, rMin, rMax, cMin, cMax); // Paint the cells. paintCells(g, rMin, rMax, cMin, cMax); paintDropLines(g); } private void paintDropLines(Graphics g) { JTable.DropLocation loc = table.getDropLocation(); if (loc == null) { return; } Color color = UIManager.getColor("Table.dropLineColor"); Color shortColor = UIManager.getColor("Table.dropLineShortColor"); if (color == null && shortColor == null) { return; } Rectangle rect; rect = getHDropLineRect(loc); if (rect != null) { int x = rect.x; int w = rect.width; if (color != null) { extendRect(rect, true); g.setColor(color); g.fillRect(rect.x, rect.y, rect.width, rect.height); } if (!loc.isInsertColumn() && shortColor != null) { g.setColor(shortColor); g.fillRect(x, rect.y, w, rect.height); } } rect = getVDropLineRect(loc); if (rect != null) { int y = rect.y; int h = rect.height; if (color != null) { extendRect(rect, false); g.setColor(color); g.fillRect(rect.x, rect.y, rect.width, rect.height); } if (!loc.isInsertRow() && shortColor != null) { g.setColor(shortColor); g.fillRect(rect.x, y, rect.width, h); } } } /* * Paints the grid lines within <I>aRect</I>, using the grid * color set with <I>setGridColor</I>. Paints vertical lines * if <code>getShowVerticalLines()</code> returns true and paints * horizontal lines if <code>getShowHorizontalLines()</code> * returns true. */ private void paintGrid(Graphics g, int rMin, int rMax, int cMin, int cMax) { g.setColor(table.getGridColor()); Rectangle minCell = table.getCellRect(rMin, cMin, true); Rectangle maxCell = table.getCellRect(rMax, cMax, true); Rectangle damagedArea = minCell.union( maxCell ); if (table.getShowHorizontalLines()) { int tableWidth = damagedArea.x + damagedArea.width; int y = damagedArea.y; for (int row = rMin; row <= rMax; row++) { y += table.getRowHeight(row); g.drawLine(damagedArea.x, y - 1, tableWidth - 1, y - 1); } } if (table.getShowVerticalLines()) { TableColumnModel cm = table.getColumnModel(); int tableHeight = damagedArea.y + damagedArea.height; int x; if (table.getComponentOrientation().isLeftToRight()) { x = damagedArea.x; for (int column = cMin; column <= cMax; column++) { int w = cm.getColumn(column).getWidth(); x += w; g.drawLine(x - 1, 0, x - 1, tableHeight - 1); } } else { x = damagedArea.x; for (int column = cMax; column >= cMin; column--) { int w = cm.getColumn(column).getWidth(); x += w; g.drawLine(x - 1, 0, x - 1, tableHeight - 1); } } } } private void paintCells(Graphics g, int rMin, int rMax, int cMin, int cMax) { JTableHeader header = table.getTableHeader(); TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn(); TableColumnModel cm = table.getColumnModel(); int columnMargin = cm.getColumnMargin(); Rectangle cellRect; TableColumn aColumn; int columnWidth; if (table.getComponentOrientation().isLeftToRight()) { for(int row = rMin; row <= rMax; row++) { if( table instanceof GridBagTable ) cellRect = ((GridBagTable)table).getGridCellRect(row, cMin, false); else cellRect = table.getCellRect(row, cMin, false); for(int column = cMin; column <= cMax; column++) { aColumn = cm.getColumn(column); columnWidth = aColumn.getWidth(); //TODO cellRect.width = columnWidth - columnMargin; int oldHeight = cellRect.height; if( table instanceof GridBagTable ){ if(((GridBagTable)table).getGridBagModel().getCellState( row, column) == GridBagModel.COVERED ) { cellRect.width = 0; cellRect.height = 0; } else{ int h = ((GridBagTable)table).getGridBagModel().getColumnGrid(row, column); if( h >1 ){ for( int n=1; n<h; n++) cellRect.width += cm.getColumn(column+n).getWidth(); } int v = ((GridBagTable)table).getGridBagModel().getRowGrid(row, column); if( v >1 ){ for( int n=1; n<v; n++) cellRect.height += table.getRowHeight(row+n); } } } if (aColumn != draggedColumn) { paintCell(g, cellRect, row, column); } cellRect.height = oldHeight; cellRect.x += columnWidth; } } } else { for(int row = rMin; row <= rMax; row++) { cellRect = table.getCellRect(row, cMin, false); aColumn = cm.getColumn(cMin); if (aColumn != draggedColumn) { columnWidth = aColumn.getWidth(); cellRect.width = columnWidth - columnMargin; paintCell(g, cellRect, row, cMin); } for(int column = cMin+1; column <= cMax; column++) { aColumn = cm.getColumn(column); columnWidth = aColumn.getWidth(); // TODO cellRect.width = columnWidth - columnMargin; cellRect.x -= columnWidth; if (aColumn != draggedColumn) { paintCell(g, cellRect, row, column); } } } } // Paint the dragged column if we are dragging. if (draggedColumn != null) { paintDraggedArea(g, rMin, rMax, draggedColumn, header.getDraggedDistance()); } // Remove any renderers that may be left in the rendererPane. rendererPane.removeAll(); } private void paintCell(Graphics g, Rectangle cellRect, int row, int column) { if (table.isEditing() && table.getEditingRow()==row && table.getEditingColumn()==column) { Component component = table.getEditorComponent(); component.setBounds(cellRect); component.validate(); } else { TableCellRenderer renderer = table.getCellRenderer(row, column); Component component = table.prepareRenderer(renderer, row, column); if( component instanceof JComponent ){ ((JComponent)component).setBorder(BorderFactory.createLineBorder(Color.gray)); } rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y, cellRect.width, cellRect.height, true); } } private Rectangle getHDropLineRect(JTable.DropLocation loc) { if (!loc.isInsertRow()) { return null; } int row = loc.getRow(); int col = loc.getColumn(); if (col >= table.getColumnCount()) { col--; } Rectangle rect = table.getCellRect(row, col, true); if (row >= table.getRowCount()) { row--; Rectangle prevRect = table.getCellRect(row, col, true); rect.y = prevRect.y + prevRect.height; } if (rect.y == 0) { rect.y = -1; } else { rect.y -= 2; } rect.height = 3; return rect; } private void paintDraggedArea(Graphics g, int rMin, int rMax, TableColumn draggedColumn, int distance) { int draggedColumnIndex = viewIndexForColumn(draggedColumn); Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true); Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true); Rectangle vacatedColumnRect = minCell.union(maxCell); // Paint a gray well in place of the moving column. g.setColor(table.getParent().getBackground()); g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y, vacatedColumnRect.width, vacatedColumnRect.height); // Move to the where the cell has been dragged. vacatedColumnRect.x += distance; // Fill the background. g.setColor(table.getBackground()); g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y, vacatedColumnRect.width, vacatedColumnRect.height); // Paint the vertical grid lines if necessary. if (table.getShowVerticalLines()) { g.setColor(table.getGridColor()); int x1 = vacatedColumnRect.x; int y1 = vacatedColumnRect.y; int x2 = x1 + vacatedColumnRect.width - 1; int y2 = y1 + vacatedColumnRect.height - 1; // Left g.drawLine(x1-1, y1, x1-1, y2); // Right g.drawLine(x2, y1, x2, y2); } for(int row = rMin; row <= rMax; row++) { // Render the cell value Rectangle r = table.getCellRect(row, draggedColumnIndex, false); r.x += distance; paintCell(g, r, row, draggedColumnIndex); // Paint the (lower) horizontal grid line if necessary. if (table.getShowHorizontalLines()) { g.setColor(table.getGridColor()); Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true); rcr.x += distance; int x1 = rcr.x; int y1 = rcr.y; int x2 = x1 + rcr.width - 1; int y2 = y1 + rcr.height - 1; g.drawLine(x1, y2, x2, y2); } } } private int viewIndexForColumn(TableColumn aColumn) { TableColumnModel cm = table.getColumnModel(); for (int column = 0; column < cm.getColumnCount(); column++) { if (cm.getColumn(column) == aColumn) { return column; } } return -1; } private Rectangle extendRect(Rectangle rect, boolean horizontal) { if (rect == null) { return rect; } if (horizontal) { rect.x = 0; rect.width = table.getWidth(); } else { rect.y = 0; if (table.getRowCount() != 0) { Rectangle lastRect = table.getCellRect(table.getRowCount() - 1, 0, true); rect.height = lastRect.y + lastRect.height; } else { rect.height = table.getHeight(); } } return rect; } private Rectangle getVDropLineRect(JTable.DropLocation loc) { if (!loc.isInsertColumn()) { return null; } boolean ltr = table.getComponentOrientation().isLeftToRight(); int col = loc.getColumn(); Rectangle rect = table.getCellRect(loc.getRow(), col, true); if (col >= table.getColumnCount()) { col--; rect = table.getCellRect(loc.getRow(), col, true); if (ltr) { rect.x = rect.x + rect.width; } } else if (!ltr) { rect.x = rect.x + rect.width; } if (rect.x == 0) { rect.x = -1; } else { rect.x -= 2; } rect.width = 3; return rect; } } // End of Class BasicTableUI

测试代码:

Java代码
  1. importjava.awt.BorderLayout;
  2. importjava.awt.event.ActionEvent;
  3. importjava.awt.event.ActionListener;
  4. importjavax.swing.JButton;
  5. importjavax.swing.JFrame;
  6. importjavax.swing.JScrollPane;
  7. importjavax.swing.table.DefaultTableModel;
  8. importcom.jrf.jgrid.guitools.gridbagtable.GridBagTable;
  9. publicclassTestimplementsActionListener{
  10. GridBagTabletable;
  11. publicTest()
  12. {
  13. JFramed=newJFrame();
  14. DefaultTableModelmodel=newDefaultTableModel(5,5);
  15. table=newGridBagTable(model);
  16. table.setRowHeight(20);
  17. JScrollPanepane=newJScrollPane(table);
  18. d.getContentPane().add(pane,BorderLayout.CENTER);
  19. JButtonbtn=newJButton("合并/拆分");
  20. d.getContentPane().add(btn,BorderLayout.NORTH);
  21. btn.addActionListener(this);
  22. d.setBounds(0,0,400,400);
  23. d.setVisible(true);
  24. }
  25. publicstaticvoidmain(String[]fsd){
  26. newTest();
  27. }
  28. publicvoidactionPerformed(ActionEvente){
  29. table.mergeCells(table.getSelectedRows(),table.getSelectedColumns());
  30. }
  31. }年前在网上参加了一个JavaSwing的招聘上机考试。招聘方要求开发一个类似EXCEL支持单元格合并的JTable。差不多用了5天的时间提交代码,最后被告知测试通过,我提出是否可做兼职,对方回复需要到上海做全职开发,最后也就放弃了。最近公司的一个项目中需要用到以前的代码,偶又重构了一次,设计思想来源于ListSelectionModel。

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

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

    Java代码
    1. packageorg.dxj.guitools.gridbagtable;
    2. importjava.awt.Component;
    3. importjava.awt.Point;
    4. importjava.awt.Rectangle;
    5. importjava.util.Enumeration;
    6. importjava.util.EventObject;
    7. importjavax.swing.DefaultCellEditor;
    8. importjavax.swing.JTable;
    9. importjavax.swing.SwingUtilities;
    10. importjavax.swing.event.TableModelEvent;
    11. importjavax.swing.table.AbstractTableModel;
    12. importjavax.swing.table.TableColumn;
    13. importjavax.swing.table.TableColumnModel;
    14. /**
    15. *@author15860102@qq.com
    16. */
    17. publicclassGridBagTableextendsJTable{
    18. GridBagModelgridBagModel;
    19. publicGridBagModelgetGridBagModel(){
    20. returngridBagModel;
    21. }
    22. publicvoidsetGridBagModel(GridBagModelgridBagModel){
    23. if(gridBagModel!=null&&gridBagModel!=this.gridBagModel)
    24. this.gridBagModel=gridBagModel;
    25. }
    26. publicGridBagTable(AbstractTableModeldm){
    27. super(dm);
    28. getTableHeader().setReorderingAllowed(false);
    29. gridBagModel=newDefaultGridBagTableModel(dm);
    30. getColumnModel().setColumnSelectionAllowed(true);
    31. }
    32. privatevoidupdateSubComponentUI(ObjectcomponentShell){
    33. if(componentShell==null){
    34. return;
    35. }
    36. Componentcomponent=null;
    37. if(componentShellinstanceofComponent){
    38. component=(Component)componentShell;
    39. }
    40. if(componentShellinstanceofDefaultCellEditor){
    41. component=((DefaultCellEditor)componentShell).getComponent();
    42. }
    43. if(component!=null){
    44. SwingUtilities.updateComponentTreeUI(component);
    45. }
    46. }
    47. publicvoidupdateUI(){
    48. //UpdatetheUIsofthecellrenderers,celleditorsandheaderrenderers.
    49. TableColumnModelcm=getColumnModel();
    50. for(intcolumn=0;column<cm.getColumnCount();column++){
    51. TableColumnaColumn=cm.getColumn(column);
    52. updateSubComponentUI(aColumn.getCellRenderer());
    53. updateSubComponentUI(aColumn.getCellEditor());
    54. updateSubComponentUI(aColumn.getHeaderRenderer());
    55. }
    56. //UpdatetheUIsofallthedefaultrenderers.
    57. EnumerationdefaultRenderers=defaultRenderersByColumnClass.elements();
    58. while(defaultRenderers.hasMoreElements()){
    59. updateSubComponentUI(defaultRenderers.nextElement());
    60. }
    61. //UpdatetheUIsofallthedefaulteditors.
    62. EnumerationdefaultEditors=defaultEditorsByColumnClass.elements();
    63. while(defaultEditors.hasMoreElements()){
    64. updateSubComponentUI(defaultEditors.nextElement());
    65. }
    66. //UpdatetheUIofthetableheader
    67. if(tableHeader!=null&&tableHeader.getParent()==null){
    68. tableHeader.updateUI();
    69. }
    70. setUI(newGridBagTableUI());
    71. }
    72. publicRectanglegetGridCellRect(introw,intcolumn,booleanincludeSpacing){
    73. returnsuper.getCellRect(row,column,includeSpacing);
    74. }
    75. publicRectanglegetCellRect(introw,intcolumn,booleanincludeSpacing){
    76. RectanglecellRect=super.getCellRect(row,column,includeSpacing);
    77. intcols=gridBagModel.getColumnGrid(row,column);
    78. TableColumnModelcm=getColumnModel();
    79. for(intn=1;n<cols;n++)
    80. cellRect.width+=cm.getColumn(column+n).getWidth();
    81. introws=gridBagModel.getRowGrid(row,column);
    82. for(intn=1;n<rows;n++)
    83. cellRect.height+=getRowHeight(row+n);
    84. returncellRect;
    85. }
    86. publicvoidtableChanged(TableModelEvente){
    87. super.tableChanged(e);
    88. //TODO
    89. }
    90. publicbooleanmergeCells(intstartRow,intendRow,intstartColumn,intendColumn){
    91. if(gridBagModel.mergeCells(startRow,endRow,startColumn,endColumn)){
    92. repaint();
    93. returntrue;
    94. }
    95. returnfalse;
    96. }
    97. publicbooleanmergeCells(int[]rows,int[]columns){
    98. if(gridBagModel.mergeCells(rows,columns)){
    99. repaint();
    100. returntrue;
    101. }
    102. returnfalse;
    103. }
    104. publicbooleanspliteCellAt(introw,intcolumn){
    105. if(gridBagModel.spliteCellAt(row,column)){
    106. repaint();
    107. returntrue;
    108. }
    109. returnfalse;
    110. }
    111. publicvoidchangeSelection(introwIndex,intcolumnIndex,booleantoggle,booleanextend){
    112. if(gridBagModel.getCellState(rowIndex,columnIndex)!=GridBagModel.COVERED)
    113. super.changeSelection(rowIndex,columnIndex,toggle,extend);
    114. Pointp;
    115. for(introw=rowIndex;row>=0;row--){
    116. for(intcol=columnIndex;col>=0;col--){
    117. p=gridBagModel.getGrid(row,col);
    118. //p=((Point)((Vector)rowVector.get(row)).get(col));
    119. if(col+p.x>columnIndex&&row+p.y>rowIndex){
    120. rowIndex=row;
    121. columnIndex=col;
    122. break;
    123. }
    124. }
    125. }
    126. super.changeSelection(rowIndex,columnIndex,toggle,extend);
    127. repaint();
    128. }
    129. publicbooleaneditCellAt(introwIndex,intcolumnIndex,EventObjecte){
    130. if(gridBagModel.getCellState(rowIndex,columnIndex)!=GridBagModel.COVERED)
    131. returnsuper.editCellAt(rowIndex,columnIndex,e);
    132. Pointp;
    133. for(introw=rowIndex;row>=0;row--){
    134. for(intcol=columnIndex;col>=0;col--){
    135. p=gridBagModel.getGrid(row,col);
    136. if(col+p.x>columnIndex&&row+p.y>rowIndex){
    137. rowIndex=row;
    138. columnIndex=col;
    139. break;
    140. }
    141. }
    142. }
    143. returnsuper.editCellAt(rowIndex,columnIndex,e);
    144. }
    145. }

    package org.dxj.guitools.gridbagtable; import java.awt.Component; import java.awt.Point; import java.awt.Rectangle; import java.util.Enumeration; import java.util.EventObject; import javax.swing.DefaultCellEditor; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.event.TableModelEvent; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; /** * @author 15860102@qq.com */ public class GridBagTable extends JTable{ GridBagModel gridBagModel; public GridBagModel getGridBagModel() { return gridBagModel; } public void setGridBagModel(GridBagModel gridBagModel){ if( gridBagModel != null && gridBagModel != this.gridBagModel ) this.gridBagModel = gridBagModel; } public GridBagTable(AbstractTableModel dm){ super(dm); getTableHeader().setReorderingAllowed(false); gridBagModel = new DefaultGridBagTableModel(dm); getColumnModel().setColumnSelectionAllowed(true); } private void updateSubComponentUI(Object componentShell) { if (componentShell == null) { return; } Component component = null; if (componentShell instanceof Component) { component = (Component)componentShell; } if (componentShell instanceof DefaultCellEditor) { component = ((DefaultCellEditor)componentShell).getComponent(); } if (component != null) { SwingUtilities.updateComponentTreeUI(component); } } public void updateUI() { // Update the UIs of the cell renderers, cell editors and header renderers. TableColumnModel cm = getColumnModel(); for(int column = 0; column < cm.getColumnCount(); column++) { TableColumn aColumn = cm.getColumn(column); updateSubComponentUI(aColumn.getCellRenderer()); updateSubComponentUI(aColumn.getCellEditor()); updateSubComponentUI(aColumn.getHeaderRenderer()); } // Update the UIs of all the default renderers. Enumeration defaultRenderers = defaultRenderersByColumnClass.elements(); while (defaultRenderers.hasMoreElements()) { updateSubComponentUI(defaultRenderers.nextElement()); } // Update the UIs of all the default editors. Enumeration defaultEditors = defaultEditorsByColumnClass.elements(); while (defaultEditors.hasMoreElements()) { updateSubComponentUI(defaultEditors.nextElement()); } // Update the UI of the table header if (tableHeader != null && tableHeader.getParent() == null) { tableHeader.updateUI(); } setUI(new GridBagTableUI()); } public Rectangle getGridCellRect(int row, int column, boolean includeSpacing){ return super.getCellRect(row, column, includeSpacing); } public Rectangle getCellRect(int row, int column, boolean includeSpacing) { Rectangle cellRect = super.getCellRect(row, column, includeSpacing); int cols = gridBagModel.getColumnGrid(row, column); TableColumnModel cm = getColumnModel(); for( int n=1; n<cols; n++) cellRect.width += cm.getColumn(column+n).getWidth(); int rows = gridBagModel.getRowGrid(row, column); for( int n=1; n<rows; n++) cellRect.height += getRowHeight(row+n); return cellRect; } public void tableChanged(TableModelEvent e){ super.tableChanged(e); //TODO } public boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn){ if( gridBagModel.mergeCells(startRow, endRow, startColumn, endColumn)){ repaint(); return true; } return false; } public boolean mergeCells(int[] rows, int[] columns){ if( gridBagModel.mergeCells(rows, columns)){ repaint(); return true; } return false; } public boolean spliteCellAt(int row, int column){ if( gridBagModel.spliteCellAt(row, column)){ repaint(); return true; } return false; } public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) { if( gridBagModel.getCellState( rowIndex , columnIndex ) != GridBagModel.COVERED ) super.changeSelection(rowIndex, columnIndex, toggle, extend); Point p; for( int row = rowIndex; row >= 0; row-- ){ for( int col = columnIndex; col >= 0; col-- ){ p = gridBagModel.getGrid(row, col); //p = ((Point)((Vector)rowVector.get(row)).get(col)); if( col + p.x > columnIndex && row + p.y > rowIndex){ rowIndex = row; columnIndex = col; break; } } } super.changeSelection(rowIndex, columnIndex, toggle, extend); repaint(); } public boolean editCellAt(int rowIndex, int columnIndex, EventObject e){ if( gridBagModel.getCellState( rowIndex , columnIndex ) != GridBagModel.COVERED ) return super.editCellAt(rowIndex, columnIndex, e); Point p; for( int row = rowIndex; row >= 0; row-- ){ for( int col = columnIndex; col >= 0; col-- ){ p = gridBagModel.getGrid(row, col); if( col + p.x > columnIndex && row + p.y > rowIndex){ rowIndex = row; columnIndex = col; break; } } } return super.editCellAt(rowIndex, columnIndex, e); } }

    Java代码
    1. packageorg.dxj.guitools.gridbagtable;
    2. importjava.awt.Point;
    3. publicinterfaceGridBagModel{
    4. //格子处于正常状态
    5. intDEFAULT=0;
    6. //格子合并了其他的格子
    7. intMERGE=1;
    8. //格子被其他格子合并
    9. intCOVERED=-1;
    10. /**
    11. *@paramrow行
    12. *@paramcolumn列
    13. *@return该单元格在行、列的跨度
    14. */
    15. PointgetGrid(introw,intcolumn);
    16. /**
    17. *在Y轴方向的跨度
    18. *@paramrow
    19. *@paramcolumn
    20. *@return
    21. */
    22. intgetRowGrid(introw,intcolumn);
    23. /**
    24. *在X轴方向的跨度
    25. *@paramrow
    26. *@paramcolumn
    27. *@return
    28. */
    29. intgetColumnGrid(introw,intcolumn);
    30. /**
    31. *@paramrows行集合
    32. *@paramcolumns列集合
    33. *@return单元格集合是否可以合并在一起
    34. */
    35. booleancanMergeCells(int[]rows,int[]columns);
    36. /**
    37. *判断该单元格状态
    38. *@paramrow
    39. *@paramcolumn
    40. *@returnMERGE|DEFAULT|COVERED
    41. */
    42. intgetCellState(introw,intcolumn);
    43. /**
    44. *将单元格集合合并
    45. *@paramstartRow开始行
    46. *@paramendRow结束行
    47. *@paramstartColumn开始列
    48. *@paramendColumn结束列
    49. *@return是否合并成功
    50. */
    51. booleanmergeCells(intstartRow,intendRow,intstartColumn,intendColumn);
    52. /**
    53. *将单元格集合合并
    54. *@paramrows行集合
    55. *@paramcolumns列集合
    56. *@return是否合并成功
    57. */
    58. booleanmergeCells(int[]rows,int[]columns);
    59. /**
    60. *拆分单元格
    61. *@paramrow行
    62. *@paramcolumn列
    63. *@return是否拆分成功
    64. */
    65. booleanspliteCellAt(introw,intcolumn);
    66. /**
    67. *清除所有合并
    68. */
    69. voidclearMergence();
    70. }

    package org.dxj.guitools.gridbagtable; import java.awt.Point; public interface GridBagModel { //格子处于正常状态 int DEFAULT = 0; //格子合并了其他的格子 int MERGE = 1; //格子被其他格子合并 int COVERED = -1; /** * @param row 行 * @param column 列 * @return 该单元格在行、列的跨度 */ Point getGrid(int row, int column); /** * 在Y轴方向的跨度 * @param row * @param column * @return */ int getRowGrid(int row, int column); /** * 在X轴方向的跨度 * @param row * @param column * @return */ int getColumnGrid(int row, int column); /** * @param rows 行集合 * @param columns 列集合 * @return 单元格集合是否可以合并在一起 */ boolean canMergeCells(int[] rows, int[] columns); /** * 判断该单元格状态 * @param row * @param column * @return MERGE|DEFAULT|COVERED */ int getCellState(int row, int column); /** * 将单元格集合合并 * @param startRow 开始行 * @param endRow 结束行 * @param startColumn 开始列 * @param endColumn 结束列 * @return 是否合并成功 */ boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn); /** * 将单元格集合合并 * @param rows 行集合 * @param columns 列集合 * @return 是否合并成功 */ boolean mergeCells(int[] rows, int[] columns); /** * 拆分单元格 * @param row 行 * @param column 列 * @return 是否拆分成功 */ boolean spliteCellAt(int row, int column); /** * 清除 所有合并 */ void clearMergence(); }

    Java代码
    1. packageorg.dxj.guitools.gridbagtable;
    2. importjava.awt.Point;
    3. importjava.util.Arrays;
    4. importjava.util.List;
    5. importjava.util.Vector;
    6. importjavax.swing.event.TableModelEvent;
    7. importjavax.swing.event.TableModelListener;
    8. importjavax.swing.table.AbstractTableModel;
    9. publicclassDefaultGridBagTableModelimplementsGridBagModel,TableModelListener{
    10. protectedAbstractTableModelmodel;
    11. protectedList<List<Point>>gridInfo;
    12. DefaultGridBagTableModel(AbstractTableModelmodel){
    13. gridInfo=newVector<List<Point>>();
    14. setTableModel(model);
    15. }
    16. publicvoidsetTableModel(AbstractTableModelmodel){
    17. if(model!=null&&model!=this.model){
    18. if(this.model!=null)
    19. this.model.removeTableModelListener(this);
    20. //防止多次添加监听器
    21. model.removeTableModelListener(this);
    22. model.addTableModelListener(this);
    23. this.model=model;
    24. clearMergence();
    25. }
    26. }
    27. publicvoidclearMergence(){
    28. if(gridInfo==null)
    29. gridInfo=newVector<List<Point>>();
    30. else
    31. gridInfo.clear();
    32. if(model==null)
    33. return;
    34. //初始化,每个格子占的格子数为(1,1);
    35. for(introw=model.getRowCount();--row>=0;){
    36. List<Point>infos=newVector<Point>();
    37. gridInfo.add(infos);
    38. for(intcol=model.getColumnCount();--col>=0;){
    39. infos.add(getDefaultPoint());
    40. }
    41. }
    42. }
    43. publicPointgetDefaultPoint(){
    44. returnnewPoint(1,1);
    45. }
    46. @Override
    47. publicbooleancanMergeCells(int[]rows,int[]columns){
    48. if(rows==null||columns==null)returnfalse;
    49. Arrays.sort(rows);
    50. for(intindex=0;index<rows.length-1;index++){
    51. if(rows[index+1]-rows[index]>1)
    52. returnfalse;
    53. }
    54. Arrays.sort(columns);
    55. for(intindex=0;index<columns.length-1;index++){
    56. if(columns[index+1]-columns[index]>1)
    57. returnfalse;
    58. }
    59. returntrue;
    60. }
    61. @Override
    62. publicintgetCellState(introw,intcolumn){
    63. Pointgrid=getGrid(row,column);
    64. if(grid==null)returnDEFAULT;
    65. if(grid.x>1||grid.y>1)
    66. returnMERGE;
    67. if(grid.x<=0||grid.y<=0)
    68. returnCOVERED;
    69. returnDEFAULT;
    70. }
    71. @Override
    72. publicintgetColumnGrid(introw,intcolumn){
    73. if(gridInfo!=null&&row>=0&&row<gridInfo.size()){
    74. List<Point>gridRow=gridInfo.get(row);
    75. if(gridRow!=null&&column>=0&&column<gridRow.size()){
    76. Pointpoint=gridRow.get(column);
    77. if(point!=null)
    78. returnpoint.x;
    79. }
    80. }
    81. return1;
    82. }
    83. @Override
    84. publicPointgetGrid(introw,intcolumn){
    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. returngridRow.get(column);
    89. }
    90. }
    91. returngetDefaultPoint();
    92. }
    93. @Override
    94. publicintgetRowGrid(introw,intcolumn){
    95. if(gridInfo!=null&&row>=0&&row<gridInfo.size()){
    96. List<Point>gridRow=gridInfo.get(row);
    97. if(gridRow!=null&&column>=0&&column<gridRow.size()){
    98. Pointpoint=gridRow.get(column);
    99. if(point!=null)
    100. returnpoint.y;
    101. }
    102. }
    103. return1;
    104. }
    105. protectedbooleansetGrid(introw,intcolumn,Pointgrid){
    106. if(gridInfo!=null&&row>=0&&row<gridInfo.size()){
    107. List<Point>gridRow=gridInfo.get(row);
    108. if(gridRow!=null&&column>=0&&column<gridRow.size()){
    109. Pointpoint=gridRow.get(column);
    110. if(point!=null){
    111. point.setLocation(grid);
    112. }
    113. else{
    114. gridRow.set(column,grid.getLocation());
    115. }
    116. returntrue;
    117. }
    118. }
    119. returnfalse;
    120. }
    121. @Override
    122. publicbooleanspliteCellAt(introw,intcolumn){
    123. if(gridInfo!=null&&row>=0&&row<gridInfo.size()){
    124. List<Point>gridRow=gridInfo.get(row);
    125. if(gridRow!=null&&column>=0&&column<gridRow.size()){
    126. Pointpoint=gridRow.get(column);
    127. if(point!=null){
    128. point=point.getLocation();
    129. for(inta=0;a<point.y;a++){
    130. for(intb=0;b<point.x;b++){
    131. setGrid(row+a,column+b,getDefaultPoint());
    132. }
    133. }
    134. }
    135. else{
    136. gridRow.set(column,getDefaultPoint());
    137. }
    138. returntrue;
    139. }
    140. }
    141. returnfalse;
    142. }
    143. @Override
    144. /**
    145. *table中发生行的添加和删除的时候需要修改该模型
    146. */
    147. publicvoidtableChanged(TableModelEvente){
    148. //TODO
    149. }
    150. @Override
    151. publicbooleanmergeCells(int[]rows,int[]columns){
    152. if(!canMergeCells(rows,columns))
    153. returnfalse;
    154. Arrays.sort(rows);
    155. Arrays.sort(columns);
    156. returnmergeCells(rows[0],rows[rows.length-1],columns[0],columns[columns.length-1]);
    157. }
    158. @Override
    159. publicbooleanmergeCells(intstartRow,intendRow,intstartColumn,intendColumn){
    160. setGrid(startRow,startColumn,newPoint(endColumn-startColumn+1,endRow-startRow+1));
    161. for(introw=startRow;row<=endRow;row++){
    162. for(intcol=startColumn;col<=endColumn;col++){
    163. if(row==startRow&&col==startColumn)
    164. continue;
    165. else
    166. setGrid(row,col,newPoint(COVERED,COVERED));
    167. }
    168. }
    169. returntrue;
    170. }
    171. publicStringtoString(){
    172. if(gridInfo==null)
    173. return"";
    174. StringBuffersb=newStringBuffer();
    175. for(List<Point>rowInfo:gridInfo){
    176. for(Pointgrid:rowInfo){
    177. sb.append("["+grid.x+","+grid.y+"],");
    178. }
    179. sb.append("\n");
    180. }
    181. returnsb.toString();
    182. }
    183. }

    package org.dxj.guitools.gridbagtable; import java.awt.Point; import java.util.Arrays; import java.util.List; import java.util.Vector; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.AbstractTableModel; public class DefaultGridBagTableModel implements GridBagModel, TableModelListener{ protected AbstractTableModel model; protected List<List<Point>> gridInfo; DefaultGridBagTableModel(AbstractTableModel model){ gridInfo = new Vector<List<Point>>(); setTableModel(model); } public void setTableModel(AbstractTableModel model){ if( model != null && model != this.model ){ if( this.model != null ) this.model.removeTableModelListener(this); //防止多次添加监听器 model.removeTableModelListener(this); model.addTableModelListener(this); this.model = model; clearMergence(); } } public void clearMergence(){ if( gridInfo == null ) gridInfo = new Vector<List<Point>>(); else gridInfo.clear(); if( model == null ) return; //初始化,每个格子占的格子数为(1,1); for(int row=model.getRowCount(); --row>=0;){ List<Point> infos = new Vector<Point>(); gridInfo.add(infos); for(int col=model.getColumnCount(); --col>=0;){ infos.add(getDefaultPoint()); } } } public Point getDefaultPoint(){ return new Point(1,1); } @Override public boolean canMergeCells(int[] rows, int[] columns) { if( rows == null || columns == null ) return false; Arrays.sort(rows); for(int index=0; index<rows.length-1; index++){ if( rows[index+1] - rows[index] > 1 ) return false; } Arrays.sort(columns); for(int index=0; index<columns.length-1; index++){ if( columns[index+1] - columns[index] > 1 ) return false; } return true; } @Override public int getCellState(int row, int column) { Point grid = getGrid(row, column); if( grid == null ) return DEFAULT; if( grid.x>1 || grid.y>1 ) return MERGE; if( grid.x<=0 || grid.y<=0 ) return COVERED; return DEFAULT; } @Override public int getColumnGrid(int row, int column) { if( gridInfo != null && row >=0 && row < gridInfo.size() ){ List<Point> gridRow = gridInfo.get(row); if( gridRow != null && column >=0 && column < gridRow.size() ){ Point point = gridRow.get(column); if( point != null ) return point.x; } } return 1; } @Override public Point getGrid(int row, int column) { if( gridInfo != null && row >=0 && row < gridInfo.size() ){ List<Point> gridRow = gridInfo.get(row); if( gridRow != null && column >=0 && column < gridRow.size() ){ return gridRow.get(column); } } return getDefaultPoint(); } @Override public int getRowGrid(int row, int column) { if( gridInfo != null && row >=0 && row < gridInfo.size() ){ List<Point> gridRow = gridInfo.get(row); if( gridRow != null && column >=0 && column < gridRow.size() ){ Point point = gridRow.get(column); if( point != null ) return point.y; } } return 1; } protected boolean setGrid(int row, int column, Point grid) { if( gridInfo != null && row >=0 && row < gridInfo.size() ){ List<Point> gridRow = gridInfo.get(row); if( gridRow != null && column >=0 && column < gridRow.size() ){ Point point = gridRow.get(column); if( point != null ){ point.setLocation(grid); } else{ gridRow.set(column, grid.getLocation()); } return true; } } return false; } @Override public boolean spliteCellAt(int row, int column) { if( gridInfo != null && row >=0 && row < gridInfo.size() ){ List<Point> gridRow = gridInfo.get(row); if( gridRow != null && column >=0 && column < gridRow.size() ){ Point point = gridRow.get(column); if( point != null ){ point = point.getLocation(); for(int a=0; a<point.y; a++){ for(int b=0; b<point.x; b++){ setGrid(row+a, column+b, getDefaultPoint()); } } } else{ gridRow.set(column, getDefaultPoint()); } return true; } } return false; } @Override /** * table中发生行的添加和删除的时候需要修改该模型 */ public void tableChanged(TableModelEvent e) { //TODO } @Override public boolean mergeCells(int[] rows, int[] columns) { if( !canMergeCells(rows, columns) ) return false; Arrays.sort(rows); Arrays.sort(columns); return mergeCells(rows[0],rows[rows.length-1],columns[0],columns[columns.length-1]); } @Override public boolean mergeCells(int startRow, int endRow, int startColumn, int endColumn) { setGrid(startRow, startColumn, new Point(endColumn-startColumn+1, endRow-startRow+1)); for(int row=startRow; row<=endRow; row++){ for(int col=startColumn; col<=endColumn; col++){ if(row==startRow&&col==startColumn) continue; else setGrid(row, col, new Point(COVERED,COVERED)); } } return true; } public String toString(){ if( gridInfo == null ) return ""; StringBuffer sb = new StringBuffer(); for(List<Point> rowInfo : gridInfo ){ for(Point grid : rowInfo){ sb.append("["+grid.x+","+grid.y+"], "); } sb.append("\n"); } return sb.toString(); } }

    Java代码
    1. packageorg.dxj.guitools.gridbagtable;
    2. importjava.awt.Color;
    3. importjava.awt.Component;
    4. importjava.awt.Dimension;
    5. importjava.awt.Graphics;
    6. importjava.awt.Point;
    7. importjava.awt.Rectangle;
    8. importjava.util.Enumeration;
    9. importjavax.swing.BorderFactory;
    10. importjavax.swing.JComponent;
    11. importjavax.swing.JTable;
    12. importjavax.swing.UIManager;
    13. importjavax.swing.plaf.basic.BasicTableUI;
    14. importjavax.swing.table.JTableHeader;
    15. importjavax.swing.table.TableCellRenderer;
    16. importjavax.swing.table.TableColumn;
    17. importjavax.swing.table.TableColumnModel;
    18. publicclassGridBagTableUIextendsBasicTableUI
    19. {
    20. publicDimensiongetPreferredSize(JComponentc){
    21. longwidth=0;
    22. Enumeration<TableColumn>enumeration=table.getColumnModel().getColumns();
    23. while(enumeration.hasMoreElements()){
    24. TableColumnaColumn=(TableColumn)enumeration.nextElement();
    25. width=width+aColumn.getPreferredWidth();
    26. }
    27. returncreateTableSize(width);
    28. }
    29. privateDimensioncreateTableSize(longwidth){
    30. intheight=0;
    31. introwCount=table.getRowCount();
    32. if(rowCount>0&&table.getColumnCount()>0){
    33. Rectangler=table.getCellRect(rowCount-1,0,true);
    34. height=r.y+r.height;
    35. }
    36. //Widthisalwayspositive.Thecalltoabs()isaworkaroundfor
    37. //abuginthe1.1.6JITonWindows.
    38. longtmp=Math.abs(width);
    39. if(tmp>Integer.MAX_VALUE){
    40. tmp=Integer.MAX_VALUE;
    41. }
    42. returnnewDimension((int)tmp,height);
    43. }
    44. publicvoidpaint(Graphicsg,JComponentc){
    45. Rectangleclip=g.getClipBounds();
    46. Rectanglebounds=table.getBounds();
    47. //accountforthefactthatthegraphicshasalreadybeentranslated
    48. //intothetable'sbounds
    49. bounds.x=bounds.y=0;
    50. if(table.getRowCount()<=0||table.getColumnCount()<=0||
    51. //thischeckpreventsusfrompaintingtheentiretable
    52. //whentheclipdoesn'tintersectourboundsatall
    53. !bounds.intersects(clip)){
    54. paintDropLines(g);
    55. return;
    56. }
    57. booleanltr=table.getComponentOrientation().isLeftToRight();
    58. PointupperLeft=clip.getLocation();
    59. if(!ltr){
    60. upperLeft.x++;
    61. }
    62. PointlowerRight=newPoint(clip.x+clip.width-(ltr?1:0),
    63. clip.y+clip.height);
    64. intrMin=table.rowAtPoint(upperLeft);
    65. intrMax=table.rowAtPoint(lowerRight);
    66. //Thisshouldneverhappen(aslongasourboundsintersecttheclip,
    67. //whichiswhywebailaboveifthatisthecase).
    68. if(rMin==-1){
    69. rMin=0;
    70. }
    71. //Ifthetabledoesnothaveenoughrowstofilltheviewwe'llget-1.
    72. //(Wecouldalsoget-1ifourboundsdon'tintersecttheclip,
    73. //whichiswhywebailaboveifthatisthecase).
    74. //Replacethiswiththeindexofthelastrow.
    75. if(rMax==-1){
    76. rMax=table.getRowCount()-1;
    77. }
    78. intcMin=table.columnAtPoint(ltr?upperLeft:lowerRight);
    79. intcMax=table.columnAtPoint(ltr?lowerRight:upperLeft);
    80. //Thisshouldneverhappen.
    81. if(cMin==-1){
    82. cMin=0;
    83. }
    84. //Ifthetabledoesnothaveenoughcolumnstofilltheviewwe'llget-1.
    85. //Replacethiswiththeindexofthelastcolumn.
    86. if(cMax==-1){
    87. cMax=table.getColumnCount()-1;
    88. }
    89. //Paintthegrid.
    90. //paintGrid(g,rMin,rMax,cMin,cMax);
    91. //Paintthecells.
    92. paintCells(g,rMin,rMax,cMin,cMax);
    93. paintDropLines(g);
    94. }
    95. privatevoidpaintDropLines(Graphicsg){
    96. JTable.DropLocationloc=table.getDropLocation();
    97. if(loc==null){
    98. return;
    99. }
    100. Colorcolor=UIManager.getColor("Table.dropLineColor");
    101. ColorshortColor=UIManager.getColor("Table.dropLineShortColor");
    102. if(color==null&&shortColor==null){
    103. return;
    104. }
    105. Rectanglerect;
    106. rect=getHDropLineRect(loc);
    107. if(rect!=null){
    108. intx=rect.x;
    109. intw=rect.width;
    110. if(color!=null){
    111. extendRect(rect,true);
    112. g.setColor(color);
    113. g.fillRect(rect.x,rect.y,rect.width,rect.height);
    114. }
    115. if(!loc.isInsertColumn()&&shortColor!=null){
    116. g.setColor(shortColor);
    117. g.fillRect(x,rect.y,w,rect.height);
    118. }
    119. }
    120. rect=getVDropLineRect(loc);
    121. if(rect!=null){
    122. inty=rect.y;
    123. inth=rect.height;
    124. if(color!=null){
    125. extendRect(rect,false);
    126. g.setColor(color);
    127. g.fillRect(rect.x,rect.y,rect.width,rect.height);
    128. }
    129. if(!loc.isInsertRow()&&shortColor!=null){
    130. g.setColor(shortColor);
    131. g.fillRect(rect.x,y,rect.width,h);
    132. }
    133. }
    134. }
    135. /*
    136. *Paintsthegridlineswithin<I>aRect</I>,usingthegrid
    137. *colorsetwith<I>setGridColor</I>.Paintsverticallines
    138. *if<code>getShowVerticalLines()</code>returnstrueandpaints
    139. *horizontallinesif<code>getShowHorizontalLines()</code>
    140. *returnstrue.
    141. */
    142. privatevoidpaintGrid(Graphicsg,intrMin,intrMax,intcMin,intcMax){
    143. g.setColor(table.getGridColor());
    144. RectangleminCell=table.getCellRect(rMin,cMin,true);
    145. RectanglemaxCell=table.getCellRect(rMax,cMax,true);
    146. RectangledamagedArea=minCell.union(maxCell);
    147. if(table.getShowHorizontalLines()){
    148. inttableWidth=damagedArea.x+damagedArea.width;
    149. inty=damagedArea.y;
    150. for(introw=rMin;row<=rMax;row++){
    151. y+=table.getRowHeight(row);
    152. g.drawLine(damagedArea.x,y-1,tableWidth-1,y-1);
    153. }
    154. }
    155. if(table.getShowVerticalLines()){
    156. TableColumnModelcm=table.getColumnModel();
    157. inttableHeight=damagedArea.y+damagedArea.height;
    158. intx;
    159. if(table.getComponentOrientation().isLeftToRight()){
    160. x=damagedArea.x;
    161. for(intcolumn=cMin;column<=cMax;column++){
    162. intw=cm.getColumn(column).getWidth();
    163. x+=w;
    164. g.drawLine(x-1,0,x-1,tableHeight-1);
    165. }
    166. }else{
    167. x=damagedArea.x;
    168. for(intcolumn=cMax;column>=cMin;column--){
    169. intw=cm.getColumn(column).getWidth();
    170. x+=w;
    171. g.drawLine(x-1,0,x-1,tableHeight-1);
    172. }
    173. }
    174. }
    175. }
    176. privatevoidpaintCells(Graphicsg,intrMin,intrMax,intcMin,intcMax){
    177. JTableHeaderheader=table.getTableHeader();
    178. TableColumndraggedColumn=(header==null)?null:header.getDraggedColumn();
    179. TableColumnModelcm=table.getColumnModel();
    180. intcolumnMargin=cm.getColumnMargin();
    181. RectanglecellRect;
    182. TableColumnaColumn;
    183. intcolumnWidth;
    184. if(table.getComponentOrientation().isLeftToRight()){
    185. for(introw=rMin;row<=rMax;row++){
    186. if(tableinstanceofGridBagTable)
    187. cellRect=((GridBagTable)table).getGridCellRect(row,cMin,false);
    188. else
    189. cellRect=table.getCellRect(row,cMin,false);
    190. for(intcolumn=cMin;column<=cMax;column++){
    191. aColumn=cm.getColumn(column);
    192. columnWidth=aColumn.getWidth();
    193. //TODO
    194. cellRect.width=columnWidth-columnMargin;
    195. intoldHeight=cellRect.height;
    196. if(tableinstanceofGridBagTable){
    197. if(((GridBagTable)table).getGridBagModel().getCellState(row,column)==GridBagModel.COVERED){
    198. cellRect.width=0;
    199. cellRect.height=0;
    200. }
    201. else{
    202. inth=((GridBagTable)table).getGridBagModel().getColumnGrid(row,column);
    203. if(h>1){
    204. for(intn=1;n<h;n++)
    205. cellRect.width+=cm.getColumn(column+n).getWidth();
    206. }
    207. intv=((GridBagTable)table).getGridBagModel().getRowGrid(row,column);
    208. if(v>1){
    209. for(intn=1;n<v;n++)
    210. cellRect.height+=table.getRowHeight(row+n);
    211. }
    212. }
    213. }
    214. if(aColumn!=draggedColumn){
    215. paintCell(g,cellRect,row,column);
    216. }
    217. cellRect.height=oldHeight;
    218. cellRect.x+=columnWidth;
    219. }
    220. }
    221. }else{
    222. for(introw=rMin;row<=rMax;row++){
    223. cellRect=table.getCellRect(row,cMin,false);
    224. aColumn=cm.getColumn(cMin);
    225. if(aColumn!=draggedColumn){
    226. columnWidth=aColumn.getWidth();
    227. cellRect.width=columnWidth-columnMargin;
    228. paintCell(g,cellRect,row,cMin);
    229. }
    230. for(intcolumn=cMin+1;column<=cMax;column++){
    231. aColumn=cm.getColumn(column);
    232. columnWidth=aColumn.getWidth();
    233. //TODO
    234. cellRect.width=columnWidth-columnMargin;
    235. cellRect.x-=columnWidth;
    236. if(aColumn!=draggedColumn){
    237. paintCell(g,cellRect,row,column);
    238. }
    239. }
    240. }
    241. }
    242. //Paintthedraggedcolumnifwearedragging.
    243. if(draggedColumn!=null){
    244. paintDraggedArea(g,rMin,rMax,draggedColumn,header.getDraggedDistance());
    245. }
    246. //RemoveanyrenderersthatmaybeleftintherendererPane.
    247. rendererPane.removeAll();
    248. }
    249. privatevoidpaintCell(Graphicsg,RectanglecellRect,introw,intcolumn){
    250. if(table.isEditing()&&table.getEditingRow()==row&&
    251. table.getEditingColumn()==column){
    252. Componentcomponent=table.getEditorComponent();
    253. component.setBounds(cellRect);
    254. component.validate();
    255. }
    256. else{
    257. TableCellRendererrenderer=table.getCellRenderer(row,column);
    258. Componentcomponent=table.prepareRenderer(renderer,row,column);
    259. if(componentinstanceofJComponent){
    260. ((JComponent)component).setBorder(BorderFactory.createLineBorder(Color.gray));
    261. }
    262. rendererPane.paintComponent(g,component,table,cellRect.x,cellRect.y,
    263. cellRect.width,cellRect.height,true);
    264. }
    265. }
    266. privateRectanglegetHDropLineRect(JTable.DropLocationloc){
    267. if(!loc.isInsertRow()){
    268. returnnull;
    269. }
    270. introw=loc.getRow();
    271. intcol=loc.getColumn();
    272. if(col>=table.getColumnCount()){
    273. col--;
    274. }
    275. Rectanglerect=table.getCellRect(row,col,true);
    276. if(row>=table.getRowCount()){
    277. row--;
    278. RectangleprevRect=table.getCellRect(row,col,true);
    279. rect.y=prevRect.y+prevRect.height;
    280. }
    281. if(rect.y==0){
    282. rect.y=-1;
    283. }else{
    284. rect.y-=2;
    285. }
    286. rect.height=3;
    287. returnrect;
    288. }
    289. privatevoidpaintDraggedArea(Graphicsg,intrMin,intrMax,TableColumndraggedColumn,intdistance){
    290. intdraggedColumnIndex=viewIndexForColumn(draggedColumn);
    291. RectangleminCell=table.getCellRect(rMin,draggedColumnIndex,true);
    292. RectanglemaxCell=table.getCellRect(rMax,draggedColumnIndex,true);
    293. RectanglevacatedColumnRect=minCell.union(maxCell);
    294. //Paintagraywellinplaceofthemovingcolumn.
    295. g.setColor(table.getParent().getBackground());
    296. g.fillRect(vacatedColumnRect.x,vacatedColumnRect.y,
    297. vacatedColumnRect.width,vacatedColumnRect.height);
    298. //Movetothewherethecellhasbeendragged.
    299. vacatedColumnRect.x+=distance;
    300. //Fillthebackground.
    301. g.setColor(table.getBackground());
    302. g.fillRect(vacatedColumnRect.x,vacatedColumnRect.y,
    303. vacatedColumnRect.width,vacatedColumnRect.height);
    304. //Painttheverticalgridlinesifnecessary.
    305. if(table.getShowVerticalLines()){
    306. g.setColor(table.getGridColor());
    307. intx1=vacatedColumnRect.x;
    308. inty1=vacatedColumnRect.y;
    309. intx2=x1+vacatedColumnRect.width-1;
    310. inty2=y1+vacatedColumnRect.height-1;
    311. //Left
    312. g.drawLine(x1-1,y1,x1-1,y2);
    313. //Right
    314. g.drawLine(x2,y1,x2,y2);
    315. }
    316. for(introw=rMin;row<=rMax;row++){
    317. //Renderthecellvalue
    318. Rectangler=table.getCellRect(row,draggedColumnIndex,false);
    319. r.x+=distance;
    320. paintCell(g,r,row,draggedColumnIndex);
    321. //Paintthe(lower)horizontalgridlineifnecessary.
    322. if(table.getShowHorizontalLines()){
    323. g.setColor(table.getGridColor());
    324. Rectanglercr=table.getCellRect(row,draggedColumnIndex,true);
    325. rcr.x+=distance;
    326. intx1=rcr.x;
    327. inty1=rcr.y;
    328. intx2=x1+rcr.width-1;
    329. inty2=y1+rcr.height-1;
    330. g.drawLine(x1,y2,x2,y2);
    331. }
    332. }
    333. }
    334. privateintviewIndexForColumn(TableColumnaColumn){
    335. TableColumnModelcm=table.getColumnModel();
    336. for(intcolumn=0;column<cm.getColumnCount();column++){
    337. if(cm.getColumn(column)==aColumn){
    338. returncolumn;
    339. }
    340. }
    341. return-1;
    342. }
    343. privateRectangleextendRect(Rectanglerect,booleanhorizontal){
    344. if(rect==null){
    345. returnrect;
    346. }
    347. if(horizontal){
    348. rect.x=0;
    349. rect.width=table.getWidth();
    350. }else{
    351. rect.y=0;
    352. if(table.getRowCount()!=0){
    353. RectanglelastRect=table.getCellRect(table.getRowCount()-1,0,true);
    354. rect.height=lastRect.y+lastRect.height;
    355. }else{
    356. rect.height=table.getHeight();
    357. }
    358. }
    359. returnrect;
    360. }
    361. privateRectanglegetVDropLineRect(JTable.DropLocationloc){
    362. if(!loc.isInsertColumn()){
    363. returnnull;
    364. }
    365. booleanltr=table.getComponentOrientation().isLeftToRight();
    366. intcol=loc.getColumn();
    367. Rectanglerect=table.getCellRect(loc.getRow(),col,true);
    368. if(col>=table.getColumnCount()){
    369. col--;
    370. rect=table.getCellRect(loc.getRow(),col,true);
    371. if(ltr){
    372. rect.x=rect.x+rect.width;
    373. }
    374. }elseif(!ltr){
    375. rect.x=rect.x+rect.width;
    376. }
    377. if(rect.x==0){
    378. rect.x=-1;
    379. }else{
    380. rect.x-=2;
    381. }
    382. rect.width=3;
    383. returnrect;
    384. }
    385. }//EndofClassBasicTableUI

    package org.dxj.guitools.gridbagtable; import java.awt.Color; 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 javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.UIManager; import javax.swing.plaf.basic.BasicTableUI; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; public class GridBagTableUI extends BasicTableUI { public Dimension getPreferredSize(JComponent c) { long width = 0; Enumeration<TableColumn> enumeration = table.getColumnModel().getColumns(); while (enumeration.hasMoreElements()) { TableColumn aColumn = (TableColumn)enumeration.nextElement(); width = width + aColumn.getPreferredWidth(); } return createTableSize(width); } private Dimension createTableSize(long width) { int height = 0; int rowCount = table.getRowCount(); if (rowCount > 0 && table.getColumnCount() > 0) { Rectangle r = table.getCellRect(rowCount-1, 0, true); height = r.y + r.height; } // Width is always positive. The call to abs() is a workaround for // a bug in the 1.1.6 JIT on Windows. long tmp = Math.abs(width); if (tmp > Integer.MAX_VALUE) { tmp = Integer.MAX_VALUE; } return new Dimension((int)tmp, height); } public void paint(Graphics g, JComponent c) { Rectangle clip = g.getClipBounds(); Rectangle bounds = table.getBounds(); // account for the fact that the graphics has already been translated // into the table's bounds bounds.x = bounds.y = 0; if (table.getRowCount() <= 0 || table.getColumnCount() <= 0 || // this check prevents us from painting the entire table // when the clip doesn't intersect our bounds at all !bounds.intersects(clip)) { paintDropLines(g); return; } boolean ltr = table.getComponentOrientation().isLeftToRight(); Point upperLeft = clip.getLocation(); if (!ltr) { upperLeft.x++; } Point lowerRight = new Point(clip.x + clip.width - (ltr ? 1 : 0), clip.y + clip.height); int rMin = table.rowAtPoint(upperLeft); int rMax = table.rowAtPoint(lowerRight); // This should never happen (as long as our bounds intersect the clip, // which is why we bail above if that is the case). if (rMin == -1) { rMin = 0; } // If the table does not have enough rows to fill the view we'll get -1. // (We could also get -1 if our bounds don't intersect the clip, // which is why we bail above if that is the case). // Replace this with the index of the last row. if (rMax == -1) { rMax = table.getRowCount()-1; } int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight); int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft); // 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 = table.getColumnCount()-1; } // Paint the grid. //paintGrid(g, rMin, rMax, cMin, cMax); // Paint the cells. paintCells(g, rMin, rMax, cMin, cMax); paintDropLines(g); } private void paintDropLines(Graphics g) { JTable.DropLocation loc = table.getDropLocation(); if (loc == null) { return; } Color color = UIManager.getColor("Table.dropLineColor"); Color shortColor = UIManager.getColor("Table.dropLineShortColor"); if (color == null && shortColor == null) { return; } Rectangle rect; rect = getHDropLineRect(loc); if (rect != null) { int x = rect.x; int w = rect.width; if (color != null) { extendRect(rect, true); g.setColor(color); g.fillRect(rect.x, rect.y, rect.width, rect.height); } if (!loc.isInsertColumn() && shortColor != null) { g.setColor(shortColor); g.fillRect(x, rect.y, w, rect.height); } } rect = getVDropLineRect(loc); if (rect != null) { int y = rect.y; int h = rect.height; if (color != null) { extendRect(rect, false); g.setColor(color); g.fillRect(rect.x, rect.y, rect.width, rect.height); } if (!loc.isInsertRow() && shortColor != null) { g.setColor(shortColor); g.fillRect(rect.x, y, rect.width, h); } } } /* * Paints the grid lines within <I>aRect</I>, using the grid * color set with <I>setGridColor</I>. Paints vertical lines * if <code>getShowVerticalLines()</code> returns true and paints * horizontal lines if <code>getShowHorizontalLines()</code> * returns true. */ private void paintGrid(Graphics g, int rMin, int rMax, int cMin, int cMax) { g.setColor(table.getGridColor()); Rectangle minCell = table.getCellRect(rMin, cMin, true); Rectangle maxCell = table.getCellRect(rMax, cMax, true); Rectangle damagedArea = minCell.union( maxCell ); if (table.getShowHorizontalLines()) { int tableWidth = damagedArea.x + damagedArea.width; int y = damagedArea.y; for (int row = rMin; row <= rMax; row++) { y += table.getRowHeight(row); g.drawLine(damagedArea.x, y - 1, tableWidth - 1, y - 1); } } if (table.getShowVerticalLines()) { TableColumnModel cm = table.getColumnModel(); int tableHeight = damagedArea.y + damagedArea.height; int x; if (table.getComponentOrientation().isLeftToRight()) { x = damagedArea.x; for (int column = cMin; column <= cMax; column++) { int w = cm.getColumn(column).getWidth(); x += w; g.drawLine(x - 1, 0, x - 1, tableHeight - 1); } } else { x = damagedArea.x; for (int column = cMax; column >= cMin; column--) { int w = cm.getColumn(column).getWidth(); x += w; g.drawLine(x - 1, 0, x - 1, tableHeight - 1); } } } } private void paintCells(Graphics g, int rMin, int rMax, int cMin, int cMax) { JTableHeader header = table.getTableHeader(); TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn(); TableColumnModel cm = table.getColumnModel(); int columnMargin = cm.getColumnMargin(); Rectangle cellRect; TableColumn aColumn; int columnWidth; if (table.getComponentOrientation().isLeftToRight()) { for(int row = rMin; row <= rMax; row++) { if( table instanceof GridBagTable ) cellRect = ((GridBagTable)table).getGridCellRect(row, cMin, false); else cellRect = table.getCellRect(row, cMin, false); for(int column = cMin; column <= cMax; column++) { aColumn = cm.getColumn(column); columnWidth = aColumn.getWidth(); //TODO cellRect.width = columnWidth - columnMargin; int oldHeight = cellRect.height; if( table instanceof GridBagTable ){ if(((GridBagTable)table).getGridBagModel().getCellState( row, column) == GridBagModel.COVERED ) { cellRect.width = 0; cellRect.height = 0; } else{ int h = ((GridBagTable)table).getGridBagModel().getColumnGrid(row, column); if( h >1 ){ for( int n=1; n<h; n++) cellRect.width += cm.getColumn(column+n).getWidth(); } int v = ((GridBagTable)table).getGridBagModel().getRowGrid(row, column); if( v >1 ){ for( int n=1; n<v; n++) cellRect.height += table.getRowHeight(row+n); } } } if (aColumn != draggedColumn) { paintCell(g, cellRect, row, column); } cellRect.height = oldHeight; cellRect.x += columnWidth; } } } else { for(int row = rMin; row <= rMax; row++) { cellRect = table.getCellRect(row, cMin, false); aColumn = cm.getColumn(cMin); if (aColumn != draggedColumn) { columnWidth = aColumn.getWidth(); cellRect.width = columnWidth - columnMargin; paintCell(g, cellRect, row, cMin); } for(int column = cMin+1; column <= cMax; column++) { aColumn = cm.getColumn(column); columnWidth = aColumn.getWidth(); // TODO cellRect.width = columnWidth - columnMargin; cellRect.x -= columnWidth; if (aColumn != draggedColumn) { paintCell(g, cellRect, row, column); } } } } // Paint the dragged column if we are dragging. if (draggedColumn != null) { paintDraggedArea(g, rMin, rMax, draggedColumn, header.getDraggedDistance()); } // Remove any renderers that may be left in the rendererPane. rendererPane.removeAll(); } private void paintCell(Graphics g, Rectangle cellRect, int row, int column) { if (table.isEditing() && table.getEditingRow()==row && table.getEditingColumn()==column) { Component component = table.getEditorComponent(); component.setBounds(cellRect); component.validate(); } else { TableCellRenderer renderer = table.getCellRenderer(row, column); Component component = table.prepareRenderer(renderer, row, column); if( component instanceof JComponent ){ ((JComponent)component).setBorder(BorderFactory.createLineBorder(Color.gray)); } rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y, cellRect.width, cellRect.height, true); } } private Rectangle getHDropLineRect(JTable.DropLocation loc) { if (!loc.isInsertRow()) { return null; } int row = loc.getRow(); int col = loc.getColumn(); if (col >= table.getColumnCount()) { col--; } Rectangle rect = table.getCellRect(row, col, true); if (row >= table.getRowCount()) { row--; Rectangle prevRect = table.getCellRect(row, col, true); rect.y = prevRect.y + prevRect.height; } if (rect.y == 0) { rect.y = -1; } else { rect.y -= 2; } rect.height = 3; return rect; } private void paintDraggedArea(Graphics g, int rMin, int rMax, TableColumn draggedColumn, int distance) { int draggedColumnIndex = viewIndexForColumn(draggedColumn); Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true); Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true); Rectangle vacatedColumnRect = minCell.union(maxCell); // Paint a gray well in place of the moving column. g.setColor(table.getParent().getBackground()); g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y, vacatedColumnRect.width, vacatedColumnRect.height); // Move to the where the cell has been dragged. vacatedColumnRect.x += distance; // Fill the background. g.setColor(table.getBackground()); g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y, vacatedColumnRect.width, vacatedColumnRect.height); // Paint the vertical grid lines if necessary. if (table.getShowVerticalLines()) { g.setColor(table.getGridColor()); int x1 = vacatedColumnRect.x; int y1 = vacatedColumnRect.y; int x2 = x1 + vacatedColumnRect.width - 1; int y2 = y1 + vacatedColumnRect.height - 1; // Left g.drawLine(x1-1, y1, x1-1, y2); // Right g.drawLine(x2, y1, x2, y2); } for(int row = rMin; row <= rMax; row++) { // Render the cell value Rectangle r = table.getCellRect(row, draggedColumnIndex, false); r.x += distance; paintCell(g, r, row, draggedColumnIndex); // Paint the (lower) horizontal grid line if necessary. if (table.getShowHorizontalLines()) { g.setColor(table.getGridColor()); Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true); rcr.x += distance; int x1 = rcr.x; int y1 = rcr.y; int x2 = x1 + rcr.width - 1; int y2 = y1 + rcr.height - 1; g.drawLine(x1, y2, x2, y2); } } } private int viewIndexForColumn(TableColumn aColumn) { TableColumnModel cm = table.getColumnModel(); for (int column = 0; column < cm.getColumnCount(); column++) { if (cm.getColumn(column) == aColumn) { return column; } } return -1; } private Rectangle extendRect(Rectangle rect, boolean horizontal) { if (rect == null) { return rect; } if (horizontal) { rect.x = 0; rect.width = table.getWidth(); } else { rect.y = 0; if (table.getRowCount() != 0) { Rectangle lastRect = table.getCellRect(table.getRowCount() - 1, 0, true); rect.height = lastRect.y + lastRect.height; } else { rect.height = table.getHeight(); } } return rect; } private Rectangle getVDropLineRect(JTable.DropLocation loc) { if (!loc.isInsertColumn()) { return null; } boolean ltr = table.getComponentOrientation().isLeftToRight(); int col = loc.getColumn(); Rectangle rect = table.getCellRect(loc.getRow(), col, true); if (col >= table.getColumnCount()) { col--; rect = table.getCellRect(loc.getRow(), col, true); if (ltr) { rect.x = rect.x + rect.width; } } else if (!ltr) { rect.x = rect.x + rect.width; } if (rect.x == 0) { rect.x = -1; } else { rect.x -= 2; } rect.width = 3; return rect; } } // End of Class BasicTableUI

    测试代码:

    Java代码
    1. importjava.awt.BorderLayout;
    2. importjava.awt.event.ActionEvent;
    3. importjava.awt.event.ActionListener;
    4. importjavax.swing.JButton;
    5. importjavax.swing.JFrame;
    6. importjavax.swing.JScrollPane;
    7. importjavax.swing.table.DefaultTableModel;
    8. importcom.jrf.jgrid.guitools.gridbagtable.GridBagTable;
    9. publicclassTestimplementsActionListener{
    10. GridBagTabletable;
    11. publicTest()
    12. {
    13. JFramed=newJFrame();
    14. DefaultTableModelmodel=newDefaultTableModel(5,5);
    15. table=newGridBagTable(model);
    16. table.setRowHeight(20);
    17. JScrollPanepane=newJScrollPane(table);
    18. d.getContentPane().add(pane,BorderLayout.CENTER);
    19. JButtonbtn=newJButton("合并/拆分");
    20. d.getContentPane().add(btn,BorderLayout.NORTH);
    21. btn.addActionListener(this);
    22. d.setBounds(0,0,400,400);
    23. d.setVisible(true);
    24. }
    25. publicstaticvoidmain(String[]fsd){
    26. newTest();
    27. }
    28. publicvoidactionPerformed(ActionEvente){
    29. table.mergeCells(table.getSelectedRows(),table.getSelectedColumns());
    30. }
    31. }

Java Swing写的支持合并单元格的JTable相关推荐

  1. Excel 求满足多个条件(2个条件,3个条件,....)的行的指定列的和公式,Excel根据汉字描述生成公式工具,支持合并单元格

    Excel 如何用公式求满足多个条件的行的指定列的和,使用这个工具,根据汉字描述自动生成需要的公式.本工具支持合并单元格 地址:http://www.excelfb.com/ 点击: Excel自动写 ...

  2. java创建excel(不用设置样式,支持xlsx、xls格式, 不支持合并单元格)

    推荐:java 读取excel(支持xlsx.xls格式, 支持合并单元格) java写excel 实现效果 实现思路 基本概念 依赖 对外提供的方法 内部方法 代码实现 测试代码 ExcelWrit ...

  3. 浏览器 excel本地预览,支持合并单元格预览

    效果图 说明:支持合并单元格预览 1.index.html <!DOCTYPE html> <html lang="en"> <head>< ...

  4. Java读取Excel中的合并单元格

    本文以Java示例展示读取Excel中的合并单元格的方法. 1. Maven仓库下载导入 在pom.xml中配置maven路径,指定依赖,如下: <dependency><group ...

  5. java poi导出excel,合并单元格

    java导出excel一般都是2种情况,一种是依赖一个实体类进行导出,或者把数据查询出来当成一个视图,对视图进行创建实体:另一种方式就是通过数据还要计算,然后一块统计,那么就不是很好处理了,我采用的是 ...

  6. java读写Excel文件、合并单元格

    [转载]http://blog.sina.com.cn/s/blog_694448320100lxbe.html 利用java操作Excel,有个开源的东东-jxl.jar,可以到http://sou ...

  7. excel模板合并单元格(jxls 合并单元格)---工具net.sf.jxls扩展---支持合并单元格(无需代码方式合并)

    概述 excel比较常用的工具类就是poi和jxl当然后者已经停止维护很久而且只支持excel2003,不支持excel2007. 对2003版,最大行数是65536行 ,对2007以上版本,最大行数 ...

  8. java后端生成pdf模板合并单元格表格

    这里只放部分片段的代码 java中使用二维数组生成表格非常方便,但是每一维的数组都需要排好序,而且,在java中所谓的二维数组,三维数组等,其实都是多个一维数组组成的 /*** 添加子女教育规划表.* ...

  9. java mergecells_java怎么操作excel合并单元格

    展开全部 利用java操作Excel,有个开源的东东-jxl.jar,可以到http://sourceforge.net/project/showfiles.php?group_id=79926下载. ...

  10. java 读取excel 合并单元格_利用java读写Excel文件、合并单元格

    一般的页眉页脚都分为三个部分,左,中,右三部分,利用如下代码可实现插入页眉页脚 java 代码publicstaticvoidsetHeader(WritableSheet dataSheet,Str ...

最新文章

  1. 比较和逻辑运算符 011
  2. java class 关键字_java关键字及其作用
  3. J2EE开发技术点4:ajax技术
  4. TIOBE 6月排行:C# 以微弱的优势超过了 Visual Basic .NET 的排名,再次进入 TOP 5
  5. statuml怎么添加用户_UML课后习题答案
  6. ajaxGet 获取封装
  7. 微型计算机机房湿度不宜过大对,2013年计算机等考三级PC技术练习题及答案(1)...
  8. JavaScript常用开发框架总结
  9. web.xml不同版本的头文件配置
  10. [linux]查看机器有几个cpu,是否支持64位
  11. dbeaver 设置编码_物料编码管理程序
  12. OpenCV4学习笔记(27)——轮廓的最大内接圆
  13. Switch视频转换器方案
  14. win10专业版关闭Windows Defender Antivirus
  15. 微信小游戏声明文件(d.ts)
  16. 亲戚(relative)
  17. 详解C语言实现植物大战僵尸阳光9999
  18. tf.round(): 四舍六入五取偶
  19. 自由 Freedom
  20. Rust: 基于 napi-rs 开发 Node.js 原生模块

热门文章

  1. Python爬虫 | 2008-2018年各省专利统计数据
  2. 三星S5P 6818开发板无线使用ADB功能方法
  3. [已解决]VitrualBox 启动linux虚拟机后,无法访问网络解决方法
  4. 布法罗纽约州立大学计算机排名,2019年QS世界大学排名纽约州立大学布法罗分校排名第313...
  5. 打开组策略 计算机配置,组策略怎么打开? 打开组策略命令与4种方法-电脑教程...
  6. 链游:不要只争朝夕,要寄望百年
  7. 如何实现幻灯片效果/图片轮播
  8. 如何让百度快速收录网站及文章
  9. 谷歌大神 Jeff Dean 的那些“惊人真相”
  10. 提取自制VOC数据集中测试集的标签和图像