最近有个任务,其中需要实现table网格的单元格合并,想了很久,最后用了一种简单但是感觉不怎么好的方式。
大概思路是这样子的:将网格中相同数据的单元格进行合并。所以这里单元格是否要合并取决于单元格的数据是否一样。
以下是详细的实现步骤:
1 、先取消网格的绘制网格线的功能。
JTable table = new JTable();
table.setModel(tableModel);
table.setShowHorizontalLines(false);
table.setShowVerticalLines(false);2、定义新的网格模型MergeTableModel继承DefaultTableModel,新增一个map用来存放数据值相同的单元格坐标。
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import javax.swing.table.DefaultTableModel;
import com.griddata.MergeTableData;
public class MergeTableModel extends DefaultTableModel {
private static final long serialVersionUID = -5431441008936488938L;
public static final int ROW = 0;
public static final int COLUMN = 1;
// 按照单元格的值分组存放被隐藏掉的单元格坐标
private Map<String, Set<Integer[]>> coverRectIndexMap = new HashMap<String, Set<Integer[]>>();
public final int columnWidth = 200;
public MergeTableModel() {
super();
}
public MergeTableModel(Object[] columnNames, int rowCount) {
super(columnNames, rowCount);
}
public MergeTableModel(MergeTableData[][] data, Object[] columnNames) {
super(data, columnNames);
}
public void addCoverRectIndex(String cellValue, int rowIndex, int colIndex) {
Set<Integer[]> coverSet = this.coverRectIndexMap.get(cellValue);
if (coverSet == null) {
coverSet = new HashSet<Integer[]>();
this.coverRectIndexMap.put(cellValue, coverSet);
coverSet.add(new Integer[] { rowIndex, colIndex });
} else {
if (isCellCovered(coverSet, rowIndex, colIndex)) {
return;
} else {
coverSet.add(new Integer[] { rowIndex, colIndex });
}
}
}
public void removeCoverRectIndex(int rowIndex, int colIndex) {
}
// 判断单元格是否被覆盖
public boolean isCellCovered(String cellValue, int rowIndex, int colIndex) {
Set<Integer[]> coverSet = this.coverRectIndexMap.get(cellValue);
if (coverSet == null) {
return false;
}
return isCellCovered(coverSet, rowIndex, colIndex);
}
private boolean isCellCovered(Set<Integer[]> coverSet, int rowIndex, int colIndex) {
for (Integer[] intArr : coverSet) {
if (intArr[0] == rowIndex && intArr[1] == colIndex) {
return true;
}
}
return false;
}
/**
* 获取同一组被覆盖的单元格中最大的行号,如果没有覆盖的单元格则返回-1
*
* @param cellValue
* @return
*/
public int getMaxCoverRow(String cellValue) {
Set<Integer[]> coverSet = this.coverRectIndexMap.get(cellValue);
if (coverSet == null) {
return -1;
}
int maxRow = 0;
for (Integer[] intArr : coverSet) {
if (maxRow < intArr[ROW]) {
maxRow = intArr[ROW];
}
}
return maxRow;
}
/**
* 获取同一组被覆盖的单元格中最大的列号,如果没有覆盖的单元格则返回-1
*
* @param cellValue
* @return
*/
public int getMaxCoverColumn(String cellValue) {
Set<Integer[]> coverSet = this.coverRectIndexMap.get(cellValue);
if (coverSet == null) {
return -1;
}
int maxCloumn = 0;
for (Integer[] intArr : coverSet) {
if (maxCloumn < intArr[COLUMN]) {
maxCloumn = intArr[COLUMN];
}
}
return maxCloumn;
}
/**
* 判断当前的值是否有隐藏的单元格
*
* @param cellValue
* @return
*/
public boolean hasCoverColumns(String cellValue) {
Set<Integer[]> coverSet = this.coverRectIndexMap.get(cellValue);
if (coverSet == null || coverSet.size() == 0) {
return false;
}
return true;
}
/**
* 清除所有数据
*/
@SuppressWarnings("rawtypes")
public void removeAllDatas() {
this.setDataVector(new Vector(), new Vector());
fireTableDataChanged();
}
}
MergeCellRender
import java.awt.Component; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.table.DefaultTableCellRenderer; import com.grid.border.PartLineBorder; import com.griddata.MergeTableData; public class MergeCellRender extends DefaultTableCellRenderer { private static final long serialVersionUID = -6176451420389928606L; @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); MergeTableModel tableModel = (MergeTableModel) table.getModel(); MergeTableData tableData = (MergeTableData) tableModel.getValueAt(row, column); if (!tableData.isMerge()) { label.setBorder(new PartLineBorder(false, false, true, true)); return label; } if (tableModel.isCellCovered(value.toString(), row, column)) { boolean isCoverMaxRow = tableModel.getMaxCoverRow(value.toString()) == row; boolean isCoverMaxColumn = tableModel.getMaxCoverColumn(value.toString()) == column; label.setBorder(new PartLineBorder(false, false, isCoverMaxRow, isCoverMaxColumn)); label.setText(""); return label; } // 从当前单元格的下一行开始,如果数据值相同,则将单元格标示为隐藏 int maxRow = row; for (int rowIndex = row + 1; rowIndex < table.getRowCount(); rowIndex++) { MergeTableData nextRowCellData = (MergeTableData) tableModel.getValueAt(rowIndex, column); if (nextRowCellData != null && nextRowCellData.equals(tableData) && nextRowCellData.isMerge()) {// 值相同 maxRow = rowIndex; } else { // 单元格的值相同要连续 break; } } int maxColumn = column; for (int columnIndex = column + 1; columnIndex < table.getColumnCount(); columnIndex++) { MergeTableData nextColumnCellData = (MergeTableData) tableModel.getValueAt(row, columnIndex); if (nextColumnCellData != null && nextColumnCellData.equals(tableData) && nextColumnCellData.isMerge()) { maxColumn = columnIndex; } else { break; } } // 将这个单元格加到mergeTableModel的隐藏单元格集合中去,第一个除外 for (int i = row; i <= maxRow; i++) { for (int j = column; j <= maxColumn; j++) { if (i == row && j == column) { continue; } else { tableModel.addCoverRectIndex(value.toString(), i, j); } } } // 如果当前的单元格对应的值没有隐藏的单元格,那么就应该画出右边界和下边界 if (!tableModel.hasCoverColumns(value.toString())) { label.setBorder(new PartLineBorder(false, false, true, true)); } else { // 如果当前单元格就是某一个方向的边界,需要画它的边界线 boolean isCoverMaxRow = tableModel.getMaxCoverRow(value.toString()) == row; boolean isCoverMaxColumn = tableModel.getMaxCoverColumn(value.toString()) == column; label.setBorder(new PartLineBorder(false, false, isCoverMaxRow, isCoverMaxColumn)); } return label; } }
PartLineBorder
class PartLineBorder extends AbstractBorder{
protected boolean top,left,bottom,right;
public PartLineBorder(boolean top,boolean left,boolean bottom,boolean right){
this.top=top;
this.left=left;
this.bottom=bottom;
this.right=right;
}
public paintBorder(Component c,Graphics g,int x,int y,int width,int height){
if(this.top){
g.drawLine(x,y,x+width-1,y);
}
if(this.left){
g.drawLine(x,y,x,y+height-1);
}
if(this.bottom){
g.drawLine(x,y+height-1,x+width-1,y+height-1);
}
if(this.right){
g.drawLine(x+width-1,y,x+width-1,y+height-1);
}
}
}4、注:这里我定义了一种MergeTableData的网格单元格数据类型,里面有个属性来标识当前单元格数据是否需要被合并掉。
这篇博客探讨了在Java Swing中如何实现JTable组件的单元格合并。作者通过取消表格网格线并创建自定义的MergeTableModel,利用数据值相同来决定合并条件。详细步骤包括取消网格线绘制,以及使用Map存储相同数据的单元格坐标。

2419

被折叠的 条评论
为什么被折叠?



