【问题标题】:How can I freeze a JTable row selection inside a scroll pane如何冻结滚动窗格内的 JTable 行选择
【发布时间】:2012-05-14 03:02:29
【问题描述】:

我有一个经常更新的 JTable。它位于滚动窗格内。

有时我们会看到一条我们想深入研究的消息。不幸的是,由于更新的数量,消息将在我们完成查看之前从可见屏幕滚动出来。

我希望能够在我们检查消息时冻结视口/滚动,但仍允许表格模型使用新消息进行更新。

我可以使用以下代码将所选行移至顶部:

        int firstSelectedRow = table.getSelectedRow();
        Rectangle rowLocation = table.getCellRect(firstSelectedRow, 0, false);
        scroll.getVerticalScrollBar().setValue(rowLocation.y);
        freezeScrolling.setText("Resume Updates");

但这只会在按下按钮时发生,然后它会快速滚动。

有没有办法告诉视口/滚动窗格冻结选择并关闭它,以便滚动窗格按预期更新?

【问题讨论】:

    标签: java swing jtable jscrollpane


    【解决方案1】:

    好的,这个很取笑我,所以我花了一些时间来寻找解决这个问题的方法。我找到了两个选择:

    1. 我更喜欢一个,因为我发现它更简单:阻止表模型的新条目并将它们存储在缓冲区中。每当我们解冻时,我们都会将缓冲区刷新到表模型中。
    2. 第二个选项包括定位当前顶部可见行。如果用户滚动,我们捕获顶部可见行。随着模型的更新,我们在表格中定位捕获的行,并将视口位置设置为该位置。

    这里是一个 SSCCE,说明了这两种机制(不要介意代码设计的蹩脚,我测试了一堆东西——扩展 JScrollPane 实际上不是一个好主意,它都可以在外部完成)。

    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.AdjustmentEvent;
    import java.awt.event.AdjustmentListener;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.Timer;
    import java.util.TimerTask;
    import java.util.Vector;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTable;
    import javax.swing.SwingUtilities;
    import javax.swing.event.TableModelEvent;
    import javax.swing.event.TableModelListener;
    import javax.swing.table.DefaultTableModel;
    
    public class Tables {
    
        private static class JScrollPaneExtension extends JScrollPane implements TableModelListener, AdjustmentListener {
            private boolean frozen = false;
            private Vector<String> rowData;
            private final JTable table;
            private final MyTableModel tableModel;
    
            private JScrollPaneExtension(JTable table, MyTableModel tableModel) {
                super(table);
                this.table = table;
                this.tableModel = tableModel;
                tableModel.addTableModelListener(this);
                getVerticalScrollBar().addAdjustmentListener(this);
            }
    
            public boolean isFrozen() {
                return frozen;
            }
    
            public void setFrozen(boolean frozen) {
                if (frozen != this.frozen) {
                    this.frozen = frozen;
                    if (frozen) {
                        captureCurrentTopRowData();
                    } else {
                        rowData = null;
                    }
                }
            }
    
            private void captureCurrentTopRowData() {
                int row = table.rowAtPoint(getViewport().getViewPosition());
                if (row > -1) {
                    rowData = (Vector<String>) tableModel.getDataVector().get(row);
                }
            }
    
            @Override
            public void adjustmentValueChanged(AdjustmentEvent e) {
                if (frozen) {
                    captureCurrentTopRowData();
                    scrollToRowData();
                }
            }
    
            @Override
            public void tableChanged(TableModelEvent e) {
                scrollToRowData();
            }
    
            private void scrollToRowData() {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        if (frozen) {
                            int index = tableModel.getDataVector().indexOf(rowData);
                            getViewport().setViewPosition(table.getCellRect(index, 0, true).getLocation());
                        }
                    }
                });
            }
    
        }
    
        public static class MyTableModel extends DefaultTableModel {
            private int count;
    
            private boolean frozen = false;
    
            private List<Vector<String>> buffer = new ArrayList<Vector<String>>();
    
            public MyTableModel() {
                addColumn("Test");
            }
    
            public void insertNewRow() {
                Vector<String> rowData = createNewRowData();
                if (isFrozen()) {
                    buffer.add(rowData);
                } else {
                    insertRow(0, rowData);
                }
            }
    
            private Vector<String> createNewRowData() {
                Vector<String> data = new Vector<String>(1);
                data.add("Hello-" + (count++));
                return data;
            }
    
            public boolean isFrozen() {
                return frozen;
            }
    
            public void setFrozen(boolean frozen) {
                if (frozen == this.frozen) {
                    return;
                }
                this.frozen = frozen;
                if (!frozen) {
                    flushBuffer();
                }
            }
    
            private void flushBuffer() {
                for (Vector<String> rowData : buffer) {
                    insertRow(0, rowData);
                }
            }
        }
    
        public static void main(String[] args) {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            final MyTableModel model = new MyTableModel();
            JTable table = new JTable(model);
            final JScrollPaneExtension scroll = new JScrollPaneExtension(table, model);
            JPanel panel = new JPanel();
            final JButton freeze = new JButton("Freeze");
            freeze.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    if (model.isFrozen()) {
                        freeze.setText("Freeze model");
                    } else {
                        freeze.setText("Continue");
                    }
                    model.setFrozen(!model.isFrozen());
                }
            });
            final JButton freeze2 = new JButton("Freeze scroll");
            freeze2.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    if (scroll.isFrozen()) {
                        freeze2.setText("Freeze scroll");
                    } else {
                        freeze2.setText("Resume scroll");
                    }
                    scroll.setFrozen(!scroll.isFrozen());
                }
            });
            scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
            frame.add(scroll);
            panel.add(freeze);
            panel.add(freeze2);
            frame.getContentPane().add(panel, BorderLayout.NORTH);
            frame.pack();
            frame.setVisible(true);
            Timer t = new Timer();
            t.scheduleAtFixedRate(new TimerTask() {
    
                @Override
                public void run() {
                    SwingUtilities.invokeLater(new Runnable() {
    
                        @Override
                        public void run() {
                            model.insertNewRow();
                        }
                    });
                }
            }, new Date(), 300);
        }
    }
    

    【讨论】:

    • +1 我喜欢这种对比;我添加了一个交叉引用here
    • 两种方法都效果很好。我仍然希望将更新添加到表格中,因为用户可能想要手动向上滚动,所以我采用了覆盖的 scrollPane 方法。
    【解决方案2】:

    您也许可以调整here 所示的方法。它根据AdjustmentListenergetValueIsAdjusting() 的值调整滚动。点击拇指暂停滚动;释放以恢复。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-13
      相关资源
      最近更新 更多