java - 如何卡住滚动 Pane 内的 JTable 行选择

标签 java swing jtable jscrollpane

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

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

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

我可以使用这段代码让选定的行转到顶部:

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

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

有没有办法告诉视口(viewport)/滚动 Pane 卡住所选内容并关闭它,以便滚动 Pane 按照您的预期进行更新?

最佳答案

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

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

这是一个说明这两种机制的 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);
    }
}

关于java - 如何卡住滚动 Pane 内的 JTable 行选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10451008/

相关文章:

java - 如何显示列名并为jtable设置滚动条?

java - 解析方法调用: Implicit dependencies on other classes

java.lang.ClassCastException : java. lang.String 无法转换为 [Ljava.lang.Object

java - 如何从不包含 JTextArea 的方法附加 JTextArea?

java - 将 JPanel 保存为图像(不是文件,而是变量)

java - JTable 缺少列标题

java - Netty3.2 API 中的超时示例( IdleStateHandler 和 ReadTimeoutHandler )不适用于服务器

java - 无法解决 jclouds 输出流不完整错误

java - 从图像的字节中检索创建日期

java - 如何正确使用自定义渲染器绘制 JTable 中的特定单元格?