尝试创建带有正确行标题的JTable

14

我正在尝试创建一个带有行头的JTable,它看起来就像一个列头,但我已经花费了太多时间:/
我的情况类似于这个问题:JTable Row Header Implementation
也可能是这个问题:customizing jtable cellrenderer with table's cell header color
它们似乎还没有完全解决我的问题。我已经搜索并尝试了许多示例,但都不够完美。甚至Oracle/Sun公司也没有任何关于带有行头的表格示例。这种类型的表格似乎不应该那么罕见。
其中一个示例只是格式化了第一列,但它看起来并不像列头:http://java-swing-tips.blogspot.com/2011/01/jtable-rowheader.html
另一个示例似乎使用JList作为行头,并且存在对齐问题(每行+1像素偏移),当我设置外观时也不好看。http://www.java2s.com/Code/Java/Swing-Components/TableRowHeaderExample.htm
这个示例看起来像我需要使用的思路(行头是单独的JTable,然后作为行头加载到JScrollPane中),但我需要将其适应我的代码,并确保行头获得正确的头部外观和风格。
http://www.chka.de/swing/table/row-headers/JTable.html
除了最后一点之外,我已经这样做了。我尝试让表头的渲染器也成为行头的渲染器。现在,行头/第一列是灰色的,而不是当它只是另一列数据时的白色,但仍然不像列头。这是正确的吗?还是我应该将其保留为主表中的常规列并对其进行其他处理?
下面是我更新表格的代码。此方法只采用一个字符串数组作为列标题,一个字符串数组作为行标题和一个2D字符串数组作为主数据。我有一个用于行头的JTable dispTableRowHeader和一个用于主数据表的JTable dispTable

    private void updateDispTable(String[][] graphicalTable, String[] graphicalTableColumnHeader, String[] graphicalTableRowHeader) {

    //set model for the main data table, put in data. Also prevent editing cells
    dispTable.setModel(new javax.swing.table.DefaultTableModel(
        graphicalTable,
        graphicalTableColumnHeader
    ){
        @Override
        public boolean isCellEditable(int rowIndex, int mColIndex) {
            return false;
        }
    });

    //some mods for main data table
    dispTable.getTableHeader().setReorderingAllowed(false);//Was also able to do this within NetBeans GUI Builder by doing Table Contents from Jtable inspector item
    dispTable.getTableHeader().setResizingAllowed(false);

    //load main table to scrollpane
    jScrollPane2.setViewportView(dispTable);

    //get model for JTable that will be used as the row header, fill in values
    DefaultTableModel rowHeaderTableModel = new DefaultTableModel(0, 1);//one column
    for (int i = 0; i < graphicalTable.length; i++)
        rowHeaderTableModel.addRow(new Object[] { graphicalTableRowHeader[i] } );

    //set model for row header, put in data. Alter renderer to make it like col header
    dispTableRowHeader.setModel(rowHeaderTableModel);
    dispTableRowHeader.setDefaultRenderer(Object.class, dispTableRowHeader.getTableHeader().getDefaultRenderer());//makes it gray but not like the header :/
    //dispTableRowHeader.setDefaultRenderer(Object.class, jScrollPane2.getColumnHeader().getDefaultRenderer());

    //load row header to scrollpane's row header
    jScrollPane2.setRowHeaderView(dispTableRowHeader);

    //set the table corner and disallow reordering and resizing
    JTableHeader corner = dispTableRowHeader.getTableHeader();
    corner.setReorderingAllowed(false);
    corner.setResizingAllowed(false);
    jScrollPane2.setCorner(JScrollPane.UPPER_LEFT_CORNER, corner);//load to scrollpane
}

非常感谢任何帮助!

编辑以下内容 我创建了一个全新的项目进行实验,并尝试了trashgod的方法(虽然是针对行标题而不是第二行),我发现它给出了与只将单元格样式设置为灰色相同的结果,而不是像标题一样进行样式设置。然后我尝试删除我的外观调整。

        // Set System L&F
        UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );

