Как сделать автопрокрутку JTextPane только тогда, когда полоса прокрутки находится внизу, а блокировка прокрутки выключена? Это не должно иметь никакого отношения к каретке, и это то, что я, кажется, нахожу во всем Google.: (
Как сделать автосканирование JTextPane только тогда, когда полоса прокрутки находится внизу, а блокировка прокрутки выключена?
Ответ 1
Я думаю, что моя программа ниже соответствует вашим требованиям именно с одним возможным предостережением: вам не разрешено вводить текстовую область. Таким образом, это было бы полезно для просмотра журналов, но не для интерактивной консоли. Код работает немного долго, потому что я превратил его в готовое к запуску демонстрацию подхода. Я предлагаю запустить программу как есть и проверить поведение. Если поведение работает хорошо для вас, тогда потратьте немного времени на изучение кода. Я включил комментарии в код, чтобы выделить некоторые из наиболее важных разделов.
Обновление 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 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);
}
});
}
}
Ответ 2
Немного поздно к этому вопросу, но я придумал это решение.
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());
}
});
Кажется, отлично работает для моих нужд. Маленькое объяснение: по существу, если полоса прокрутки не перемещается человеком, а панель была последней на максимуме/внизу, а затем reset до максимума. Если он отрегулирован вручную, проверьте, настроено ли оно внизу.
Ответ 3
Прокрутка текстовой области может представлять интерес.
Я понятия не имею, как влияет на него ключ блокировки прокрутки. Я нашел следующее на странице Википедии Scroll Lock:
Следовательно, Scroll Lock можно рассматривать как несуществующую функцию почти во всех современных программах и операционных системах.
Поэтому я бы не стал беспокоиться об этом.
Ответ 4
Мне нужно было сделать то же самое для области ввода журнала. Решения, которые я нашел в Интернете, не работали для меня (они либо останавливают автоматическую прокрутку при быстром входе в большое количество сообщений, либо блокируют полосу прокрутки внизу, даже если вы прокручиваете колесико мыши).
Я сделал это следующим образом:
public static void makeTextAreaAutoScroll(JTextArea textArea) {
// Get the text area 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);
Теперь ваша текстовая область должна автоматически прокручиваться, если полоса прокрутки находится внизу, остановка автоматической прокрутки, если вы перемещаете полосу прокрутки (путем перетаскивания панели или с помощью колеса) и автоматически прокручиваете, если вы поместите полосу прокрутки в внизу снова.
Ответ 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
Надеюсь, что поможет вам
Ответ 6
После того, как я прочитал 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
, даже если пользователь использует колесико мыши для прокрутки вверх и вниз.