JTable 单元格合并 【转】




单元格合并

一、单元格合并。

(1)我们可以使用Jtable的三个方法:getCellRect(),columnAtPoint(),and
rowAtPoint()。第一个方法返回一个单元格的边界(Rectangle类),第二、三个方法分别返回屏幕指定位置的列和行。为了实现单元格合并,我们需要重载(overwrite)这三个方法。 

(2)另外我们需要找出渲染Jtable的ComponentUI对象,并且修改它以达到我们的目的。 

(3)创建新的类记录单元格合并情况的数据模型,它要包涵一个方法来取得单元格的所跨越的列数。另外,为了使用Jtable画(paint)起来更容易些,我们需要一个方法来确定指定单元格是否被其它单元格所覆盖,被哪个单元格覆盖。我们将这两种方法都集成在接口Cmap里: 






代码:

package com.neuri.ctable; 
public
interface CMap 

/** 
*
@参数row:指定单元格所在的逻辑行 
* @参数column:指定单元格所在的逻辑列 
*
@返回指定单元格所跨越的列数 
*/ 
int span (int row, int
column); 
/** 
* @参数row:指定单元格所在的逻辑行 
*
@参数column:指定单元格所在的逻辑列 
*
@返回覆盖指定单元格的可视单元格的列值,如果单元格本来就是可视的话,返回自身的列值 
*/ 
int
visibleCell(int row, int
column); 


现在我们开始重载上面提及过的三个方法。由于我们目前只关注于跨列单元格的合并,方法rowAtPoint()就不用重载了。然而,方法columnAtPoint()就必须重载了,我们会使用Jtable自身的方法来取得指定单元格的列值,并且计算出覆盖该单元格的可视单元格列值(如果该单元格本来就是可视的,则返回自身列值)。在单元格合并后,在合并区域内只有一个跨越多列的可视单元格,其它被覆盖的单元格则不会再被渲染。当使用getCellRect()方法取得被覆盖的单元格的大小时,都返回覆盖该单元格的可视单元格的大小。 






代码:

