JTable复杂表头实现

最近开发一个窗体程序,需要用到表格进行报表的数据展示,由于是报表,大家懂得,表头单元格那是各种合并....开始我还以为会很简单,直接传个什么参数或者什么的就能直接搞定,于是各种百度,才发现这么强大的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);
	}
	
}

运行效果图:

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值