JTable模型侦听器过早检测插入的行(在绘制它们之前) [英] JTable model listener detects inserted rows too soon (before they are drawn)

查看:144
本文介绍了JTable模型侦听器过早检测插入的行(在绘制它们之前)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 JTable ,可以有用户动态添加的行。它位于 JScrollPane 中,因此当行数足够大时,滚动条变为活动状态。我的愿望是,当用户添加新行时,滚动条一直移动到底部,以便在滚动窗格中看到新行。我现在(下面的SSCCE)尝试使用表模型侦听器来检测何时插入行,并在进行检测时强制滚动条一直向下。然而,似乎这种检测太早了,因为模型已更新,但新行尚未实际绘制,所以会发生什么是滚动器一直移动到底部只是之前插入新行,然后将新行插入窗格末尾的下方(不可见)。

I have a JTable which can have rows dynamically added by the user. It sits in a JScrollPane, so as the number of rows gets large enough, the scroller becomes active. My desire is that when the user adds a new row, the scroller moves all the way to the bottom, so that the new row is visible in the scrollpane. I'm currently (SSCCE below) trying to use a table model listener to detect when the row is inserted, and force the scrollbar all the way down when the detection is made. However, it seems this detection is "too early," as the model has updated but the new row has not actually been painted yet, so what happens is the scroller moves all the way to the bottom just before the new row is inserted, and then the new row is inserted just below the end of the pane (out of visibility).

显然这种方法在某种程度上是错误的。什么是正确的方法?

Obviously this approach is wrong somehow. What is the correct approach?

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);

        tableModel = new DefaultTableModel(new Object[]{"Stuff"},0);
        table = new JTable(tableModel);
        scrollPane.setViewportView(table);
        table.getModel().addTableModelListener(new TableModelListener() {
            public void tableChanged(TableModelEvent e) {
                if (e.getType() == TableModelEvent.INSERT) {
                    JScrollBar scrollBar = scrollPane.getVerticalScrollBar();
                    scrollBar.setValue(scrollBar.getMaximum());
                }
            }
        });

        JButton btnAddRow = new JButton("Add Row");
        btnAddRow.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                tableModel.addRow(new Object[]{"new row"});
            }
        });
        splitPane.setRightComponent(btnAddRow);
    }
}

编辑:根据trashgod的请求更新了下面的SSCCE。但是,如果我将滚动逻辑从表模型侦听器移动到按钮侦听器,那么这个版本仍然不起作用,那么它确实有效!

Updated SSCCE below based on trashgod's request. This version still does not work, however, if I move the scrolling logic from the table model listener to the button listener as he did, then it does work!

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Rectangle;
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.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);

            tableModel = new DefaultTableModel(new Object[]{"Stuff"},0);
            table = new JTable(tableModel);
            scrollPane.setViewportView(table);
            table.getModel().addTableModelListener(new TableModelListener() {
                public void tableChanged(TableModelEvent e) {
                    if (e.getType() == TableModelEvent.INSERT) {
                        int last = table.getModel().getRowCount() - 1;
                        Rectangle r = table.getCellRect(last, 0, true);
                        table.scrollRectToVisible(r);
                    }
                }
            });

            JButton btnAddRow = new JButton("Add Row");
            btnAddRow.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    tableModel.addRow(new Object[]{"new row"});
                }
            });
            splitPane.setRightComponent(btnAddRow);
        }
    }


推荐答案

示例使用 scrollRectToVisible()来(有条件地)滚动到最后单元矩形。作为一项功能,您可以单击拇指以暂停滚动并释放以继续。

This example uses scrollRectToVisible() to (conditionally) scroll to the last cell rectangle. As a feature you can click on the thumb to suspend scrolling and release to resume.

private void scrollToLast() {
    if (isAutoScroll) {
        int last = table.getModel().getRowCount() - 1;
        Rectangle r = table.getCellRect(last, 0, true);
        table.scrollRectToVisible(r);
    }
}

附录:我试过在我的SSCCE中 scrollRectToVisible ,它仍然表现出同样的问题。

Addendum: I tried scrollRectToVisible in my SSCCE, and it still exhibits the same problem.

动作提供鼠标和键盘控制:

This Action provides both mouse and keyboard control:

JButton btnAddRow = new JButton(new AbstractAction("Add Row") {

    @Override
    public void actionPerformed(ActionEvent e) {
        tableModel.addRow(new Object[]{"new row"});
        int last = table.getModel().getRowCount() - 1;
        Rectangle r = table.getCellRect(last, 0, true);
        table.scrollRectToVisible(r);
    }
});

附录:这是您示例中的一个变体,用于说明修改后的布局策略。

Addendum: Here's a variation on your example that illustrates a revised layout strategy.

/** @see https://stackoverflow.com/a/14429388/230513 */
public class TableListenerTest {

    private static final int N = 8;
    private JFrame frame;
    private JScrollPane scrollPane;
    private JTable table;
    private DefaultTableModel tableModel;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                TableListenerTest window = new TableListenerTest();
                window.frame.setVisible(true);
            }
        });
    }

    public TableListenerTest() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.X_AXIS));
        tableModel = new DefaultTableModel(new Object[]{"Stuff"}, 0);
        for (int i = 0; i < N; i++) {
            tableModel.addRow(new Object[]{"new row"});
        }
        table = new JTable(tableModel) {

            @Override
            public Dimension getPreferredScrollableViewportSize() {
                return new Dimension(200, table.getRowHeight() * N);
            }
        };
        scrollPane = new JScrollPane();
        scrollPane.setViewportView(table);
        JButton btnAddRow = new JButton(new AbstractAction("Add Row") {

            @Override
            public void actionPerformed(ActionEvent e) {
                tableModel.addRow(new Object[]{"new row"});
                int last = table.getModel().getRowCount() - 1;
                Rectangle r = table.getCellRect(last, 0, true);
                table.scrollRectToVisible(r);
            }
        });
        frame.add(scrollPane);
        frame.add(btnAddRow);
        frame.pack();
    }
}

这篇关于JTable模型侦听器过早检测插入的行(在绘制它们之前)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