Создание 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());
            }
        });
 }

Таким образом, вы сможете автоматически прокручивать потокобезопасно!

Другие вопросы по тегам