package com.neuri.ctable; 
import
javax.swing.*; 
import javax.swing.table.*; 
import
java.awt.*; 
public class CTable
extends JTable { 
public CMap
map; 
public CTable(CMap cmp, TableModel
tbl) { 
 super(tbl); 
 map=cmp; 
 setUI(new
CTUI());//设置Jtable的渲染UI 
 } 
public
Rectangle getCellRect(int row, int
column, boolean includeSpacing){ 
//
该方法是Jtable构建时所必须的 
 if (map==null) return
super.getCellRect(row,column, includeSpacing); 
//
指定单元格的可视单元格列值 
 int
sk=map.visibleCell(row,column); 
 Rectangle
r1=super.getCellRect(row,sk,includeSpacing); 
//
如果指定单元格列宽不为1,累计出跨列单元格的宽度 
 if
(map.span(row,sk)!=1) 
  for (int i=1;
i<map.span(row,sk);
i++){ 
   r1.width+=getColumnModel().getColumn(sk+i).getWidth(); 
   } 
 return
r1; 
 } 
public int columnAtPoint(Point
p) { 
  int x=super.columnAtPoint(p); 
//
当指定位置不在Table内时,返回-1 
  if (x<0) return x; 
  int
y=super.rowAtPoint(p); 
//获取指定位置可视单元格的列值 
  return
map.visibleCell(y,x); 
  } 




现在剩下的就只有创建一个表格的渲染对象了。不同的用户接口管理器(user
interface managers)使用不同的类来画表格。我们会继承子类
javax.swing.plaf.basic.BasicTableUI,并且重载其方法
paintComponent。 

在组件(component)画在屏幕上之前,它已经被初始化和设定好了,所以我们能使用其内部的属性
table 和 rendererPane。属性 table 就是将要被画在屏幕的表格,rendererPane
是用于将单元格画在表格中的特殊对象。使用RendererPane的目的是打破单元格和表格的直接依赖关系,并且防止当一个单元格被修改时重画整个表。 

BasicTableUI的方法getClipBounds是用于找出表格的哪一部分将会被画出来,所以我们首先要知道那些行是可视的,我们可以使用Jtable
的rowAtPoint方法。我们可以使用Rectangle类的intersects方法来确定这些行中的所有单元格是否将会被画在屏幕上。在我们画任何一个单元格前,我们必须检查一下当前单元格是否可视,如果该单元格是被其它单元格所覆盖的,就将覆盖它的单元格画出来。 

根据单元格是否正在被编辑,单元格将会被方法getCellEditor或getCellRenderer所返回的对象画出来。如果你查看一下BasicTableUI的源代码,你就会发现所以单元格会先被BasicTableUI调用table.prepareRenderer画(drawn)出来,然后再被BasicTableUI调用rendererPane.paintComponent来渲染(paint)。我们会采用同样的方法。 






代码:

package com.neuri.ctable; 
import
javax.swing.table.*; 
import
javax.swing.plaf.basic.*; 
import
java.awt.*; 
import javax.swing.*; 
public
class CTUI extends
BasicTableUI 

public void paint(Graphics
g, JComponent c) { 
  Rectangle
r=g.getClipBounds(); 
  int firstRow=table.rowAtPoint(new
Point(0,r.y)); 
  int lastRow=table.rowAtPoint(new
Point(0,r.y+r.height)); 
// -1 is a flag that the ending
point is outside the table 
  if
(lastRow<0) 
  lastRow=table.getRowCount()-1; 
  for
(int i=firstRow; i<=lastRow;
i++) 
  paintRow(i,g); 
 } 
private
void paintRow(int
row, Graphics g) 
 { 
Rectangle
r=g.getClipBounds(); 
for (int i=0;
i<table.getColumnCount();i++) 
 { 
 Rectangle
r1=table.getCellRect(row,i,true); 
 if (r1.intersects(r)) //
at least a part is visible 
 { 
 int
sk=((CTable)table).map.visibleCell(red,i); 
 paintCell(row,sk,g,r1); 
//
increment the column
counter 
 i+=((CTable)table).map.span(row,sk)-1; 
  } 
 } 

private
void paintCell(int
row, int column, Graphics g,Rectangle area) { 
int
verticalMargin = table.getRowMargin(); 
int horizontalMargin
= table.getColumnModel().getColumnMargin(); 

Color c =
g.getColor(); 
g.setColor(table.getGridColor()); 
g.drawRect(area.x,area.y,area.width-1,area.height-1); 
g.setColor(c); 

area.setBounds(area.x
+ horizontalMargin/2, 
area.y +
verticalMargin/2, 
area.width -
horizontalMargin, 
area.height -
verticalMargin); 

if (table.isEditing() &&
table.getEditingRow()==row
&& 
  table.getEditingColumn()==column)

  Component component =
table.getEditorComponent(); 
  component.setBounds(area); 
  component.validate(); 
  } 
else

 TableCellRenderer renderer = table.getCellRenderer(row,
column); 
 Component component =
table.prepareRenderer(renderer, row, column); 
 if
(component.getParent() ==
null) 
 rendererPane.add(component); 
 rendererPane.paintComponent(g,
component, table, area.x, area.y, 
 area.width, area.height,
true); 
  } 
 } 


http://www.swingwiki.org/howto:column_spanning

今天下载了关于java
swing的一个开源项目包tame,不过由于完成年代久远(98年),很多类在新的jdk1.4或jdk1.5上已经会报错。例如AttributiveCellTableModel类的setDataVector方法便需要改为:

public void setDataVector(Vector newData, Vector
columnNames)
{
super.setDataVector(newData, columnNames);
cellAtt
= new
DefaultCellAttribute(dataVector.size(),columnIdentifiers.size());
}
有心重整tame,先记一笔。

①java swing基于MVC架构,或者说是Model-driven结构。以jtable为例,它的特有GUI-State
Model是TableColumnModel(JTable是面向列的,它基于每一列进行绘制和编辑。分别是列绘制器TableCellRenderer和列编辑器TableCellEditor);它的共有GUI-State
Model是Selection Model(和jlist、jtree等共用)。除了GUI-State
Model,还有决定显示在控件中的内容的Application-data model,jtable的Application-data
model是TableModel。实现方式是先定义接口TableModel,再定义抽象类AbstractTableModel实现这个接口,然后由DefaultTableModel实现抽象类。不过一般来说用户需要自己扩展AbstractTableModel实现它的几个方法来获取和设定值。

