GridBagModel:抽象模型接口。该接口用于描述表格中单元格的合并状态。
DefaultGridBagTableModel:GridBagModel的默认实现。
GridBagTable:继承自JTable的控制器。通过该类中的方法控制表格单元的合并和拆分。
GridBagTableUI:GridBagTable对应的UI。
TODO:(已合并)行、列的插入,删除操作对应的GridBagModel的修改,不过已留接口。
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 [email protected]
*/
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= 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);
}
}
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();
}
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> gridInfo;
DefaultGridBagTableModel(AbstractTableModel model){
gridInfo = new Vector>();
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>();
else
gridInfo.clear();
if( model == null )
return;
//初始化,每个格子占的格子数为(1,1);
for(int row=model.getRowCount(); --row>=0;){
List infos = new Vector();
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 1 )
return false;
}
Arrays.sort(columns);
for(int index=0; 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 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 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 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 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 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 rowInfo : gridInfo ){
for(Point grid : rowInfo){
sb.append("["+grid.x+","+grid.y+"], ");
}
sb.append("\n");
}
return sb.toString();
}
}
getShowVerticalLines()
returns true and paints getShowHorizontalLines()
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 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 aRect, using the grid
* color set with setGridColor. Paints vertical lines
* if getShowVerticalLines()
returns true and paints
* horizontal lines if getShowHorizontalLines()
* 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; n1 ){
for( int n=1; n= 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
测试代码:
GridBagModel:抽象模型接口。该接口用于描述表格中单元格的合并状态。
DefaultGridBagTableModel:GridBagModel的默认实现。
GridBagTable:继承自JTable的控制器。通过该类中的方法控制表格单元的合并和拆分。
GridBagTableUI:GridBagTable对应的UI。
TODO:(已合并)行、列的插入,删除操作对应的GridBagModel的修改,不过已留接口。
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 [email protected]
*/
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= 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);
}
}
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();
}
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> gridInfo;
DefaultGridBagTableModel(AbstractTableModel model){
gridInfo = new Vector>();
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>();
else
gridInfo.clear();
if( model == null )
return;
//初始化,每个格子占的格子数为(1,1);
for(int row=model.getRowCount(); --row>=0;){
List infos = new Vector();
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 1 )
return false;
}
Arrays.sort(columns);
for(int index=0; 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 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 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 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 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 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 rowInfo : gridInfo ){
for(Point grid : rowInfo){
sb.append("["+grid.x+","+grid.y+"], ");
}
sb.append("\n");
}
return sb.toString();
}
}
getShowVerticalLines()
returns true and paints getShowHorizontalLines()
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 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 aRect, using the grid
* color set with setGridColor. Paints vertical lines
* if getShowVerticalLines()
returns true and paints
* horizontal lines if getShowHorizontalLines()
* 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; n1 ){
for( int n=1; n= 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
测试代码: