【问题标题】:JTable horizontal scrollbar based on width of one column基于一列宽度的JTable水平滚动条
【发布时间】:2013-02-22 00:40:28
【问题描述】:

我遇到了一个之前在这里讨论过的问题:得到一个包含JTableJScrollPane 来显示我想要的水平滚动条。 HERE 是我尝试关注的帖子,但由于某种原因,它似乎不适用于我的情况(我认为这是因为我已将其中一个列设置为具有固定宽度。

这是一个 SSCCE:

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

@SuppressWarnings("serial")
public class TableScrollTest extends JFrame
{
    public TableScrollTest() {

        DefaultTableModel model = new DefaultTableModel(new Object[]{"key", "value"},0);
        model.addRow(new Object[]{"short", "blah"});
        model.addRow(new Object[]{"long", "blah blah blah blah blah blah blah"});

        JTable table = new JTable(model) {
            public boolean getScrollableTracksViewportWidth() {
                return getPreferredSize().width < getParent().getWidth();
            }
        };        
        table.getColumn("key").setPreferredWidth(60);
        table.getColumn("key").setMinWidth(60);
        table.getColumn("key").setMaxWidth(60);
        table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );

        JScrollPane scrollPane = new JScrollPane( table );
        getContentPane().add( scrollPane );
    }

    public static void main(String[] args) {
        TableScrollTest frame = new TableScrollTest();
        frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
        frame.pack();
        frame.setSize(200, 200);
        frame.setResizable(false);
        frame.setVisible(true);
    }
}

简而言之,我有一个用于显示键/值对的两列表。存放表格的容器是固定宽度的,表格的第一列也是固定宽度的(因为我知道所有键名的长度)。第二列将包含不同字符串长度的值。仅当存在的值太长而无法适应为列分配的宽度时,才会出现水平滚动条。

因为上面的第二个值有这么长的长度,滚动条应该是可见的。然而,事实并非如此,我所尝试的一切都只是成功地让它始终可见,这不是我想要的......我只希望它在存在“长”值的情况下可见。似乎getScrollableTracksViewportWidth() 方法在表构造函数中被覆盖检查表的首选宽度是什么......所以我需要以某种方式指示表只根据第二列的内容选择更大的宽度.. . 但我很难过。

有什么想法吗?

【问题讨论】:

  • 你的直接问题是因为你已经覆盖了getScrollableTracksViewportWidth,这意味着表格永远不会比滚动窗格更宽......
  • @MadProgrammer 你确定吗?我链接的示例覆盖了该方法,在该示例中,表格确实比滚动窗格更宽。
  • 但是table的preferredWidth是基于TableColumn的宽度,没有考虑到内容的宽度,也就是说除非你把第二列的宽度改成一样您更改第一个的方式,它将始终返回true。我删除了getScrollableTracksViewportWidth 方法并手动调整了列的大小,并且能够显示水平滚动条。
  • @MadProgrammer 好的,我明白你在说什么。看来我的目标是强制单元格渲染器自动使单元格适合长字符串,所以我不必像上面那样手动调整它的大小。

标签: java swing jtable jscrollpane tablecolumn


【解决方案1】:

这是一个骇人听闻的解决方案

基本上,它的作用是根据所有行的值计算所有列的“首选”宽度。

它考虑了模型的变化以及父容器的变化。

完成后,它会检查“首选”宽度是大于还是小于可用空间,并相应地设置trackViewportWidth 变量。

您可以添加对固定列的检查(我没有打扰),这会使该过程“稍微”快一些,但是随着每次更新的进行,当您向表中添加更多列和行时,这会受到影响需要遍历整个模型。

您可以放入某种缓存,但就在那时,我会考虑固定宽度的列;)

import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

public class TableScrollTest extends JFrame {

