如何仅在滚动条位于底部并且滚动锁定关闭时才使JTextPane自动滚动?

如何仅在滚动条位于底部并且滚动锁定关闭时才使JTextPane自动滚动? 这不应该与插入符号有任何关系,这似乎是我在Google上发现的。 🙁

我认为我的程序完全符合您的要求,但有一个可能的警告:您不能输入文本区域。 所以这对日志查看器很有用,但不适合交互式控制台。 代码运行了一段时间,因为我已经把它变成了一个可以运行的方法演示。 我建议按原样运行程序并检查行为。 如果这种行为适合你,那么就花一点时间研究代码。 我在代码中包含了注释,以突出显示一些更重要的部分。


更新2013-07-17:您可能还希望在页面的下方单独回答中查看随机花花公子的解决方案。 他的方法比我的更优雅。

另请参阅Swing:滚动到JScrollPane的底部,以当前视口位置为条件,以获得不会干扰插入符号位置的潜在解决方案。


SCCE源代码如下:

import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.Timer; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; public class ScrollingJTextAreaExample extends JFrame { // Worker thread to help periodically append example messages to JTextArea Timer timer = new Timer(); // Merely informative counter, will be displayed with the example messages int messageCounter = 0; // GUI components JScrollPane jScrollPane; JTextArea jTextArea; public ScrollingJTextAreaExample() { initComponents(); // Boiler plate GUI construction and layout // Configure JTextArea to not update the cursor position after // inserting or appending text to the JTextArea. This disables the // JTextArea's usual behavior of scrolling automatically whenever // inserting or appending text into the JTextArea: we want scrolling // to only occur at our discretion, not blindly. NOTE that this // breaks normal typing into the JTextArea. This approach assumes // that all updates to the ScrollingJTextArea are programmatic. DefaultCaret caret = (DefaultCaret) jTextArea.getCaret(); caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE); // Schedule a task to periodically append example messages to jTextArea timer.schedule(new TextGeneratorTask(), 250, 250); // This DocumentListener takes care of re-scrolling when appropriate Document document = jTextArea.getDocument(); document.addDocumentListener(new ScrollingDocumentListener()); } // Boring, vanilla GUI construction and layout code private void initComponents() { jScrollPane = new javax.swing.JScrollPane(); jTextArea = new javax.swing.JTextArea(); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); jScrollPane.setViewportView(jTextArea); getContentPane().add(jScrollPane, java.awt.BorderLayout.CENTER); setSize(320, 240); setLocationRelativeTo(null); } // ScrollingDocumentListener takes care of re-scrolling when appropriate class ScrollingDocumentListener implements DocumentListener { public void changedUpdate(DocumentEvent e) { maybeScrollToBottom(); } public void insertUpdate(DocumentEvent e) { maybeScrollToBottom(); } public void removeUpdate(DocumentEvent e) { maybeScrollToBottom(); } private void maybeScrollToBottom() { JScrollBar scrollBar = jScrollPane.getVerticalScrollBar(); boolean scrollBarAtBottom = isScrollBarFullyExtended(scrollBar); boolean scrollLock = Toolkit.getDefaultToolkit() .getLockingKeyState(KeyEvent.VK_SCROLL_LOCK); if (scrollBarAtBottom && !scrollLock) { // Push the call to "scrollToBottom" back TWO PLACES on the // AWT-EDT queue so that it runs *after* Swing has had an // opportunity to "react" to the appending of new text: // this ensures that we "scrollToBottom" only after a new // bottom has been recalculated during the natural // revalidation of the GUI that occurs after having // appending new text to the JTextArea. EventQueue.invokeLater(new Runnable() { public void run() { EventQueue.invokeLater(new Runnable() { public void run() { scrollToBottom(jTextArea); } }); } }); } } } class TextGeneratorTask extends TimerTask { public void run() { EventQueue.invokeLater(new Runnable() { public void run() { String message = (++messageCounter) + " Lorem ipsum dolor sit amet, consectetur" + " adipisicing elit, sed do eiusmod tempor" + " incididunt ut labore et dolore magna aliqua.\n"; jTextArea.append(message); } }); } } public static boolean isScrollBarFullyExtended(JScrollBar vScrollBar) { BoundedRangeModel model = vScrollBar.getModel(); return (model.getExtent() + model.getValue()) == model.getMaximum(); } public static void scrollToBottom(JComponent component) { Rectangle visibleRect = component.getVisibleRect(); visibleRect.y = component.getHeight() - visibleRect.height; component.scrollRectToVisible(visibleRect); } public static void main(String args[]) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new ScrollingJTextAreaExample().setVisible(true); } }); } } 

这个问题迟到了,但我提出了这个解决方案。

  conversationPane = new JTextPane(); final JScrollPane conversationScrollPane = new JScrollPane(conversationPane); conversationScrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { BoundedRangeModel brm = conversationScrollPane.getVerticalScrollBar().getModel(); boolean wasAtBottom = true; public void adjustmentValueChanged(AdjustmentEvent e) { if (!brm.getValueIsAdjusting()) { if (wasAtBottom) brm.setValue(brm.getMaximum()); } else wasAtBottom = ((brm.getValue() + brm.getExtent()) == brm.getMaximum()); } }); 

