Создание JScrollPane автоматически прокручивает все вниз
Я пытаюсь реализовать JScrollPane с JTextArea. JTextArea добавляется, и я хочу, чтобы JScrollPane продолжал прокручиваться вниз по мере добавления текста. Как этого достичь?
9 ответов
Для (что я думаю, что) более простой ответ проверьте: прокрутка области текста.
До JDK5 вам пришлось бы вручную менять позицию каретки после каждого добавления. Теперь вы можете задать это поведение по умолчанию следующим образом:
JTextArea textArea = new JTextArea(); DefaultCaret caret = (DefaultCaret)textArea.getCaret(); caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
Преимущество этого состоит в том, что вам не нужно использовать этот фрагмент более одного раза в своем коде!
Я нашел ответ здесь: JScrollPane и JList с автоматической прокруткой
scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
e.getAdjustable().setValue(e.getAdjustable().getMaximum());
}
});
Если вы постоянно записываете в него данные, вы можете использовать:
textArea.setCaretPosition(textArea.getDocument().getLength());
сразу после добавления новых данных.
Это автоматически прокручивает весь путь вниз по JScorllPane.
Вот решение.
JTextArea textArea = new JTextArea();
DefaultCaret caret = (DefaultCaret)textArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);`
Принятое решение работает хорошо, но только когда текстовая область редактируема, т.е. без jTextArea.setEditable(false)
, Решение, предложенное Krigath, носит более общий характер, но имеет проблему, как указано здесь JScrollPane и 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();
});
Затем панель прокручивается вниз только при расширении вертикальной полосы прокрутки в ответ на добавленные строки текста.
Я допускаю, что можно найти метод фильтрации событий без дополнительных переменных, и был бы признателен, если бы кто-нибудь опубликовал его.
Возможен обходной путь: вы можете объявить этот слушатель как класс, а затем создать его экземпляр для события, где он необходим. После чего вы можете удалить класс после принудительного перекрашивания экрана. Работает как шарм.
Я просмотрел ответы и обнаружил, что ответ @user9999 является хорошим решением для тех, кто хочет, чтобы полоса прокрутки постоянно прокручивалась. Я отредактировал код, избавился от переменной. Это останавливает прокрутку - когда пользователь прокручивает вручную. Если пользователь прокручивает до конца текстовой области или области прокрутки, автоматическая прокрутка продолжается.
(также, как предложил @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
может быть изменен в зависимости от размера шрифта TextArea.
(PS: редактировать для экрана съемки)
import javax.swing.*;
import javax.swing.text.DefaultCaret;
public class NewClass {
public static void main(String[] args) {
JFrame win = new JFrame("test");
win.setSize(800, 500);
win.setResizable(false);
win.setLayout(null);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setLocationRelativeTo(null);
JTextArea txtListNum = new JTextArea();
JScrollPane barList = new JScrollPane(txtListNum);
DefaultCaret crList = (DefaultCaret) txtListNum.getCaret();
crList.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
barList.setLocation(10, 10); // Scrollbar Position
barList.setSize(180, 450); // Scrollbar Dimession
win.add(barList); // Scrolbar add to Frame
win.setVisible(true);
}
}
Будьте осторожны, если собираетесь использовать автоматическую прокрутку в многопоточной программе. Например, если где-то есть такой метод, как
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(...)) вызывается из другого потока. (Особенно, если вы пытаетесь изменить размер окна или пытаетесь прокручивать его, автопрокрутка включена)
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());
}
});
}
Таким образом, вы сможете автоматически прокручивать потокобезопасно!