    public TableScrollTest() {

        DefaultTableModel model = new DefaultTableModel(new Object[]{"key", "value"}, 0);
        model.addRow(new Object[]{"short", "blah"});
        model.addRow(new Object[]{"long", "blah blah blah blah blah blah blah"});

        JTable table = new JTable(model) {
            private boolean trackViewportWidth = false;
            private boolean inited = false;
            private boolean ignoreUpdates = false;

            @Override
            protected void initializeLocalVars() {
                super.initializeLocalVars();
                inited = true;
                updateColumnWidth();
            }

            @Override
            public void addNotify() {
                super.addNotify();
                updateColumnWidth();
                getParent().addComponentListener(new ComponentAdapter() {
                    @Override
                    public void componentResized(ComponentEvent e) {
                        invalidate();
                    }
                });
            }

            @Override
            public void doLayout() {
                super.doLayout();
                if (!ignoreUpdates) {
                    updateColumnWidth();
                }
                ignoreUpdates = false;
            }

            protected void updateColumnWidth() {
                if (getParent() != null) {
                    int width = 0;
                    for (int col = 0; col < getColumnCount(); col++) {
                        int colWidth = 0;
                        for (int row = 0; row < getRowCount(); row++) {
                            int prefWidth = getCellRenderer(row, col).
                                    getTableCellRendererComponent(this, getValueAt(row, col), false, false, row, col).
                                    getPreferredSize().width;
                            colWidth = Math.max(colWidth, prefWidth + getIntercellSpacing().width);
                        }

                        TableColumn tc = getColumnModel().getColumn(convertColumnIndexToModel(col));
                        tc.setPreferredWidth(colWidth);
                        width += colWidth;
                    }

                    Container parent = getParent();
                    if (parent instanceof JViewport) {
                        parent = parent.getParent();
                    }

                    trackViewportWidth = width < parent.getWidth();
                }
            }

            @Override
            public void tableChanged(TableModelEvent e) {
                super.tableChanged(e);
                if (inited) {
                    updateColumnWidth();
                }
            }

            public boolean getScrollableTracksViewportWidth() {
                return trackViewportWidth;
            }

            @Override
            protected TableColumnModel createDefaultColumnModel() {
                TableColumnModel model = super.createDefaultColumnModel();
                model.addColumnModelListener(new TableColumnModelListener() {
                    @Override
                    public void columnAdded(TableColumnModelEvent e) {
                    }

                    @Override
                    public void columnRemoved(TableColumnModelEvent e) {
                    }

                    @Override
                    public void columnMoved(TableColumnModelEvent e) {
                        if (!ignoreUpdates) {
                            ignoreUpdates = true;
                            updateColumnWidth();
                        }
                    }

                    @Override
                    public void columnMarginChanged(ChangeEvent e) {
                        if (!ignoreUpdates) {
                            ignoreUpdates = true;
                            updateColumnWidth();
                        }
                    }

                    @Override
                    public void columnSelectionChanged(ListSelectionEvent e) {
                    }
                });
                return model;
            }
        };
        table.getColumn("key").setPreferredWidth(60);
//        table.getColumn("key").setMinWidth(60);
//        table.getColumn("key").setMaxWidth(60);
//        table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);

        JScrollPane scrollPane = new JScrollPane(table);
        getContentPane().add(scrollPane);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                TableScrollTest frame = new TableScrollTest();
                frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
                frame.pack();
                frame.setSize(200, 200);
                frame.setResizable(true);
                frame.setVisible(true);
            }
        });
    }
}

【讨论】:

  • 谢谢!这与我在上面想出的非常相似,但我只是因为你的 cmets 和我链接到的@kleopatra 的帖子才想出我的。但是,您的解决方案中有一个小错误......您的宽度会累积每行的宽度值......所以您添加的行越多,您在每行末尾获得的“死”空间就越多。我想你想像我一样使用 max 函数。但是我仍然必须添加我在回答中评论过的 1-5 像素软糖因子。为您的帮助点赞。 :-)
  • 也将接受这个答案,因为对addNotifytableChanged 等的覆盖很聪明,并且全部自动化。我计划每次我的表更改时手动调用一些 update 方法,但是像你所做的那样将它包含在表定义中要好得多。
  • @The111 修复了错误 - 还添加了 intercellSpacing 因子。好地方!
【解决方案2】:

看来我的目标是强制单元格渲染器自动使单元格适合长字符串

这不是渲染器的工作。您必须手动设置列的宽度。

请参阅Table Column Adjuster 了解一种方法。

【讨论】:

    【解决方案3】:

    如果发布了更聪明的东西,我不一定会接受这个答案,但这是我自己根据迄今为止发布的 cmets 和 THIS 发布的解决方案。唯一的问题是我想出的宽度总是约 1 像素太短(在某些外观和感觉上还可以),所以我在接近结尾处添加了行 width += 5 这似乎使它工作好的,但对我来说感觉很hacky。欢迎任何关于这种方法的 cmets。

    import java.awt.Component;
    
    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
    import javax.swing.JTable;
    import javax.swing.table.DefaultTableModel;
    import javax.swing.table.TableCellRenderer;
    
    @SuppressWarnings("serial")
    public class TableScrollTest extends JFrame {
        public TableScrollTest() {
            DefaultTableModel model = new DefaultTableModel(new Object[]{"key", "value"},0);
            model.addRow(new Object[]{"short", "blah"});
            model.addRow(new Object[]{"long", "blah blah blah blah blah blah blah"});
    
            JTable table = new JTable(model);        
            table.getColumn("key").setPreferredWidth(60);
            table.getColumn("key").setMinWidth(60);
            table.getColumn("key").setMaxWidth(60);
            table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
    
            int width = 0;
            for (int row = 0; row < table.getRowCount(); row++) {
                TableCellRenderer renderer = table.getCellRenderer(row, 1);
                Component comp = table.prepareRenderer(renderer, row, 1);
                width = Math.max (comp.getPreferredSize().width, width);
            }
            width += 5;
            table.getColumn("value").setPreferredWidth(width);
            table.getColumn("value").setMinWidth(width);
            table.getColumn("value").setMaxWidth(width);
    
            JScrollPane scrollPane = new JScrollPane( table );
            getContentPane().add( scrollPane );
        }
    
        public static void main(String[] args) {
            TableScrollTest frame = new TableScrollTest();
            frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
            frame.pack();
            frame.setSize(200, 200);
            frame.setResizable(false);
            frame.setVisible(true);
        }
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-25
    • 2013-07-06
    • 1970-01-01
    • 2011-01-28
    • 2012-04-05
    • 2014-12-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多