Jtable单元格合并的问题

这篇博客探讨了在Java Swing中如何实现JTable组件的单元格合并。作者通过取消表格网格线并创建自定义的MergeTableModel,利用数据值相同来决定合并条件。详细步骤包括取消网格线绘制,以及使用Map存储相同数据的单元格坐标。

最近有个任务,其中需要实现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();
        }
}


3、定义MergeCellRender继承DefaultTableCellRender,重写getTableCellRenderComponent()方法,判断如果当前单元格需要被合并(数据值跟前面的单元格相同),则将当前的单元格控件(JLabel)的text设置为空字符串。然后给这个label设置边框线,注意需要新建一种边框线,因为只需要画一部分的边框线,而不是把整个矩形边框画出来。

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的网格单元格数据类型,里面有个属性来标识当前单元格数据是否需要被合并掉。




评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值