jar
jinlin
2025-03-04 23f02e6b45dd7cf0ab2e7827144913ca59575ea4
src/main/java/com/example/client/utils/ComplexTableUI.java
@@ -12,130 +12,153 @@
 * @author 雷锋
 * @Date 2019年5月27日
 */
import javax.swing.*;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.MouseEvent;
public class ComplexTableUI extends BasicTableUI {
    private Object[][] headerRows;
    private Object[][] headerRows; // 表头数据,用于判断合并逻辑
    private JTable table;
    private int singleRowHeight;
    public ComplexTableUI(Object[][] headerRows,JTable table){
    private int singleRowHeight; // 每行的高度
    private int headerHeight; // 表头的总高度
    public ComplexTableUI(Object[][] headerRows, JTable table) {
        this.headerRows = headerRows;
        this.table = table;
        //获取单行的高度,不能使用header.getHeight()获取高度,因为此时表头还没初始化完毕,获取出来的高度是0
        this.singleRowHeight = table.getRowHeight();
//      System.out.println(table.getRowHeight());
        JTableHeader tableHeader = table.getTableHeader();
        //设置表头不允许拖动 、由于合并了单元格,拖动之后会乱
        tableHeader.setReorderingAllowed(false);
        //设置表头整体高度、宽度
        tableHeader.setPreferredSize(new Dimension(table.getWidth(), singleRowHeight * headerRows.length ));
        this.singleRowHeight = table.getRowHeight(); // 获取单行高度
        this.headerHeight = singleRowHeight * headerRows.length; // 计算表头总高度
    }
    /**
     * 重写BasicTableHeaderUI.paint的方法是最重要的部分
     */
    @Override
    public void installUI(JComponent c) {
        super.installUI(c);
        // 禁用列的拖动,因为合并单元格可能导致布局混乱
        table.getTableHeader().setReorderingAllowed(false);
    }
    @Override
    public void paint(Graphics g, JComponent c) {
        for( int row = 0 ; row < headerRows.length ; row++ ){
            Object[] headerRow = headerRows[row];
            for( int col = 0 ; col < headerRow.length ; col++ ){
                Object cell = headerRow[col];
                //如果单元格为合并类单元格、获取其上方是X合并类单元格 + 左边是Y合并类单元格,那么该单元格不需要在窗口展示
                if( cell == ComplexTable.mergeCellX || cell == ComplexTable.mergeCellY || ( col > 0 && row > 0 && headerRow[col - 1] == ComplexTable.mergeCellY && headerRows[row-1][col] == ComplexTable.mergeCellX )  )
                    continue;
                Rectangle rect = this.getCellRect(row, col);
                String text = cell == null ? "" : cell.toString();
        super.paint(g, c); // 绘制默认的表格内容
        // 遍历表格主体的每一行和每一列
        for (int row = 0; row < table.getRowCount(); row++) {
            for (int col = 0; col < table.getColumnCount(); col++) {
                if (isMergedCell(row, col)) {
                    continue; // 跳过被合并的单元格
                }
                // 获取单元格的绘制区域
                Rectangle rect = getCellRect(row, col);
                // 获取单元格内容
                Object value = table.getValueAt(row, col);
                String text = value == null ? "" : value.toString();
                // 绘制单元格
                paintCell(g, rect, text);
            }
        }
    }
    /**
     * 获取当前单元格需要占多少个单位,比如此时的row+1行col列的值=mergeCell,那么说明当前单元格需要占2行
     * @param row
     * @param col
     * @return
     * 判断单元格是否被合并
     */
    private Rectangle getCellRect(int row , int col){
        int mergeRowNum = 1;
        int nextRow = row;
        //判断出y轴方向合并了几行
        while( ++nextRow < headerRows.length ){
            Object nextRowCell = headerRows[nextRow][col];
            if( nextRowCell == ComplexTable.mergeCellY )
                mergeRowNum++;
            else
                break;
        }
        int mergeCellNum = 1;
        int nextCol = col;
        Object[] headerRow = headerRows[row];
        //判断x轴方向合并了几列
        while( ++nextCol < headerRow.length ){
            Object nextCell = headerRow[nextCol];
            if( nextCell == ComplexTable.mergeCellX )
                mergeCellNum++;
            else
                break;
    private boolean isMergedCell(int row, int col) {
        // 示例:假设合并逻辑基于表格主体数据
        Object cellValue = table.getValueAt(row, col);
        return cellValue == ComplexTable.mergeCellX || cellValue == ComplexTable.mergeCellY;
    }
    /**
     * 获取单元格的绘制区域
     */
    private Rectangle getCellRect(int row, int col) {
        // 获取默认单元格区域
        Rectangle rect = table.getCellRect(row, col, false);
        // 调整 y 坐标,考虑表头高度
        rect.y += headerHeight;
        // 如果需要合并单元格,调整区域大小
        if (isMergedCell(row, col)) {
            int mergeRowNum = getMergeRowNum(row, col);
            int mergeColNum = getMergeColNum(row, col);
            rect.height = getCellHeight(mergeRowNum);
            rect.width = getCellWidth(col, mergeColNum);
        }
        //得到一个单元格,起点坐标、宽度、高度
        Rectangle rect = new Rectangle();
        rect.height = this.getCellHeight(mergeRowNum);
        rect.width = this.getCellWidth( col , mergeCellNum);
        rect.y = this.getCellY(row);
        rect.x = this.getCellX( col );
        return rect;
    }
    //根据合并行数得到单元格的高度
    private int getCellHeight( int mergeRowNum ){
        int height = 0;
        for( int i = 0 ; i < mergeRowNum ; i++ )
            height += singleRowHeight;
        return height;
    }
    //根据合并列数得到单元格宽度
    private int getCellWidth( int column , int mergeCellNum ){
        int width = 0;
        TableColumnModel colModel = table.getColumnModel();
        for( int i = 0 ; i < mergeCellNum ; i++ ){
            width += colModel.getColumn( column + i ).getWidth();
    /**
     * 获取合并的行数
     */
    private int getMergeRowNum(int row, int col) {
        int mergeRowNum = 1;
        for (int nextRow = row + 1; nextRow < table.getRowCount(); nextRow++) {
            Object nextRowCell = table.getValueAt(nextRow, col);
            if (nextRowCell == ComplexTable.mergeCellY) {
                mergeRowNum++;
            } else {
                break;
            }
        }
        return width;
    }
    //根据单元格所在列得到x轴坐标
    private int getCellX( int column ){
        int width = 0;
        TableColumnModel colModel = table.getColumnModel();
        for( int i = 0 ; i < column ; i++ ){
            width += colModel.getColumn( i ).getWidth();
        }
        return width;
    }
    //根据单元格所在行得到y轴坐标
    private int getCellY( int row ){
        int height = 0;
        for( int i = 0 ; i < row ; i++ ){
            height += singleRowHeight;
        }
        return height;
        return mergeRowNum;
    }
    //得到具有指定文本的标签
    private JLabel getComponent(String text){
    /**
     * 获取合并的列数
     */
    private int getMergeColNum(int row, int col) {
        int mergeColNum = 1;
        for (int nextCol = col + 1; nextCol < table.getColumnCount(); nextCol++) {
            Object nextCell = table.getValueAt(row, nextCol);
            if (nextCell == ComplexTable.mergeCellX) {
                mergeColNum++;
            } else {
                break;
            }
        }
        return mergeColNum;
    }
    /**
     * 根据合并行数计算单元格高度
     */
    private int getCellHeight(int mergeRowNum) {
        return mergeRowNum * singleRowHeight;
    }
    /**
     * 根据合并列数计算单元格宽度
     */
    private int getCellWidth(int col, int mergeColNum) {
        TableColumnModel colModel = table.getColumnModel();
        int width = 0;
        for (int i = 0; i < mergeColNum; i++) {
            width += colModel.getColumn(col + i).getWidth();
        }
        return width;
    }
    /**
     * 绘制单元格
     */
    private void paintCell(Graphics g, Rectangle rect, String text) {
        Component component = getComponent(text);
        rendererPane.paintComponent(g, component, table, rect.x, rect.y,
                rect.width, rect.height, true);
    }
    /**
     * 创建用于绘制的组件
     */
    private JLabel getComponent(String text) {
        JLabel label = new JLabel(text, JLabel.CENTER);
        Font font = new Font("宋体", Font.BOLD, 23);
        Font font = new Font("宋体", Font.PLAIN, 16);
        label.setFont(font);
        label.setBorder(UIManager.getBorder("TableHeader.cellBorder"));
        label.setBorder(UIManager.getBorder("Table.cellBorder"));
        return label;
    }
    private void paintCell(Graphics g, Rectangle cellRect , String text) {
        Component component = this.getComponent(text);
        rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y,
                cellRect.width, cellRect.height, true);
    }
}
}