Как сделать JTextPane автопрокруткой только тогда, когда полоса прокрутки внизу и блокировка прокрутки отключена? - PullRequest
10 голосов
/ 28 октября 2010

Как сделать автоматическую прокрутку JTextPane, только когда полоса прокрутки внизу и блокировка прокрутки отключена?Это не должно иметь ничего общего с каретой, которую я, похоже, нахожу во всем Google.(

Ответы [ 6 ]

13 голосов
/ 29 октября 2010

Я думаю, что моя программа ниже точно соответствует вашим требованиям, с одной возможной оговоркой: вам не разрешено печатать в текстовой области.Так что это было бы хорошо для просмотра журнала, но не для интерактивной консоли.Код выполняется немного долго, потому что я превратил его в готовую к запуску демонстрацию этого подхода.Я предлагаю запустить программу как есть и проверить поведение.Если поведение работает хорошо для вас, потратьте немного времени на изучение кода.Я включил в код комментарии, чтобы выделить некоторые из наиболее важных разделов.


Обновление 2013-07-17: Вы также можете проверить решение random dude в своем отдельном ответе дальше вниз по странице.Его подход более элегантен, чем мой.

Также см. 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);
            }
        });
    }
}
10 голосов
/ 16 марта 2011

Немного опоздал на этот вопрос, но я придумал это решение.

  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());

     }
  });   

Кажется, идеально подходит для моих нужд. Небольшое объяснение: по сути, если полоса прокрутки не перемещалась человеком, и полоса была последней на максимуме / дне, тогда сбросьте ее на максимум. Если он настраивается вручную, проверьте, не отрегулирован ли он снизу.

3 голосов
/ 29 октября 2010

Прокрутка текстовой области может представлять интерес.

Понятия не имею, как на это влияет кнопка блокировки прокрутки. На странице Scroll Lock я нашел следующее:

Таким образом, блокировку прокрутки можно рассматривать как неработающую функцию практически во всех современных программах и операционных системах.

Так что я бы не волновался об этом.

1 голос
/ 14 мая 2014

Мне нужно было сделать то же самое для области ввода текста. Решения, которые я нашел в Интернете, не работали для меня (они либо прекращали автоматическую прокрутку при быстрой записи большого количества сообщений, либо блокировали полосу прокрутки внизу, даже если вы прокручивали вверх с помощью колесика мыши).

Я сделал это так:

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);

Теперь ваша текстовая область должна автоматически прокручиваться, если полоса прокрутки находится внизу, прекратить автоматическую прокрутку, если вы перемещаете полосу прокрутки (перетаскивая полосу или используя колесо), и снова автоматически прокручивать, если вы устанавливаете полосу прокрутки снова дно.

0 голосов
/ 09 сентября 2016

После прочтения решения Майка Кларка и случайного чувака я получаю приведенный ниже фрагмент кода.

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 даже если пользовательиспользует колесо мыши для прокрутки вверх и вниз.

0 голосов
/ 10 ноября 2013

Попробуйте это:

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

Надеюсь, что вам поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...