似乎完全符合我的需求。 一点解释:基本上如果滚动条没有被人移动而且条形图最后一个/最低位置,则将其重置为最大值。 如果正在手动调整,请检查它是否已调整到底部。

文本区域滚动可能是有意义的。

我不知道滚动锁定键如何影响它。 我在Scroll Lock的Wikipedia页面上找到了以下内容:

因此,Scroll Lock可以被视为几乎所有现代程序和操作系统中的一个已不存在的function。

所以我不担心。

我需要对日志文本区域执行相同操作。 我在网上找到的解决方案对我没有用(他们要么在快速记录大量消息时停止自动滚动,要么即使你用鼠标滚轮向上滚动也阻止了底部的滚动条)。

我是这样做的:

 public static void makeTextAreaAutoScroll(JTextArea textArea) { // Get the text area's scroll pane : final JScrollPane scrollPane = (JScrollPane) (textArea.getParent().getParent()); // Disable the auto scroll : ((DefaultCaret)textArea.getCaret()).setUpdatePolicy(DefaultCaret.NEVER_UPDATE); // Add a listener to the vertical scroll bar : scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { private int _val = 0; private int _ext = 0; private int _max = 0; private final BoundedRangeModel _model = scrollPane.getVerticalScrollBar().getModel(); @Override public void adjustmentValueChanged(AdjustmentEvent e) { // Get the new max : int newMax = _model.getMaximum(); // If the new max has changed and if we were scrolled to bottom : if (newMax != _max && (_val + _ext == _max) ) { // Scroll to bottom : _model.setValue(_model.getMaximum() - _model.getExtent()); } // Save the new values : _val = _model.getValue(); _ext = _model.getExtent(); _max = _model.getMaximum(); } }); } 

只需这样使用它:

 makeTextAreaAutoScroll(yourTextArea); 

你可以用这段代码测试:

 new Timer().schedule(new TimerTask() { @Override public void run() { javax.swing.SwingUtilities.invokeLater(new Runnable() { @Override public void run() { String line = "test " + Math.random(); yourTextArea.append(yourTextArea.getText().isEmpty() ? line : "\n" + line); } }); } }, 0, 5); 

现在,如果滚动条位于底部,则文本区域应自动滚动;如果移动滚动条,则停止自动滚动(通过拖动条形或使用滚轮),如果再次将滚动条置于底部,则再次自动滚动。

尝试这个 :

 JTextArea txt = new JTextArea(); JScrollPane jsp = new JScrollPane(history, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); txt.setCaretPosition(txt.getDocument().getLength()); // do this afeter any event 

希望对你有所帮助

在我阅读Mike Clark和随机dude的解决方案之后,我最终得到了下面的代码段。

 private boolean doAutoScroll = true; private JTextPane textPane; private JScrollPane scrollPane; public void setup() { /* Left Panel */ textPane = new JTextPane(); textPane.setPreferredSize(new Dimension(600, 400)); // width, height /* * Not update the cursor position after inserting or appending text to the JTextPane. * [NOTE] * This breaks normal typing into the JTextPane. * This approach assumes that all updates to the JTextPane are programmatic. */ DefaultCaret caret = (DefaultCaret) textPane.getCaret(); caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE); scrollPane = new JScrollPane(textPane); scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { BoundedRangeModel brm = scrollPane.getVerticalScrollBar().getModel(); @Override public void adjustmentValueChanged(AdjustmentEvent e) { // Invoked when user select and move the cursor of scroll by mouse explicitly. if (!brm.getValueIsAdjusting()) { if (doAutoScroll) brm.setValue(brm. getMaximum()); } else { // doAutoScroll will be set to true when user reaches at the bottom of document. doAutoScroll = ((brm.getValue() + brm.getExtent()) == brm.getMaximum()); } } }); scrollPane.addMouseWheelListener(new MouseWheelListener() { BoundedRangeModel brm = scrollPane.getVerticalScrollBar().getModel(); @Override public void mouseWheelMoved(MouseWheelEvent e) { // Invoked when user use mouse wheel to scroll if (e.getWheelRotation() < 0) { // If user trying to scroll up, doAutoScroll should be false. doAutoScroll = false; } else { // doAutoScroll will be set to true when user reaches at the bottom of document. doAutoScroll = ((brm.getValue() + brm.getExtent()) == brm.getMaximum()); } } }); } 

不同之处在于,即使用户使用鼠标滚轮向上和向下滚动,它还会使用MouseWheelListener更新doAutoScroll标志。