然后行头看起来和列头一样!但那是在Nimbus或其他默认外观下。请参见以下图像。第一张是使用系统的外观(在Win7中),第二张是默认设置。

系统外观(Win7)- 我调整了外观,设置为系统外观

Java的Nimbus外观- 默认外观

果然,我的程序也发生了同样的情况。所以现在似乎我的问题出在外观上。我想让它看起来像第一张图片(系统的外观),但左边也要样式化。

顺便说一句,在Win7中有一个MySQL Workbench的示例,它具有适当样式的行和列标题,并且它们甚至在鼠标悬停时都会给单元格带来蓝色阴影。太糟糕了它不是用Java制作的,否则我可以尝试检查他们是如何做到的。

MySQL Workbench截图- MySQL Workbench截图

以下是SSCCE代码

package mytableexample2;
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;

public class MyTableExample2 extends javax.swing.JFrame {

    public MyTableExample2() {
        initComponents();
    }

    @SuppressWarnings("unchecked")
    private void initComponents() {
        jScrollPane1 = new javax.swing.JScrollPane();
        jTable1 = new javax.swing.JTable(){
            @Override
            public Component prepareRenderer(
                    TableCellRenderer renderer, int row, int col) {
                if (col == 0) {
                    return this.getTableHeader().getDefaultRenderer()
                        .getTableCellRendererComponent(this,
                        this.getValueAt(row, col), false, false, row, col);
                } else {
                    return super.prepareRenderer(renderer, row, col);
                }
            }
        };
        jTable1.setAutoCreateRowSorter(false);
        final JTableHeader header = jTable1.getTableHeader();
        header.setDefaultRenderer(new HeaderRenderer(jTable1));

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jTable1.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {"Row 1", "Data 2", "Data 3", "Data 4", "Data 5"},
                {"Row 2", "Data 6", "Data 7", "Data 8", "Data 9"},
                {"Row 3", "Data 10", "Data 11", "Data 12", "Data 13"}
            },
            new String [] {
                "", "Col 1", "Col 2", "Col 3", "Col 4"
            }
        ));
        jScrollPane1.setViewportView(jTable1);


        //Netbeans generated layout
        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 278, Short.MAX_VALUE)
                .addContainerGap())
        );
        pack();
    }


    public static void main(String args[]) {
        try {
            //THIS SETS TO SYSTEM'S LOOK AND FEEL
            UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );

            //THIS SETS TO OTHER JAVA LOOK AND FEEL
            //UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
            //UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");

        } catch (ClassNotFoundException ex) {
                java.util.logging.Logger.getLogger(MyTableExample2.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(MyTableExample2.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(MyTableExample2.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(MyTableExample2.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MyTableExample2().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTable jTable1;
}

class HeaderRenderer implements TableCellRenderer {
TableCellRenderer renderer;
    public HeaderRenderer(JTable jTable1) {
        renderer = jTable1.getTableHeader().getDefaultRenderer();
    }
    @Override
    public Component getTableCellRendererComponent(
        JTable table, Object value, boolean isSelected,
        boolean hasFocus, int row, int col) {
        return renderer.getTableCellRendererComponent(
            table, value, isSelected, hasFocus, row, col);
    }
}

我也尝试了这里的方法[链接](https://dev59.com/_FnUa4cB1Zd3GeqPXRgv),结果相同。 - Michael K
请提供 sscce,以生成 第一张图片 - trashgod
我添加了MySQL Workbench的截图,展示了一个带有顶部和左侧的表格,就像我想要的那样。但它不是用Java编写的。 - Michael K
2个回答

5
使用此 HeaderRenderer 作为第一行的列渲染器可能会产生您想要的效果:

补充:我已更新示例以反映您具有手动布局的sscce。 我平台的 getSystemLookAndFeelClassName() com.apple.laf.AquaLookAndFeel ,因此我没有看到相同的结果。 两个观察结果:您已经 setAutoCreateRowSorter(false)以防止排序小部件增殖,而Nimbus保留其交替行高亮显示。

enter image description here

import java.awt.Component;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;

public class MyTableExample3 extends JFrame {

    private JScrollPane scrollPane;
    private JTable table;

    public MyTableExample3() {
        initComponents();
    }

    @SuppressWarnings("unchecked")
    private void initComponents() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        table = new javax.swing.JTable() {

            @Override
            public Component prepareRenderer(
                TableCellRenderer renderer, int row, int col) {
                if (col == 0) {
                    return this.getTableHeader().getDefaultRenderer()
                        .getTableCellRendererComponent(this, this.getValueAt(
                            row, col), false, false, row, col);
                } else {
                    return super.prepareRenderer(renderer, row, col);
                }
            }
        };
        table.setAutoCreateRowSorter(false);
        final JTableHeader header = table.getTableHeader();
        header.setDefaultRenderer(new HeaderRenderer(table));

        table.setModel(new javax.swing.table.DefaultTableModel(
            new Object[][]{
                {"Row 1", "Data 2", "Data 3", "Data 4", "Data 5"},
                {"Row 2", "Data 6", "Data 7", "Data 8", "Data 9"},
                {"Row 3", "Data 10", "Data 11", "Data 12", "Data 13"}
            },
            new String[]{
                "", "Col 1", "Col 2", "Col 3", "Col 4"
            }));
        scrollPane = new JScrollPane(table);
        this.add(scrollPane);
        pack();
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new MyTableExample3().setVisible(true);
            }
        });
    }

    private static class HeaderRenderer implements TableCellRenderer {

        TableCellRenderer renderer;

        public HeaderRenderer(JTable table) {
            renderer = table.getTableHeader().getDefaultRenderer();
        }

        @Override
        public Component getTableCellRendererComponent(
            JTable table, Object value, boolean isSelected,
            boolean hasFocus, int row, int col) {
            return renderer.getTableCellRendererComponent(
                table, value, isSelected, hasFocus, row, col);
        }
    }
}

你误解了我的意思 - 我实际上想要第一列作为表头,而不是两行 - 就像 MS Excel 或其他任何带有顶部横向表头和左侧纵向表头的表格。但无论如何,我按照你的方式构建了一个示例,得到了相同的结果(只是灰色单元格而已,而不是像表头那样进行样式设置)。然后我删除了我的外观调整,发现两个表头实际上是相同的!这很好。唯一的区别是当鼠标悬停在行表头上时,它们没有以任何方式突出显示,就像列表头那样。我正在发布带有图像的问题编辑。 - Michael K
已更新。遗憾的是,我无法在Mac OS X上复制该效果。 - trashgod
好的,我在 OS X 上看到左侧有漂亮的标题单元格。在 Java Look and Feels 可以工作但 System (Win7) 不能工作后,我就想可能是这种情况。 - Michael K
我注意到即使在Nimbus L&F上,左侧和顶部看起来一致,左侧标题单元格在鼠标悬停时不会像顶部标题单元格那样突出或阴影。这对你有用吗?顺便说一句,你的图像上有很好的阴影 :) - Michael K
在Mac上使用Nimbus,在列0中使用标题渲染器会导致交替行被突出显示,就像其他行一样。我不能对阴影做任何贡献;它是screenshot机制的一个功能。 - trashgod