②以jtable为例,它并未提供实现单元格合并的方法。所以我们需要重载它的三个方法(getCellRect:获取单元格的边界,columnAtPoint和rowAtPoint:分别返回屏幕指定位置的列和行)。

③现在我们需要自己绘制jtable,所以要用到Graphics类。另大部分的swing
components
并不是直接由paint()方法来渲染(render),而是使用ComponentUI对象来完成渲染的。所以我们需要找出渲染Jtable的ComponentUI对象(BasicTableUI),并且修改它(重载paint()方法)以达到我们的目的。

④现在开始具体实现,tame先定义了4个接口(CellAttribute、ColoredCell、CellFont、CellSpan),用DefaultCellAttribute类实现了这四个接口,包含相关表格的基本属性(颜色、字体、合并单元格的属性等),这个类将每个cell定义为一个三维数组int[][][]
span,并且都初始化为1。继承自DefaultTableModel的AttributiveCellTableModel负责初始化table,至此并无特异之处。当需要合并单元格时。监听按钮将调用DefaultCellAttribute的combine方法把被覆盖的单元格的三维数组int[][][]
span设置为小于1,这样在绘制的时候就可以判断哪单元格可见,哪些单元格不可见了。接着通过重载jtable的三个方法得到cell的边界以及行和列的位置,重绘表格的时候通过每个cell返回的结果(边界、位置、是否可见)等循环绘制。

发表于 2006-04-05 09:04 沉思的狗

2010-05-01 11:11

JTABLE单元格合并(转)

最近,我为了做一个管理系统,需要用到合并JTable的单元格。查找了很多资料,终于简单的实现了。现在把代码共享出来,希望对大家有用。

本程序主要实现行的合并,列的合并大家可以根据下面的代码修改。

CMap.java :

package com;

public interface CMap
{
/**
* @param row
* logical cell row
* @param
column
* logical cell column
* @return number of columns spanned a
cell
*/
int span(int
row, int column);

/**
* @param row
* logical cell row
* @param column
*
logical cell column
* @return the index of a visible cell covering a
logical cell
*/
int
visibleCell(int row, int column);
}

CTUI.java :

package com;

import javax.swing.table.*;
import
javax.swing.plaf.basic.*;
import java.awt.*;
import
javax.swing.*;

public
class CTUI extends BasicTableUI {
public
void paint(Graphics g, JComponent c) {
   
Rectangle r = g.getClipBounds();
// int firstRow =
table.rowAtPoint(new Point(0, r.y));//JTable table=super.table就好理解
//
int lastRow = table.rowAtPoint(new Point(0, r.y + r.height));
    int firstCol =
table.columnAtPoint( new Point( r.x , 0 ) );
    int
lastCol = table.columnAtPoint(new Point( r.x + r.width, 0 ));
//
-1 is a flag that the ending point is outside the table
// if (lastRow
< 0)
// lastRow = table.getRowCount() - 1;
  
if (lastCol < 0)
    lastCol = table.getColumnCount()
- 1;
    for
(int i = firstCol; i <= lastCol;
i++)
           
paintCol(i, g);

private void paintCol(int col, Graphics g)
{
  Rectangle r =
g.getClipBounds();
  for (int i = 0; i < table.getRowCount();
i++) {
     Rectangle r1 = table.getCellRect( i,
col, true);
       if (r1.intersects(r))
// at least a part is
visible{
        int sk = ((CTable)
table).map.visibleCell( i, col
);
        paintCell( sk, col, g,
r1);
// increment the column
counter
       i
+= ((CTable) table).map.span( sk, col ) -
1;
      }
  }
}

private void paintCell(int row,
int column, Graphics g, Rectangle area)
{
      int
verticalMargin = table.getRowMargin();
     
int horizontalMargin =
table.getColumnModel().getColumnMargin();

Color c =
g.getColor();
   
g.setColor(table.getGridColor());
    g.drawRect(area.x,
area.y, area.width - 1, area.height - 1);
   
g.setColor(c);

area.setBounds(area.x + horizontalMargin /
2, area.y + verticalMargin/
2, 
                                
area.width - horizontalMargin,

area.height- verticalMargin);

if
(table.isEditing() && table.getEditingRow() == row&&
table.getEditingColumn() == column)
{
             
Component component =
table.getEditorComponent();
                 
component.setBounds(area);
                 
component.validate();
        } else
{
               
TableCellRenderer renderer = table.getCellRenderer(row,
column);
                Component
component = table.prepareRenderer(renderer, row,
column);
        if
(component.getParent() ==
null)
         
rendererPane.add(component);
        
rendererPane.paintComponent(g, component, table, area.x,
area.y, area.width, area.height,
true);
                 
}
         
}
}

