最近开发一个窗体程序,需要用到表格进行报表的数据展示,由于是报表,大家懂得,表头单元格那是各种合并....开始我还以为会很简单,直接传个什么参数或者什么的就能直接搞定,于是各种百度,才发现这么强大的java,居然没有为这一块提供相关的api,需要自己重写相关的类,才能实现这一效果,无语.....表示有点小失望。
看了好几篇大牛的博文(都是不知道多少年前的写的,其中有一篇居然是98年写的。。。相信大家在找这方面的资料的时候,肯定看过这个。。手动狗头,,, 在好几个地方都看到别人用这个类,只是在这个基础上做了其他的改动而已)
关于这个我自己也看的有点蒙,非常绕,感觉用起来也不是很顺手,下面贴一下我实际用的过程中的代码块
最开始表头一直弄的很怪异:
因为在new GroupHeader("鉴伪情况" ) 一直指定了开始列 new GroupHeader("鉴伪情况" , 2 )和“质量挑剔情况”那块都是在new GroupHeader加了开始列的参数,后面抱着试一试的把开始列去掉了,就好了..... 说实话没整明白....
最终的效果:
对了既然用来别人的东西就贴一下地址:https://download.csdn.net/download/laizhenhai88/5188318
由于上面的写法有点繁琐,所以自己想写一套自己自认为比较方便的写法。经过了一段时间的摸索(主要是看了一篇博文:https://blog.csdn.net/h932075062/article/details/8632619),明白了对多行表头的实现(自己之前想得太复杂了.....)。
下面贴自己简单实现多行表头的代码,其实就需要2个类:
表头UI类
package org.hxb.header;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumnModel;
import org.hxb.table.ComplexTable;
/**
* 实现表头渲染UI,主要功能时画出表头的单元格
* @author 雷锋
* @Date 2019年5月27日
*/
public class ComplexHeaderUI extends BasicTableHeaderUI {
private Object[][] headerRows;
private JTable table;
private int singleRowHeight;
public ComplexHeaderUI(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 ));
}
/**
* 重写BasicTableHeaderUI.paint的方法是最重要的部分
*/
@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();
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;
}
//得到一个单元格,起点坐标、宽度、高度
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 = header.getColumnModel();
for( int i = 0 ; i < mergeCellNum ; i++ ){
width += colModel.getColumn( column + i ).getWidth();
}
return width;
}
//根据单元格所在列得到x轴坐标
private int getCellX( int column ){
int width = 0;
TableColumnModel colModel = header.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;
}
//得到具有指定文本的标签
private JLabel getComponent(String text){
JLabel label = new JLabel(text, JLabel.CENTER);
label.setFont(new Font("Dialog", Font.PLAIN, 12));
label.setBorder(UIManager.getBorder("TableHeader.cellBorder"));
return label;
}
/**
* 画出单元格
* @param g 画笔
* @param cellRect 坐标 宽度 高度
* @param component 单元格内的组件
*/
private void paintCell(Graphics g, Rectangle cellRect , String text) {
Component component = this.getComponent(text);
rendererPane.paintComponent(g, component, header, cellRect.x, cellRect.y,
cellRect.width, cellRect.height, true);
}
}
表格类,继承JTable
package org.hxb.table;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import org.hxb.header.ComplexHeaderUI;
public class ComplexTable extends JTable {
public final static Object mergeCellX = "mergeCellX";//标识单元格是否要被横向合并
public final static Object mergeCellY = "mergeCellY";//标识单元格是否要被纵向合并
public ComplexTable(Object[][] headerRows , Object[][] body){
super( new DefaultTableModel(body, headerRows[0]) );
// super( 0 , headerRows[0].length );
this.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
//设置table内容居中
DefaultTableCellRenderer tcr = new DefaultTableCellRenderer();
tcr.setHorizontalAlignment(JLabel.CENTER);// 这句和上句作用一样
this.setDefaultRenderer(Object.class, tcr);
//设置鼠标点击选中单元格,而不是选中整行
// getColumnModel().setColumnSelectionAllowed(true);
//TODO: 扩展body的合并
// this.setUI(newUI);
//设置表头UI
this.getTableHeader().setUI(new ComplexHeaderUI(headerRows , this));
}
}
测试类,Main方法
package text;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import org.hxb.table.ComplexTable;
public class test {
public static void main(String[] args) {
Object[][] headerRows = new Object[2][6];
headerRows[0] = new Object[]{"1-1",ComplexTable.mergeCellX,"1-3","1-4",ComplexTable.mergeCellX,"1-6"};
//此处2-5是不会显示出来的,因为1-4向下合并了一行 + 向右合并了一列 , 而2-5被这个矩形范围包括了
headerRows[1] = new Object[]{"2-1", "2-2" ,ComplexTable.mergeCellY,ComplexTable.mergeCellY,"2-5","2-6"};
Object[][] body = new Object[10][6];
for( int row = 0 ; row < body.length ; row++ ){
Object[] tableRow = body[row];
for( int col = 0 ; col < tableRow.length ; col++ ){
tableRow[col] = row + "_" + col;
}
}
JFrame frame=new JFrame();
frame.setTitle("复杂表头demo");
frame.getContentPane().add(new JScrollPane(new ComplexTable(headerRows , body)));
frame.setSize(800,500);
frame.setDefaultCloseOperation(3);
frame.setVisible(true);
}
}
运行效果图: