【问题标题】:JTextArea scroll to bottom only if text is appended仅当附加文本时,JTextArea 才会滚动到底部
【发布时间】:2015-03-24 12:32:08
【问题描述】:

我正在尝试创建一个JTextArea,每次将文本附加到该文本区域时,它都会滚动到底部。否则,用户应该能够滚动顶部并查看上一条消息。我使用了这段代码:

JTextArea terminalText  = new JTextArea();
JPanel terminal = new JPanel();
terminal.setLayout(new BorderLayout()); 
add(terminal);  //Adds the terminal to mother JPanel

//I added scrollbar to my JTextArea
JScrollPane scroll = new JScrollPane(terminalText);  
terminal.add(scroll, BorderLayout.CENTER);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

scroll.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {  
public void adjustmentValueChanged(AdjustmentEvent e) {  
 e.getAdjustable().setValue(e.getAdjustable().getMaximum());  
}});

到目前为止,每次我使用terminalText.append 将某些内容附加到terminalText 时,这段代码似乎都会让我的文本区域滚动到terminalText 文本区域的底部。

但是,用户不能使用滚动条滚动到顶部以查看上一条消息。有没有办法来解决这个问题?我应该使用DocumentListener 来实现这一点吗?

【问题讨论】:

  • 每次附加一些文本时使用setCaretPosition(terminalText.getDocument().getLength()); 之类的东西。您可以将其放在 DoucmentListener 中,但可能需要使用 SwingUtilities#invokeLater 以避免可能的突变问题
  • 我在我的 GUI 中多次使用 append,我希望能够自动化这个过程。你能解释一下SwingUtilities#invokeLaterDocumentListener 的用法吗?

标签: java swing awt jscrollpane jtextarea


【解决方案1】:

查看Smart Scrolling

如果滚动条位于底部,那么随着文本的添加,您将看到新文本。

如果用户滚动到不同的位置,则视口将一直停留在那里,直到用户滚动回底部。

【讨论】:

  • 谢谢,正是我想要的!希望这个功能会在下一个版本的 java 中提供。
【解决方案2】:

作为一个简单(粗略)的概念证明......

这基本上是在JTextArea 中添加一个DocumentListener,并且在任何Document 事件上,使用setCaretPosition 将插入符号移动到文档的末尾。

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import java.util.WeakHashMap;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.JTextComponent;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JTextArea ta = new JTextArea(10, 20);
                ta.setWrapStyleWord(true);
                ta.setLineWrap(true);
                MoveToTheBottom.install(ta);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new JScrollPane(ta));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                Timer timer = new Timer(500, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        ta.append(new Date().toString() + "\n");
                    }
                });
                timer.start();
            }
        });
    }

    public static class MoveToTheBottom implements DocumentListener {

        private static WeakHashMap<JTextComponent, DocumentListener> registry = new WeakHashMap<>(25);
        private JTextComponent parent;

        protected MoveToTheBottom(JTextComponent parent) {
            this.parent = parent;
            parent.getDocument().addDocumentListener(this);
        }

        public static void install(JTextComponent parent) { 
            MoveToTheBottom bottom = new MoveToTheBottom(parent);
            registry.put(parent, bottom);
        }

        public static void uninstall(JTextComponent parent) {
            DocumentListener listener = registry.remove(parent);
            if (listener != null) {
                parent.getDocument().removeDocumentListener(listener);
            }
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            parent.setCaretPosition(e.getDocument().getLength());
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            parent.setCaretPosition(e.getDocument().getLength());
        }

        @Override
        public void changedUpdate(DocumentEvent e) {
            parent.setCaretPosition(e.getDocument().getLength());
        }

    }

}

该示例演示了一个可能的可重用 API,您可以根据需要使用它来“安装”和“卸载”支持

【讨论】:

    猜你喜欢
    • 2014-11-03
    • 2013-01-11
    • 1970-01-01
    • 2013-04-22
    • 2018-06-26
    • 2014-01-10
    • 2012-03-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多