1

我有在表中显示日志事件的窗口。例如,用户正在阅读表格某行中的文本。当新日志出现时,它们被添加到表的开头,并且用户正在阅读的行向下移动。当在表的开头添加新行时,我需要防止 JScrollPane 滚动。我尝试了不同的东西,但没有任何帮助。有人可以建议我如何实施吗?提前致谢!

4

1 回答 1

1

棘手的任务 :-) 搬出是因为默认情况下不会以任何方式调整滚动位置:在上面添加行只是保持垂直滚动位置,然后像以前一样指向不同的行。

需要什么:

  • 跟踪插入前的 visibleRect/最后一行
  • 监听插入类型的模型变化
  • 计算新的可见矩形,使旧的最后一行滚动回视图
  • 触发滚动

这很棘手,因为我们需要倾听模型的变化。该侦听器是由表在内部注册的 modelListener 的邻居,该表在更改时自行更新。所以我们需要确保在内部更改完成采取行动,同时在内部更新之前使用信息。

一个肮脏的解决方案 - 取决于最后添加的第一个通知的摇摆监听器通知的通常顺序(注意:在生产代码中不要!它很容易破坏,当更改 LAF 时) - 是收集之前的状态时通知并调整包含在 invokeLater 中的滚动位置:

final DefaultTableModel model = new DefaultTableModel(20, 2);
for (int i = 0; i < model.getRowCount(); i++) {
    model.setValueAt(i, i, 0);
}
final JTable table = new JTable(model);
Action action = new AbstractAction("add row at 0") {

    @Override
    public void actionPerformed(ActionEvent e) {
        model.insertRow(0, new Object[] {});
    }
};
JXFrame frame = wrapWithScrollingInFrame(table, "modify scroll");
TableModelListener l = new TableModelListener() {

    @Override
    public void tableChanged(TableModelEvent e) {
        if (!TableUtilities.isInsert(e)) return;
        final Rectangle r = table.getVisibleRect();
        final int lastRow = table.rowAtPoint(
                new Point(0, r.y + r.height - 5));
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                // assuming 
                // a) the insert was exactly 1 row
                // b) was _before_ the old last row in view-coordinates
                Rectangle cell = table.getCellRect(lastRow + 1, 0, true);
                cell.translate(0, -r.height + cell.height);
                cell.height = r.height;
                table.scrollRectToVisible(cell);
            }
        });
    }
};
model.addTableModelListener(l);

干净地获取之前状态的适当方法取决于您的确切上下文。您可以在表的生命周期内跟踪第一行/最后一行,更新它们(一个或全部)

  • 行 selectionModel 的 ListSelectionListener(如果用户正在阅读的行总是被选中)
  • 垂直滚动条的 ChangeListener
  • 一个 ComponentListener 来改变 scrollPane 的大小
于 2013-08-24T16:56:19.690 回答