使JScrollPane自动滚动到底部

22

我正在尝试使用JScrollPane和JTextArea实现滚动条。JTextArea一直被添加,我希望JScrollPane在添加更多文本时保持向下滚动。如何实现呢?

8个回答

42

如果您正在寻找(我认为是)更简单的答案,请查看:文本区滚动

在JDK5之前,您需要在每次附加后手动更改插入符号的位置。现在,您可以像这样将此行为作为默认行为:

 JTextArea textArea = new JTextArea();
 DefaultCaret caret = (DefaultCaret)textArea.getCaret();
 caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);

这样做的好处是你不需要在代码中多次使用此片段!


+1 setUpdatePolicy() API解释得非常清楚。我已经相应地更新了我在引用问题中的答案。 - trashgod
是的,API 经常变化,很难一直跟上变化。 - camickr
这很有帮助,但如果用户点击最后一行之后的任何其他位置,自动滚动就会停止。这不是很直观。如果按照通常的行为方式工作,只需滚动到底部即可重新激活自动滚动,那将是很好的。 - jcalfee314
2
@jcalfee314,可以查看智能滚动来寻找另一个可能的解决方案。 - camickr
为了解决这个问题,我添加了一个按钮。当单击该按钮时,将重新启用自动滚动。按钮的代码只需要这一行:textArea.setCaretPosition(textArea.getDocument().getLength()); - Rok T.
@jcalfee314 这里是解决该问题的答案 - Krisztian Nagy Zsolt

9
我在这里找到了答案: JScrollPane和JList自动滚动
scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {  
        public void adjustmentValueChanged(AdjustmentEvent e) {  
            e.getAdjustable().setValue(e.getAdjustable().getMaximum());  
        }
    });

7
你的“解决方案”实际上是被链接问题的问题:如果你手动尝试移动垂直滚动条,这段代码将会再次滚动到底部! - jackrabbit
@jackrabbit 这里有一段代码,可以让它在用户手动移动垂直滚动条时停止滚动。如果用户滚动到底部,它将继续滚动。 - Krisztian Nagy Zsolt

7

如果您不断地向其中写入数据,则可以使用以下方法:

textArea.setCaretPosition(textArea.getDocument().getLength());

在添加新数据后,就会自动滚动到JScrollPane的底部。


1
虽然可行,但不是最佳解决方案 - 最佳方案是@camickr的答案 :-) - kleopatra

5

这是解决方案。

JTextArea textArea = new JTextArea();
DefaultCaret caret = (DefaultCaret)textArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);` 

1
即使这与Rob接受的答案相同,但如果您回答正确,我会给您+1分。 - kleopatra

2
接受的解决方案很好,但只适用于文本区域可编辑的情况,即没有 jTextArea.setEditable(false)。由Krigath建议的解决方案更通用,但在此处提问时存在问题JScrollPane and JList auto scroll。使用该问题的答案,您可以获得通用解决方案,例如:
        JScrollPane scrollPane = new JScrollPane(jTextArea);

    verticalScrollBarMaximumValue = scrollPane.getVerticalScrollBar().getMaximum();
    scrollPane.getVerticalScrollBar().addAdjustmentListener(
            e -> {
                if ((verticalScrollBarMaximumValue - e.getAdjustable().getMaximum()) == 0)
                    return;
                e.getAdjustable().setValue(e.getAdjustable().getMaximum());
                verticalScrollBarMaximumValue = scrollPane.getVerticalScrollBar().getMaximum();
            });

只有垂直滚动条扩展时,Pane 才会向下滚动,以响应追加的文本行。我承认可以找到一种无需额外变量过滤事件的方法,并且如果有人发布该方法,我将不胜感激。


我修改了你的代码,现在它运行得非常好。+1 - Krisztian Nagy Zsolt

1
一种解决方法是:您可以将该监听器声明为一个类,然后在需要它的事件中实例化它。之后,您可以在强制重新绘制屏幕后删除该类。运作得很好。

0

如果您要在多线程程序中使用自动滚动,请小心。 例如,如果某个方法如下:

public void addNewLine(String s){
        textPane.setText(textPane.getText()+"\n"+s);
        if(autoscrollCheckBox.isSelected()){
            this.revalidate();
            JScrollBar vertical = scrollPane.getVerticalScrollBar();
            vertical.setValue(vertical.getMaximum());
        }
}

如果从另一个线程调用addNewLine(或vertical.setValue(...))方法,将会遇到以下异常。(特别是在启用autoscroll的情况下尝试调整窗口大小或滚动时)
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.text.BoxView.calculateMajorAxisRequirements(BoxView.java:871)
at javax.swing.text.BoxView.checkRequests(BoxView.java:930)
at javax.swing.text.BoxView.getMinimumSpan(BoxView.java:568)
...

原因是,方法的多线程调用会干扰Swings的事件处理,因此您将获得随机结果或像上面那样的错误(阅读更多这里)。
正确的方法是调用SwingUtilities.invokeLater(...):
public void addNewLine(String s){
       SwingUtilities.invokeLater( () -> {
            textPaneOutput.setText(textPaneOutput.getText()+"\n"+s);
            if(autoscrollCheckBox.isSelected()){
                this.revalidate();
                JScrollBar vertical = scrollPane.getVerticalScrollBar();
                vertical.setValue(vertical.getMaximum());
            }
        });
 }

这样你就能够自动滚动线程安全了!


0

我看了一下答案,发现@user9999的answer是一个适合那些想要滚动条持续滚动的好解决方案。我编辑了代码,去掉了变量。这使得当用户手动滚动时,滚动停止。如果用户滚动到文本区域或滚动区域的末尾,自动滚动会继续。

(正如@user9999建议的那样,我删除了变量,并添加了jScrollPane1.getHeight()作为测量值,以便在当前滚动条值低于最大值时停止滚动)

以下是解决方法:

jScrollPane1.getVerticalScrollBar().addAdjustmentListener(
e -> {
    if ((e.getAdjustable().getValue() - e.getAdjustable().getMaximum()) > -jScrollPane1.getHeight() - 20){
        e.getAdjustable().setValue(e.getAdjustable().getMaximum());
    }   
});

编辑: 在-jScrollPane1.getHeight() - 20中添加了-20,因为有时候没有它就无法滚动,我猜-20可以根据文本区域的字体大小进行调整。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接