CTable.java :

package com;
import javax.swing.*;
import
javax.swing.table.*;
import java.awt.*;

public class CTable extends
JTable {
  public CMap map;

public CTable(CMap cmp, TableModel tbl) {
    
super(tbl);
    map = cmp;
   
setUI(new CTUI());
  }

public Rectangle getCellRect(int row, int column,
boolean includeSpacing) {
// required because getCellRect is used in
JTable constructor
     if (map ==
null)
    return super.getCellRect(row, column,
includeSpacing);
// add widths of all spanned logical
cells
     int sk = map.visibleCell(row,
column);
//Rectangle r1 = super.getCellRect(row, sk,
includeSpacing);
     Rectangle r1 =
super.getCellRect( sk, column,
includeSpacing);
        if
(map.span( sk, column ) !=
1)
           for
(int i = 1; i < map.span( sk, column ); i++) {
//r1.width +=
getColumnModel().getColumn(sk +
i).getWidth();
          
r1.height += this.getRowHeight( sk + i
);
}
return r1;
}
public int rowAtPoint(Point
p) {
    int x = super.columnAtPoint(p);
// -1 is
returned by columnAtPoint if the point is not in the
table
      if (x <
0)
           return
x;
    int y =
super.rowAtPoint(p);
        
return map.visibleCell(y, x);
     }
}

CMap1.java :

/******************************************************************************************

CMap1对CMap地实现

span( ) 表示合并的单元格的列,返回的是合并的格数。

visibleCell() 表示要渲染的格。返回的渲染的开始格的行。

本程序的table是16行10列,合并的单元格是第一列和最后一列(最后一列是第10列)每两个行。

*******************************************************************************************/

package com;

import javax.swing.*;
import javax.swing.table.*;

class CMap1 implements
CMap {
public int span(int row, int column)
{
   if( column == 0 || column == 9 )
   return
2;
   return 1;
 }

public int
visibleCell(int row, int column) {
    if( ( ( row >= 0 ) && ( row <
2 ) ) && ( column == 0 || column == 9 )
)
       return 0;
   if( (
( row >= 2 ) && ( row < 4 ) ) && ( column == 0 ||
column == 9 ) )
      return 2;
  if(
( ( row >= 4 ) && ( row < 6 ) ) && ( column == 0 ||
column == 9 ) )
    return 4;
  if( ( ( row
>= 6 ) && ( row < 8 ) ) && ( column == 0 || column
== 9 ) )
    return 6;
  if( ( ( row >= 8 )
&& ( row < 10 ) ) && ( column == 0 || column == 9 )
)
    return 8;
  if( ( ( row >= 10 )
&& ( row < 12 ) ) && ( column == 0 || column == 9 )
)
    return 10;
  if( ( ( row >= 12 )
&& ( row < 14 ) ) && ( column == 0 || column == 9 )
)
    return 12;
  if( ( ( row >= 14 )
&& ( row < 16 ) ) && ( column == 0 || column == 9 )
)
    return 14;
   
System.out.println( ">>>row = " + row + "column = " + column
);
  return row;
   }
}

下面的程序进行测试。

package com;

import javax.swing.*;
import javax.swing.table.*;

public class CTest
{
public static void main(String args[]) {
JFrame jf = new
JFrame("Table with cell spanning");

CMap m = new CMap1();
DefaultTableModel tm = new
DefaultTableModel( 16, 10 ){
  public
boolean isCellEditable( int indexRow, int indexColumn
)
                
{return
false;}
              
};
//tm.isCellEditable( 16, 10 );
 tm.setValueAt(
"port1", 0, 0);//对一个合并的单元格填一个数据。
jf.getContentPane().add(new JScrollPane(new
CTable(m,
tm)));
jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);
jf.setSize(500,
500);
jf.show();//jf.setVisibale(true)
 
}
}


