JTable单元格合并
合并指定的一列或几列中行连续相同的单元格的值。
效果如下
CombineData.java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class CombineData {
public ArrayList combineColumns = new ArrayList();//用于保存需要合并的列号
private String[][] datas;//table的数据,用来计算合并的单元格
private ArrayList> rowPoss;
private ArrayList> rowCounts;
public CombineData() {
}
public CombineData(String[][] datas, int... combineColumns) {
this.datas = datas;
for (int i = 0; i < combineColumns.length; i++) {
if (combineColumns[i] < 0) {
continue;
}
this.combineColumns.add(combineColumns[i]);
}
process();
}
public CombineData(String[][] datas, List combineColumns) {
this.datas = datas;
for (Integer column : combineColumns) {
if (column < 0) {
continue;
}
this.combineColumns.add(column);
}
process();
}
public void initData(String[][] datas, int... combineColumns) {
this.datas = datas;
this.combineColumns.clear();
for (int i = 0; i < combineColumns.length; i++) {
if (combineColumns[i] < 0) {
continue;
}
this.combineColumns.add(combineColumns[i]);
}
process();
}
public void initData(String[][] datas, List combineColumns) {
this.datas = datas;
this.combineColumns.clear();
for (Integer column : combineColumns) {
if (column < 0) {
continue;
}
this.combineColumns.add(column);
}
process();
}
private void process() {
rowPoss = new ArrayList>();
rowCounts = new ArrayList>();
for (Integer integer : combineColumns) {
HashMap rowPos = new HashMap();
HashMap rowCount = new HashMap();
String pre = "";
int count = 0;
int start = 0;
for (int i = 0; i < datas.length; i++) {
String[] data = datas[i];
if (pre.equals(data[integer])) {
count++;
} else {
rowCount.put(start, count);
pre = data[integer];
count = 1;
start = i;
}
rowPos.put(i, start);
}
rowCount.put(start, count);
rowPoss.add(rowPos);
rowCounts.add(rowCount);
}
}
/**
* 返回table中row行column列单元格所跨行数
*/
public int span(int row, int column) {
int index = combineColumns.lastIndexOf(column);
if (index != -1) {
return rowCounts.get(index).get(rowPoss.get(index).get(row));
} else {
return 1;
}
}
/**
* 返回table中row行column列单元格所在的合并单元格的起始行位置
*/
public int visibleCell(int row, int column) {
int index = combineColumns.lastIndexOf(column);
if ((index != -1) && row > -1) {
return rowPoss.get(index).get(row);
} else {
return row;
}
}
}
CombineTable.java
import javax.swing.*;
import java.awt.*;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
public class CombineTable extends JTable {
public CombineData combineData;
public CombineTable(TableModel tableModel) {
super(tableModel);
super.setUI(new CombineTableUI());
}
public CombineTable(CombineData combineData, TableModel tableModel) {
super(tableModel);
this.combineData = combineData;
for (Integer column : combineData.combineColumns) {
TableColumn tableColumn = super.columnModel.getColumn(column);
tableColumn.setCellRenderer(new CombineColumnRender());
}
super.setUI(new CombineTableUI());
}
public void setCombineData(CombineData combineData) {
this.combineData = combineData;
for (Integer column : combineData.combineColumns) {
TableColumn tableColumn = super.columnModel.getColumn(column);
tableColumn.setCellRenderer(new CombineColumnRender());
}
}
@Override
public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
// required because getCellRect is used in JTable constructor
if (combineData == null) {
return super.getCellRect(row, column, includeSpacing);
}
// add widths of all spanned logical cells
int sk = combineData.visibleCell(row, column);
//Rectangle r1 = super.getCellRect(row, sk, includeSpacing);
Rectangle rect1 = super.getCellRect(sk, column, includeSpacing);
if (combineData.span(sk, column) != 1) {
for (int i = 1; i < combineData.span(sk, column); i++) {
//r1.width += getColumnModel().getColumn(sk + i).getWidth();
rect1.height += this.getRowHeight(sk + i);
}
}
return rect1;
}
@Override
public int rowAtPoint(Point p) {
int column = super.columnAtPoint(p);
// -1 is returned by columnAtPoint if the point is not in the table
if (column < 0) {
return column;
}
int row = super.rowAtPoint(p);
return combineData.visibleCell(row, column);
}
@Override
public boolean isCellEditable(int row, int column) {
if (combineData.combineColumns.contains(column)) {
return false;
}
return super.isCellEditable(row, column);
}
@Override
public boolean isCellSelected(int row, int column) {
if (combineData.combineColumns.contains(column)) {
return false;
}
return super.isCellSelected(row, column);
}
}
CombineTableUI.java
import javax.swing.table.*;
import javax.swing.plaf.basic.*;
import java.awt.*;
import javax.swing.*;
class CombineTableUI extends BasicTableUI {
@Override
public void paint(Graphics g, JComponent c) {
Rectangle r = g.getClipBounds();
rendererPane.removeAll();
int firstCol = table.columnAtPoint(new Point(r.x, 0));
int lastCol = table.columnAtPoint(new Point(r.x + r.width, 0));
// -1 is a flag that the ending point is outside the table
if (lastCol < 0) {
lastCol = table.getColumnCount() - 1;
}
for (int i = firstCol; i <= lastCol; i++) {
paintCol(i, g);
}
paintGrid(g, 0, table.getRowCount() - 1, 0, table.getColumnCount() - 1);
}
private void paintCol(int col, Graphics g) {
Rectangle r = g.getClipBounds();
for (int i = 0; i < table.getRowCount(); i++) {
Rectangle r1 = table.getCellRect(i, col, true);
if (r1.intersects(r)) // at least a part is visible
{
int sk = ((CombineTable) table).combineData.visibleCell(i, col);
paintCell(sk, col, g, r1);
// increment the column counter
i += ((CombineTable) table).combineData.span(sk, col) - 1;
}
}
}
private void paintCell(int row, int column, Graphics g, Rectangle area) {
int verticalMargin = table.getRowMargin();
int horizontalMargin = table.getColumnModel().getColumnMargin();
area.setBounds(area.x + horizontalMargin / 2, area.y + verticalMargin / 2, area.width - horizontalMargin, area.height - verticalMargin);
if (table.isEditing() && table.getEditingRow() == row && table.getEditingColumn() == column) {
Component component = table.getEditorComponent();
component.setBounds(area);
component.validate();
} else {
TableCellRenderer renderer = table.getCellRenderer(row, column);
Component component = table.prepareRenderer(renderer, row, column);
if (component.getParent() == null) {
rendererPane.add(component);
}
rendererPane.paintComponent(g, component, table, area.x, area.y,
area.width, area.height, 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()) {
CombineData cMap = ((CombineTable) table).combineData;
for (int row = rMin; row <= rMax; row++) {
for (int column = cMin; column <= cMax; column++) {
Rectangle cellRect = table.getCellRect(row, column, true);
if (cMap.combineColumns.contains(column)) {
int visibleCell = cMap.visibleCell(row, column);
int span = cMap.span(row, column);
if (span > 1 && row < visibleCell + span - 1) {
} else {
g.drawLine(cellRect.x, cellRect.y + cellRect.height - 1, cellRect.x + cellRect.width - 1, cellRect.y + cellRect.height - 1);
}
} else {
g.drawLine(cellRect.x, cellRect.y + cellRect.height - 1, cellRect.x + cellRect.width - 1, cellRect.y + cellRect.height - 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);
}
}
}
}
}
CombineColumnRender.java
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
/**
* 设置需要合并的列的单元格不能被选中,不能聚焦
* @author hualun-alan
*/
class CombineColumnRender extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
CombineTable cTable = (CombineTable) table;
if (cTable.combineData.combineColumns.contains(column)) {
hasFocus = false;
}
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
Test.java
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.table.*;
public class Test {
public static void main(String args[]) {
JFrame jf = new JFrame("Cell Combine Table");
JTable cTable = getTable1();
jf.getContentPane().add(new JScrollPane(cTable));
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(500, 500);
jf.setVisible(true);
}
private static CombineTable getTable1() {
String[][] datas = new String[10][6];
for (int i = 0; i < datas.length; i++) {
String[] data = datas[i];
for (int j = 0; j < data.length; j++) {
data[j] = "";
}
data[0] = String.valueOf((int) (i / 3));
}
ArrayList combineColumns = new ArrayList();
combineColumns.add(0);
CombineData m = new CombineData(datas, combineColumns);
DefaultTableModel tm = new DefaultTableModel(datas, new String[]{"1", "2", "3", "4", "5"});
CombineTable cTable = new CombineTable(m, tm);
TableColumn column = cTable.getColumnModel().getColumn(0);
column.setCellRenderer(new CombineColumnRender());
column.setWidth(50);
column.setMaxWidth(50);
column.setMinWidth(50);
cTable.setCellSelectionEnabled(true);
return cTable;
}
private static CombineTable getTable2() {
String[][] datas = new String[10][6];
for (int i = 0; i < datas.length; i++) {
String[] data = datas[i];
for (int j = 0; j < data.length; j++) {
data[j] = "";
}
data[0] = String.valueOf((int) (i / 4));
data[1] = String.valueOf((int) (i / 2));
}
CombineData m = new CombineData(datas, 0, 1);
DefaultTableModel tm = new DefaultTableModel(datas, new String[]{"1", "2", "3", "4", "5"});
CombineTable cTable = new CombineTable(m, tm);
TableColumnModel columnModel = cTable.getColumnModel();
for (Integer integer : m.combineColumns) {
TableColumn column = columnModel.getColumn(integer);
column.setCellRenderer(new CombineColumnRender());
column.setWidth(50);
column.setMaxWidth(50);
column.setMinWidth(50);
}
cTable.setCellSelectionEnabled(true);
return cTable;
}
}