4
也许

这里输入图片描述

import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.UIManager.*;
import javax.swing.event.*;
import javax.swing.table.*;

public class JTableRowHeader {

    private JFrame frame = new JFrame("JTable RowHeader");
    private JScrollPane scrollPane;
    private JTable table;
    private DefaultTableModel model;
    private TableRowSorter<TableModel> sorter;
    private JTable headerTable;

    public JTableRowHeader() {
        table = new JTable(4, 4);
        for (int i = 0; i < table.getRowCount(); i++) {
            table.setValueAt(i, i, 0);
        }
        sorter = new TableRowSorter<TableModel>(table.getModel());
        table.setRowSorter(sorter);
        model = new DefaultTableModel() {

            private static final long serialVersionUID = 1L;

            @Override
            public int getColumnCount() {
                return 1;
            }

            @Override
            public boolean isCellEditable(int row, int col) {
                return false;
            }

            @Override
            public int getRowCount() {
                return table.getRowCount();
            }

            @Override
            public Class<?> getColumnClass(int colNum) {
                switch (colNum) {
                    case 0:
                        return String.class;
                    default:
                        return super.getColumnClass(colNum);
                }
            }
        };
        headerTable = new JTable(model);
        for (int i = 0; i < table.getRowCount(); i++) {
            headerTable.setValueAt("Row " + (i + 1), i, 0);
        }
        headerTable.setShowGrid(false);
        headerTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        headerTable.setPreferredScrollableViewportSize(new Dimension(50, 0));
        headerTable.getColumnModel().getColumn(0).setPreferredWidth(50);
        headerTable.getColumnModel().getColumn(0).setCellRenderer(new TableCellRenderer() {

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

                boolean selected = table.getSelectionModel().isSelectedIndex(row);
                Component component = table.getTableHeader().getDefaultRenderer().getTableCellRendererComponent(table, value, false, false, -1, -2);
                ((JLabel) component).setHorizontalAlignment(SwingConstants.CENTER);
                if (selected) {
                    component.setFont(component.getFont().deriveFont(Font.BOLD));
                    component.setForeground(Color.red);
                } else {
                    component.setFont(component.getFont().deriveFont(Font.PLAIN));
                }
                return component;
            }
        });
        table.getRowSorter().addRowSorterListener(new RowSorterListener() {

            @Override
            public void sorterChanged(RowSorterEvent e) {
                model.fireTableDataChanged();
            }
        });
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {

            @Override
            public void valueChanged(ListSelectionEvent e) {
                model.fireTableRowsUpdated(0, model.getRowCount() - 1);
            }
        });
        scrollPane = new JScrollPane(table);
        scrollPane.setRowHeaderView(headerTable);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(scrollPane);
        frame.add(new JButton(new AbstractAction("Toggle filter") {

            private static final long serialVersionUID = 1L;
            private RowFilter<TableModel, Object> filter = new RowFilter<TableModel, Object>() {

                @Override
                public boolean include(javax.swing.RowFilter.Entry<? extends TableModel, ? extends Object> entry) {
                    return ((Number) entry.getValue(0)).intValue() % 2 == 0;
                }
            };

            @Override
            public void actionPerformed(ActionEvent e) {
                if (sorter.getRowFilter() != null) {
                    sorter.setRowFilter(null);
                } else {
                    sorter.setRowFilter(filter);
                }
            }
        }), BorderLayout.SOUTH);
        frame.pack();
        frame.setLocation(150, 150);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        try {// UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if (info.getName().equals("Nimbus")) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (Exception e) {
            //e.printStackTrace();
        }
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JTableRowHeader TestTableRowHeader = new JTableRowHeader();
            }
        });
    }
}

这是使用系统外观和感觉的样子- 在此输入图片描述

+1 不错的 RowSorter。由于某种原因,JLabel.CENTER 直到我将鼠标悬停在列标题上才生效。 - trashgod
改为(SwingConstants.CENTER) :-) - mKorbel
不,这不是常量;我认为这与 com.apple.laf.AquaTableHeaderUI 有些奇怪的问题。 - trashgod
抱歉,我不知道如何解决这个问题(我的错,我是AS xxx和Windows用户)。对于Mac OS X,请问使用UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());是否存在相同的问题? - mKorbel
感谢您的帖子!我加载了它,发现它与我的功能完全相同(我在您的帖子中添加了图像)。当我使用系统外观时,左侧的单元格只是普通的灰色,没有任何阴影。 - Michael K
@mKorbel:有趣;getSystemLookAndFeelClassName()返回com.apple.laf.AquaLookAndFeel。抱歉我没有早些回复。 - trashgod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接