`
zeyuphoenix
  • 浏览: 55429 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JTable的表头合并

 
阅读更多
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="ProgId" content="Word.Document"> <meta name="Generator" content="Microsoft Word 12"> <meta name="Originator" content="Microsoft Word 12"> <link rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cphoenix%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> <link rel="Edit-Time-Data" href="file:///C:%5CDOCUME%7E1%5Cphoenix%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_editdata.mso"> <!--[if !mso]> <style> v":* {behavior:url(#default#VML);} o":* {behavior:url(#default#VML);} w":* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} </style> <![endif]--><link rel="themeData" href="file:///C:%5CDOCUME%7E1%5Cphoenix%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> <link rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5Cphoenix%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml"> <!--[if gte mso 9]><xml> Normal 0 false 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE </xml><![endif]--><!--[if gte mso 9]><![endif]--><style> <!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4;} @font-face {font-family:Calibri; panose-1:0 0 0 0 0 0 0 0 0 0; mso-font-alt:"Times New Roman";} @font-face {font-family:""@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal { mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; font-size:10.5pt; font-family:"Calibri","serif"; mso-bidi-font-family:"Times New Roman";} .MsoChpDefault { mso-bidi-font-family:"Times New Roman";} /* Page Definitions */ @page {} @page Section1 {size:595.3pt 841.9pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; layout-grid:15.6pt;} div.Section1 {page:Section1;} --> </style> <!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable { mso-style-parent:""; font-size:10.5pt; font-family:"Calibri","serif";} </style> <![endif]-->

JTableHeader的单个表头最复杂的操作也就是Renderer渲染和Editor编辑,然后增加事件处理和悬浮框提示,最多再加点特殊显示效果,这和JTable单元格的操作相同,在前面的例子里都已经讲过了,这里就剩下最后一个也是关于JTableHeader表头的操作了, 表头单元格的合并和拆分.

JTableHeader的单个表头可编辑时可以把它看做一个JTextField,不可操作时可以看做一个JLabel,对于表头的合并和拆分操作来说就是把JLabelJTextField进行合并和拆分的过程.JTable表头的合并简单来说就是把你选定的要合并的表头的边线擦掉,然后调整宽度和高度,再在这几个合并的表头外围画一个新的边线,然后设置JTableHeaderUI,刷新就可以了,JTable的单元格基本相同,唯一的区别就是JTableHeader的表头不像单元格那个容易得到和处理,我们需要定义数据结构来存储它.

先看完成后的效果:

首先是合并一行的多个单元格为一个的界面:

<!--[if gte vml 1]> <![endif]-->

再就是多行多列的合并了:

<!--[if gte vml 1]> <![endif]-->

这两个例子,实现是一样的只是因为数据结构不一样.

最后一个则是和前面单元格合并组成的例子,我们综合前面的单元格合并,结合这次的JTableHeader合并,做一个综合的合并的例子:

<!--[if gte vml 1]> <![endif]-->

然后看工程的目录:

<!--[if gte vml 1]> <![endif]-->

首先的定义一个数据结构,存储我们合并的JTableHeader.

需要理解的是,虽然看到的是一个大的单元格,但是其实它也是几个JTableHeader,只是去掉了其内的边框.所以对我们的合并的JTableHeader来说,需要定义一个数据结构来存储它们的最小分子,当然它们的Renderer也存储了,

/**

 *thetableheaderthathavecolumngroup.

*/

publicclass ColumnGroup {

看它的属性:

    /**headerrenderer.*/

    private TableCellRenderer renderer = null;

这个是合并的JTableHeaderRenderer,这里我们简单化,我们就不像前面写RendererEditor那样分开存储了,我们假设这个JTableHeader使用同一类的Renderer,如果你想实现不一样的Renderer,你可以把它们定义成数组(PS:这样效果会比较怪异,一个合并的单元格包含了几个组件).

    /**theheadergroup.*/

    private Vector<Object> vector = null;

这个是合并的单元格的各个实际的最小单元格存储结构.

    /**headervalue.*/

    private String text = null;

这个是合并后单元格显示的文本信息

    /**thehiddenborderwidth*/

    privateintmargin = 0;

这个是合并的单元格内部两个最小JTableHeader的间隙,其实就是去掉线后那个Border.

除了提供各个属性的GetSet方法外,还提供了增加单元格add

    /**

     *addheaderorgrouptocolumngroup.

     */

    publicvoid add(Object obj) {

       if (obj == null) {

           return;

       }

       v.addElement(obj);

    }

取得合并后的单元格的大小getSize:

    /**

     *getheadergroupsize.

     */

    public Dimension getSize(JTable table) {

这个方法需要计算,首先是取得一个没有合并的最小单元格的JTableHeader的大小,通过Renderer取得组件:

       Component comp = renderer.getTableCellRendererComponent(table,

              getHeaderValue(), false, false, -1, -1);

       int height = comp.getPreferredSize().height;

宽度需要计算合并的还要加上间隙:

Enumeration<Object> enumTemp = v.elements();

       while (enumTemp.hasMoreElements()) {

              TableColumn aColumn = (TableColumn) obj;

              width += aColumn.getWidth();

              width += margin;

最后取得这个合并的JTableHeader的大小:

       returnnew Dimension(width, height);

还有一个比较重要的方法是根据JTable的某一列取得它的所有的包含列,这个主要用于绘制:

    public Vector<ColumnGroup> getColumnGroups(TableColumn column,

           Vector<ColumnGroup> group) {

通过递归判断列到底属于那个ColumnGroup

    group.addElement(this);

       if (v.contains(column)) {

           return group;

       }

没有找的则是Null.

然后我们看的类是MyTableColumn,它继承于TableColumn,主要是JTableHeader的编辑属性和Editor.

publicclass MyTableColumn extends TableColumn {

主要属性:

    /**tableheadereditor.*/

    private TableCellEditor headerEditor;

    /**isheadereditable.*/

    privatebooleanisHeaderEditable;

方法只是简单的GetSet设置,设置了默认编辑与否和默认的JTableHeaderEditor.

其实也是一个简单的类,主要是描述JTableHeaderRenderer,这个在之前我们就介绍了,大概写下:

publicclass MyHeaderRenderer extends JLabel implements

        TableCellRenderer {

实现默认的方法:

@Override

public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

首先是取得JTableHeader:

       // set some color font about header.

       JTableHeader header = table.getTableHeader();

设置属性:

       setForeground(fgColor);

       setBackground(bgColor);

setFont(header.getFont());

还有设置高度,和前面的那个Renderer专题一样:

       setPreferredSize(new Dimension(getWidth(), headerHeight));

最后就是两个最重要的类,JTableHeader和它的UI的绘制:

先看UI,我们继承于BasicTableHeaderUI:

/**

 *BasicTableHeaderUIimplementation.

*/

publicclass MyGroupTableHeaderUI extends BasicTableHeaderUI {

它重写BasicTableHeaderUIpaint方法进行自己的UI绘制,

    /**

     *Paintarepresentationofthetableinstancethatwasset

     *ininstallUI().

     */

    @Override

    publicvoid paint(Graphics g, JComponent c) {

首先取得旧的绘制边框:

       Rectangle oldClipBounds = g.getClipBounds();

       Rectangle clipBounds = new Rectangle(oldClipBounds);

然后根据JTable的宽度比较决定绘制的宽度,

       int tableWidth = table.getColumnModel().getTotalColumnWidth();

       clipBounds.width = Math.min(clipBounds.width, tableWidth);

       g.setClip(clipBounds);

取得行的边框

       ((MyTableHeader) header).setColumnMargin();

       Dimension size = header.getSize();

然后开始绘制行:

先去的需要绘制的合并JTableHeader的属性:

       Enumeration<?> cGroups = ((MyTableHeader) header)

                  .getColumnGroups(aColumn);

然后算出本行内那几个列需要合并:

       ColumnGroup cGroup = (ColumnGroup) cGroups.nextElement();

                  Rectangle groupRect = (Rectangle) h.get(cGroup);

                  if (groupRect == null) {

                     icount ++;

                     groupRect = new Rectangle(cellRect);

                     Dimension d = cGroup.getSize(header.getTable());

                     groupRect.width = d.width - icount;

                     groupRect.height = d.height;

                     h.put(cGroup, groupRect);

                  }

最后就是绘制具体的单元格了:

       Color c = g.getColor();

       g.setColor(table.getGridColor());

       g.drawRect(cellRect.x, cellRect.y, cellRect.width - 1,

              cellRect.height - 1);

       g.setColor(c);

       cellRect.setBounds(cellRect.x + spacingWidth / 2, cellRect.y

              + spacingHeight / 2, cellRect.width - spacingWidth,

              cellRect.height - spacingHeight);

不仅如此还需要控制编辑状态和普通状态有Renderer的显示问题:

       TableCellRenderer renderer = cGroup.getHeaderRenderer();

      Component component = renderer.getTableCellRendererComponent(header

              .getTable(), cGroup.getHeaderValue(), false, false, -1, -1);

       rendererPane.add(component);

       rendererPane.paintComponent(g, component, header, cellRect.x,

              cellRect.y, cellRect.width, cellRect.height, true);
到这里JTableHeader合并后的显示绘制就完成了,然后就是它的大小的显示:

    @Override

    public Dimension getPreferredSize(JComponent c) {

       long width = 0;

       Enumeration<?> enumeration = header.getColumnModel().getColumns();

       while (enumeration.hasMoreElements()) {

           TableColumn aColumn = (TableColumn) enumeration.nextElement();

           width = width + aColumn.getPreferredWidth();

       }

       return createHeaderSize(width);

    }

JTableHeader和单元格合并不同的一点是它的事件比较复杂,我们需要重写写它的MouseInputHandler

/**

   *Thisinnerclassismarked&quot;public&quot;duetoacompilerbug. * Thisclassshouldbetreatedasa&quot;protected&quot;innerclass.

    *InstantiateitonlywithinsubclassesofBasicTableUI.

    */

privateclass MouseInputHandler extends

           BasicTableHeaderUI.MouseInputHandler {

它提供一个属性,

       /**theshowcomponent.*/

       private Component dispatchComponent = null;

然后在鼠标事件上加上我们自己的处理:

       @Override

       publicvoid mousePressed(MouseEvent e) {

根据鼠标的位置取得我们合并后绘制的单元格:

           Point p = e.getPoint();

           TableColumnModel columnModel = header.getColumnModel();

           int index = columnModel.getColumnIndexAtX(p.x);

           if (index != -1) {

              if (header.editCellAt(index, e)) {

                  setDispatchComponent(e);

设置完我们的显示后,再把事件下发:

           MouseEvent e2 = SwingUtilities.convertMouseEvent(header, e,

                  dispatchComponent);

           dispatchComponent.dispatchEvent(e2);

其它的鼠标事件也一样处理,添加我们自己的显示.

然后就是最重要的JTableHeader,我们重写它:

publicclass MyTableHeader extends JTableHeader implements CellEditorListener {

重写updateUI方法,设置我们自己的UI:

    @Override

    publicvoid updateUI() {

       setUI(new MyGroupTableHeaderUI());

       resizeAndRepaint();

       invalidate();

    }

同时提供setColumnMarginaddColumnGroupsetCellEditor等方法设置JTableHeader的属性和Editor.

然后就是设置JTableHeader编辑状态和编辑后状态的设置了:

首先根据鼠标事件取得是否是需要编辑的JTableHeader.

    publicboolean editCellAt(int index, EventObject e) {

在可编辑的JTableHeader的单元格增加事件:

TableCellEditor editor = getCellEditor(index);

       if (editor != null && editor.isCellEditable(e)) {

           editorComp = prepareEditor(editor, index);

           editorComp.setBounds(getHeaderRect(index));

           add(editorComp);

           editorComp.validate();

           setCellEditor(editor);

           setEditingColumn(index);

           editor.addCellEditorListener(this);

           returntrue;

       }

重写editingStopped方法和editingCanceled方法.编辑状态停止后设置显示:

    @Override

    publicvoid editingStopped(ChangeEvent e) {

       TableCellEditor editor = getCellEditor();

       if (editor != null) {

           Object value = editor.getCellEditorValue();

           int index = getEditingColumn();

           columnModel.getColumn(index).setHeaderValue(value);

           removeEditor();

       }

    }

    @Override

    publicvoid editingCanceled(ChangeEvent e) {

       removeEditor();

    }

最后就是使用了,虽然JTableHeader设置已经很麻烦了,但是设置显示数据更麻烦,因此不到万不得已不要用了,它基本不支持,基本不可能通过后期数据生成,一般用也是做成报表之类,但报表的JasperReport做的更好,所以虽然在这里写,只是算一个思路.

使用首先要设置JTableHeader,构造JTableHeader的数据结构:

@Override

    protected JTableHeader createDefaultTableHeader() {

        returnnew MyTableHeader(columnModel);

}

TableColumnModel cm = table.getColumnModel();

    ColumnGroup g_name = new ColumnGroup("Name");

    g_name.add(cm.getColumn(1));

取得JTableHeader,设置合并的数据结构:

    MyTableHeader header = (MyTableHeader) table

       .getTableHeader();

header.addColumnGroup(g_name);

然后设置RendererUI

    TableCellRenderer renderer = new MyHeaderRenderer();

    model.getColumn(i).setHeaderRenderer(renderer);

table.getTableHeader().setUI(new MyGroupTableHeaderUI());

其它的就和一个普通的JTable一样了.

我们可以综合单元格合并和JTableHeader合并,这样就可以作出复杂的JTable,如上面最后那张图.


分享到:
评论

相关推荐

    JTable表头合并

    JTable表头合并, 很方便使用,swing的控件,下载下来,直接导入工程就可以使用了,接口封装的很好。想要什么样的表头都可以满足你

    复杂JTable-跨列表头

    复杂JTable-跨列表头, JTable表头合并,已经测试Ok,希望对你有用

    netbeans jtable 复杂表头的实现方法

    jtable swing jtableheader netbeans 复杂表头 表头合并 使用netbeans打开后可以打开JFrame

    JTable实例大全

    里面包含有很全的JTable实例,如合并表头,合并表格,等等,已经打包成JAR

    JTable Examples

    网上的JTable的例子,共25个,已全部调试通过,包括JTable多级表头,多级行头,合并单元格等

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    实例045 用数组设置JTable表格的列名与列宽 55 3.2 数组操作 57 实例046 数组的下标界限 57 实例047 按钮控件数组实现计数器界面 58 实例048 复选框控件数组 59 实例049 用数组反转字符串 60 3.3 数组排序与查询 61 ...

Global site tag (gtag.js) - Google Analytics