为了账号安全,请及时绑定邮箱和手机立即绑定

JTable模型侦听器过早检测到插入的行(在绘制之前)

JTable模型侦听器过早检测到插入的行(在绘制之前)

慕容3067478 2019-11-03 04:04:23
我有一个JTable可以由用户动态添加的行。它位于中JScrollPane,因此当行数足够大时,滚动器变为活动状态。我的愿望是,当用户添加新行时,滚动条将一直移动到底部,以便新行在滚动窗格中可见。我目前(下面的SSCCE)试图使用表模型侦听器来检测何时插入行,并在进行检测时将滚动条一直向下推。但是,由于模型已更新,但实际上尚未绘制新行,因此这种检测似乎“还为时过早”,因此发生的情况是,在插入新行之前,滚动条一直一直移动到底部,并且然后将新行插入到窗格末尾的下方(不可见)。显然,这种方法在某种程度上是错误的。正确的方法是什么?import java.awt.Dimension;import java.awt.EventQueue;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;import javax.swing.BoxLayout;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JScrollBar;import javax.swing.JScrollPane;import javax.swing.JSplitPane;import javax.swing.JTable;import javax.swing.event.TableModelEvent;import javax.swing.event.TableModelListener;import javax.swing.table.DefaultTableModel;public class TableListenerTest {    private JFrame frame;    private JScrollPane scrollPane;    private JTable table;    private DefaultTableModel tableModel;    public static void main(String[] args) {        EventQueue.invokeLater(new Runnable() {            public void run() {                try {                    TableListenerTest window = new TableListenerTest();                    window.frame.setVisible(true);                } catch (Exception e) {                    e.printStackTrace();                }            }        });    }    public TableListenerTest() {        initialize();    }    private void initialize() {        frame = new JFrame();        frame.setBounds(100, 100, 450, 200);        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));        JSplitPane splitPane = new JSplitPane();        frame.getContentPane().add(splitPane);        scrollPane = new JScrollPane();        scrollPane.setPreferredSize(new Dimension(100, 2));        splitPane.setLeftComponent(scrollPane);
查看完整描述

3 回答

?
宝慕林4294392

TA贡献2021条经验 获得超8个赞

但是,这种检测似乎为时过早。


为了强调(@trashgod已经在评论中提到了它):是的,这是预期的,也是Swing中一个相当普遍的问题:-)


该表-以及具有任何模型的任何其他视图,不仅是数据,还包括选择,调整...-正在监听模型以进行自我更新。因此,自定义侦听器只是该行中的另一个侦听器(未定义服务顺序)。如果要执行依赖于视图状态的操作,则必须确保在所有内部更新准备就绪后执行此操作(在此具体上下文中,内部更新包括垂直scrollBar的adjustmentModel的更新)推迟自定义处理通过包装SwingUtilities.invokeLater直到完成内部操作为止:


TableModelListener l = new TableModelListener() {

    @Override

    public void tableChanged(TableModelEvent e) {

        if (e.getType() == TableModelEvent.INSERT) {

            invokeScroll();

        }

    }


    protected void invokeScroll() {

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {

                int last = table.getModel().getRowCount() - 1;

                Rectangle r = table.getCellRect(last, 0, true);

                table.scrollRectToVisible(r);

            }

        });

    }

};

table.getModel().addTableModelListener(l);



查看完整回答
反对 回复 2019-11-04
?
30秒到达战场

TA贡献1828条经验 获得超6个赞

我认为这可能有助于我理解@trashgod所说的话我没得到的东西。他提到invokeLater像您一样将滚动调用包装在一个包装中,我认为这将无法完成任何事情,因为我的整个程序已经在这种包装中。我现在注意到包装器创建了一个new Runnable,我猜这是实现他所谈论的“排序”的要素,从而保证了这个新的runnable不会在另一个runnable中的下一行完成之前运行。完全正确吗?

查看完整回答
反对 回复 2019-11-04
  • 3 回答
  • 0 关注
  • 340 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信