java - JScrollPane 和自动滚动 : Disturbing scrolling behavior

标签 java swing jscrollpane autoscroll

我正在编写一个使用 JScrollPane 的应用程序。在这个 JScrollPane 中,我想自动显示搜索结果,这意味着我必须在 JScrollPane 中动态添加和删除结果。结果实现为 JTextArea,它嵌入在 GridBagLayout 中。

当有大量搜索结果时,JScrollPane 会自动滚动到底部(它应该在顶部)。我已经用我在这里找到的解决方案解决了它。这里的问题是,你可以看到,它是如何滚动回到顶部的。是否可以消除这种行为?

我发现了以下事情:

  • 我必须删除以前的搜索结果才能显示新的搜索结果。如果我不删除以前的,它会正确显示。
  • 它既没有解决我在添加行后每次调整 JScrollPane 时更新 JScrollPane 的问题,也没有解决仅在添加所有行后才更新的问题。

最好的办法就是禁用自动滚动。我创建了一个可执行示例来演示此行为。单击“添加行”按钮时,将添加 500 行。多点几次,就很清楚了。

非常感谢您的帮助!

import java.awt.GridBagConstraints;
import javax.swing.JTextArea;

public class ScrollPaneTest extends javax.swing.JFrame {
    private javax.swing.JButton jButton1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JScrollPane jScrollPane1;


    /**
     * Creates new form ScrollPaneTest
     */
    public ScrollPaneTest() {
        initComponents();
    }

    /**
     * Adds a new row.
     * @param index The index of the new row.
     */
    private void addRow(int index) {
        JTextArea row = new JTextArea("Area " + index);
        row.setEditable(false);
        row.setBorder(null);

        GridBagConstraints gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = index;
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 0);
        jPanel2.add(row, gridBagConstraints);
    }


    /**
     * Initializes the components.
     */
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        jPanel1 = new javax.swing.JPanel();
        jPanel2 = new javax.swing.JPanel();
        jButton1 = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        jScrollPane1.setPreferredSize(new java.awt.Dimension(400, 400));

        jScrollPane1.setViewportView(jPanel1);

        jPanel1.setLayout(new java.awt.BorderLayout());

        jPanel2.setLayout(new java.awt.GridBagLayout());
        jPanel1.add(jPanel2, java.awt.BorderLayout.NORTH);

        jScrollPane1.setViewportView(jPanel1);

        jButton1.setText("Create Rows");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(layout.createSequentialGroup()
                .addGap(148, 148, 148)
                .addComponent(jButton1)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 245, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jButton1)
                .addGap(0, 21, Short.MAX_VALUE))
        );

        pack();
    }                   

    /**
     * Creates 500 new rows.
     * @param evt
     */
    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) { 
        jPanel2.removeAll();

        for(int i = 0; i < 1000; i++) {
            addRow(i);
        }

         javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                jScrollPane1.getVerticalScrollBar().setValue(0);
            }

         });
         jPanel2.validate();
         jPanel2.repaint();
    }                                        

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() { 
                new ScrollPaneTest().setVisible(true);
            }
        });
    }             
}

更新 1

我删除了 lambda 表达式。希望它现在也可以用

更新 2

令人不安的滚动行为问题已通过替换解决

jPanel2.validate();
jPanel2.repaint();

jScrollPane1.validate();
jScrollPane1.repaint();

尽管如此,这个问题的两个答案在其他一些情况下都可能非常有用,应该引起注意。

最佳答案

实现此目的的一种方法是为您的滚动 Pane 自定义 JViewPort。此自定义视口(viewport)覆盖 setViewPosition 并使用标志来阻止滚动或不滚动。

这是此类代码的示例,在更改文本区域的内容之前,我们“锁定”视口(viewport)以防止滚动,稍后再解锁:

import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class TestNoScrolling {

    private int lineCount = 0;
    private LockableViewPort viewport;
    private JTextArea ta;

    private final class LockableViewPort extends JViewport {

        private boolean locked = false;

        @Override
        public void setViewPosition(Point p) {
            if (locked) {
                return;
            }
            super.setViewPosition(p);
        }

        public boolean isLocked() {
            return locked;
        }

        public void setLocked(boolean locked) {
            this.locked = locked;
        }
    }

    protected void initUI() {
        JFrame frame = new JFrame("test");
        ta = new JTextArea(5, 30);
        JScrollPane scrollpane = new JScrollPane();
        viewport = new LockableViewPort();
        viewport.setView(ta);
        scrollpane.setViewport(viewport);
        frame.add(scrollpane);
        frame.pack();
        frame.setVisible(true);
        Timer t = new Timer(1000, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                viewport.setLocked(true);
                ta.append("Some new line " + lineCount++ + "\n");
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        viewport.setLocked(false);
                    }
                });
            }
        });
        t.setRepeats(true);
        t.start();
    }

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

            @Override
            public void run() {
                new TestNoScrolling().initUI();
            }
        });
    }
}

关于java - JScrollPane 和自动滚动 : Disturbing scrolling behavior,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29087376/

相关文章:

java - 将 Maven 本地 .jar 作为 lib 导入

Java 文件名过滤器

java - 基于 Applet 的 Jasper 报告!

java - 为什么某个 IDE 不允许我为 JComboBox 添加参数?

jquery - 如何使用 jScrollPane(jquery 插件)应用边框半径

java - 堆栈被覆盖

java - 使用 Java 在 Jtable 中显示客户数据

java - 外观更新后刷新 JTable

java - 为什么水平滚动条永远不会出现在 JTable 上?

java - 将 JScrollPane 添加到 JSplitPane