JTable 单元格合并 【转】,布布扣,bubuko.com

时间: 2024-10-27 03:39:21

JTable 单元格合并 【转】的相关文章

在JTable单元格上 加入组件,并赋予可编辑能力 [转]

表格(单元格放置组件) 对于JTable单元格的渲染主要是通过两个接口来实现的,一个是TableCellRenderer另一个是TableCellEditor,JTable默认是用的是DefaultCellRenderer和DefaultCellEditor,这两个都是在类似JTextfield的一个JComponent的基础上来实现的,如果我们需要在JTable的单元格内放置特殊的控件或者绘制出特殊的效果,就要实现TableCellRenderer和TableCellEditor接口,在其上绘

DataGridView单元格合并

本文章转载:http://www.cnblogs.com/xiaofengfeng/p/3382094.html 图: 代码就是如此简单 文件下载:DataGridView单元格合并源码

devexpress实现单元格合并以及依据条件合并单元格

1.devexpress实现单元格合并非常的简单,只要设置属性[AllowCellMerge=True]就可以了,实现效果如下图: 2.但是在具体要求中并非需要所有的相同单元格都合并,可能需要其他的条件来控制合并.这个时候我们就需要在事件gridView1_CellMerge中来控制了.下图为根据最后一列判断是否合并单元格的效果图(其中第四列设置为不合并<非必需>,这里只是为了达到一个比较效果.). 3.重要代码: int row1 = e.RowHandle1; int row2 = e.R

关于table动态添加数据 单元格合并 数组合并

var newArr = [ {"BranchID":1,"BranchName":"城二","BranchFullName":"城二分公司","IssueTypeID":101,"IssueTypeName":"宏蜂窝连片弱覆盖","Total":242,"WithoutDemand":139,"

NPOI 生成Excel (单元格合并、设置单元格样式:字段,颜色、设置单元格为下拉框并限制输入值、设置单元格只能输入数字等)

NPIO源码地址:https://github.com/tonyqus/npoi NPIO使用参考:源码中的 NPOITest项目 下面代码包括: 1.包含多个Sheet的Excel 2.单元格合并 3.设置单元格样式:字段,颜色 4.设置单元格为下拉框并限制输入值 5.设置单元格只能输入数字 // // GET: /Excel/ public ActionResult Write() { var workbook = new HSSFWorkbook();//从流内容创建Workbook对象

SNF快速开发平台MVC-表格单元格合并组件

1.   表格单元格合并组件 1.1.      效果展示 1.1.1.    页面展现表格合并单元格 图 4.1 1.1.2.    导出excel合并单元格 图 4.2 1.2.      调用说明 1.2.1.    表格合并单元格调用说明 首先,要有一个在viewModel中绑定的表格,例如,我们有一个绑定对象为this.grid的表格 我们要在表格的onLoadSuccess事件中添加一个方法 snf.mergeCellsByParentField ("grid", &quo

mysql GROUP_CONCAT 函数 将相同的键的多个单元格合并到一个单元格

mysql GROUP_CONCAT 函数 将相同的键的多个单元格合并到一个单元格 MemberID MemberName FruitName -------------- --------------------- -------------- 1 Al Apple 1 Al Cherry Desired output MemberID MemberName FruitName ----------- -------------- ------------ 1 Al Apple, Cherry

自定义控件:DataGridView 单元格合并和二维表头

DataGridView单元格合并和二维表头应用: //DataGridView绑定数据 DataTable dt = new DataTable(); dt.Columns.Add("1"); dt.Columns.Add("2"); dt.Columns.Add("3"); dt.Columns.Add("4"); dt.Rows.Add("中国", "上海", "5000

FastReport单元格合并

FastReport 自带的单元格合并功能为“抑制重复值”,功能真的很弱,还不如不用.网上也有不少解决方案,不过用来用去,都不尽人意. 下面是网上的两个方案: 1.报表脚本中写代码解决:单元格合并 2.修改相关源代码解决:单元格合并 最初我是两种方案结合使用,第1种方案的缺点是很明显的,有时需要写很多的代码,一不小心容易出错:第2种方案也存在问题,似乎只处理第一页的的情况,当有多页时,就乱套了. 由于本人多个软件都是使用fastreport报表控件,随着时间推移,报表越来越多,设计报表也带来了很