java 仅当滚动条位于底部且滚动锁定关闭时,如何使 JTextPane 自动滚动?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4045722/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make JTextPane autoscroll only when scroll bar is at bottom and scroll lock is off?
提问by Fran Fitzpatrick
How to make JTextPane autoscroll only when scroll bar is at bottom and scroll lock is off? This shouldn't have anything to do with caret, which is what I seem to be finding all over Google. :(
仅当滚动条位于底部且滚动锁定关闭时,如何使 JTextPane 自动滚动?这不应该与插入符号有任何关系,这就是我在 Google 上似乎发现的所有内容。:(
回答by Mike Clark
I think my program below meets your requirements exactly, with one possible caveat: you're not allowed to type in the text area. So this would be good for a log viewer, but not an interactive console. The code runs a little long because I have made it into a ready-to-run demo of the approach. I suggest running the program as-is and checking out the behavior. If the behavior works well for you, then invest a little time in studying the code. I have included comments in the code to highlight some of the more important sections.
我认为我下面的程序完全符合您的要求,但有一个可能的警告:您不能在文本区域中输入。所以这对日志查看器有好处,但对交互式控制台不利。该代码运行时间有点长,因为我已将其制作为该方法的现成演示。我建议按原样运行程序并检查行为。如果该行为适合您,则花一点时间研究代码。我在代码中添加了注释以突出显示一些更重要的部分。
Update 2013-07-17: You may also want to check out random dude's solution in his separate answer farther down the page. His approach is more elegant than mine.
2013 年 7 月 17 日更新:您可能还想在页面下方的单独答案中查看random dude的解决方案。他的方法比我的更优雅。
Also see Swing: Scroll to bottom of JScrollPane, conditional on current viewport locationfor a potential solution that does not interfere with the caret position.
另请参阅Swing:滚动到 JScrollPane 的底部,以当前视口位置为条件,以获取不干扰插入符号位置的潜在解决方案。
SCCE source code follows:
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);
}
});
}
}
回答by random dude
Little late to this question, but I came up with this solution.
这个问题有点晚了,但我想出了这个解决方案。
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());
}
});
Seems to work perfectly for my needs. Little explanation: Essentially if the scroll bar is not being moved by a person and the bar was last at the maximum/bottom then reset it to the maximum. If it's being manually adjusted, then check to see if it was adjusted to be at the bottom.
似乎非常适合我的需要。小解释:本质上,如果滚动条没有被人移动并且滚动条最后位于最大值/底部,则将其重置为最大值。如果是手动调整,则检查是否调整到底部。
回答by camickr
Text Area Scrollingmay be of interest.
文本区域滚动可能很有趣。
I have no idea how the scroll lock key affects it. I found the following from the Wikipedia page on Scroll Lock:
我不知道滚动锁定键如何影响它。我从Scroll Lock的维基百科页面中找到了以下内容:
Therefore, Scroll Lock can be regarded as a defunct feature in almost all modern programs and operating systems.
因此,几乎所有现代程序和操作系统都可以将 Scroll Lock 视为已失效的功能。
So I wouldn't worry about it.
所以我不会担心。
回答by Tim Autin
I needed to do the same for a logging text area. The solutions I found on the web did not worked for me (they either stop auto scrolling when logging to much messages quickly, or they blocked the scrollbar at bottom even if you scroll up whith your mouse wheel).
我需要对日志文本区域做同样的事情。我在网上找到的解决方案对我不起作用(它们要么在快速记录大量消息时停止自动滚动,要么即使您使用鼠标滚轮向上滚动也会阻止底部的滚动条)。
I did it this way :
我是这样做的:
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();
}
});
}
Just use it this way :
只需这样使用它:
makeTextAreaAutoScroll(yourTextArea);
You can test with this piece of code :
您可以使用这段代码进行测试:
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);
Now your text area should auto scroll if the scroll bar is at bottom, stop auto scrolling if you move the scroll bar (by dragging the bar or by using the wheel), and auto scroll again if you put the scroll bar at bottom again.
现在,如果滚动条在底部,您的文本区域应该自动滚动,如果您移动滚动条(通过拖动条或使用滚轮),则停止自动滚动,如果再次将滚动条置于底部,则再次自动滚动。
回答by corochann
After I read Mike Clark and random dude's solution, I end up with below snippet code.
在我阅读了 Mike Clark 和 random 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());
}
}
});
}
The difference is that it is additionally using MouseWheelListener
to update doAutoScroll
flag even if user uses mouse wheel to scroll up and down.
不同之处在于,即使用户使用鼠标滚轮上下滚动,它也额外MouseWheelListener
用于更新doAutoScroll
标志。
回答by BilalDja
Try this :
试试这个 :
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
Hope that helps you
希望能帮到你