Java JTextPane + JScrollPane: деактивировать автоматическую прокрутку - PullRequest
2 голосов
/ 09 января 2012

В настоящее время я пишу простой чат на Java, и в настоящее время я застрял в этой проблеме: Я хочу, чтобы мой выходной JTextPane вел себя так, как вы ожидаете от хорошего чата, то есть по умолчанию текст прокручивается автоматически при поступлении нового текста (с использованием outputfield.setCaretPosition (outputdocument.getLength ())), но когда пользователь прокручивает это должно быть отключено и, конечно, повторно включено, когда пользователь снова прокручивает вниз.

Я пытался поиграться с полосами прокрутки и всем, но я не могу найти способ определить, находится ли полоса прокрутки внизу или нет.

Я создаю прокручиваемую область вывода, просто создав JTextPane, JScrollPane и вставив одну в другую.

JTextPane outputpane = new JTextPane
...
JScrollPane outputscroll = new JScrollPane(outputpane);
outputscroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

Редактировать: вот код, который я использую для создания компонентов и отображения новых сообщений:

// lots of imports


public class RoomFrame extends JFrame implements ListSelectionListener, ActionListener, Comparable, Runnable
{   
public static final String DEFAULT_FONT = "Courier";
public static final int DEFAULT_FONT_SIZE = 12;

private JTextPane mOutputField;
private JScrollPane mOutputScroll;

private StyledDocument mOutputDocument;


public RoomFrame(...)
{
    super(...);

    createGUI();
    setOutputStyles();
    setVisible(true);

    new Thread(this).start();
}

// ========================================================

private void createGUI()
{
    Color borderhighlightouter = new Color(220, 220, 220);
    Color borderhighlightinner = new Color(170, 170, 170);
    Color bordershadowouter = new Color(120, 120, 120);
    Color bordershadowinner = new Color(170, 170, 170);

    setLayout(new GridBagLayout());
    setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    setSize(500, 400);


    // -----------------------------
    // Output
    mOutputField = new JTextPane();
    mOutputField.setEditable(false);
    mOutputField.setBackground(new Color(245, 245, 245));
    mOutputDocument = mOutputField.getStyledDocument();

    mOutputScroll = new JScrollPane(mOutputField);

    mOutputScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    mOutputScroll.setPreferredSize(new Dimension(250, 250));
    mOutputScroll.setMinimumSize(new Dimension(50, 50));
    mOutputScroll.setOpaque(false);
    mOutputScroll.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 1, 0), BorderFactory.createBevelBorder(BevelBorder.LOWERED, borderhighlightouter, borderhighlightinner, bordershadowouter, bordershadowinner)));

    getContentPane().add(mOutputScroll);
}

private void setOutputStyles()
{
    Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);

    Style regular = mOutputDocument.addStyle("regular", def);
    StyleConstants.setFontFamily(def, DEFAULT_FONT);
    StyleConstants.setFontSize(def, DEFAULT_FONT_SIZE);
    StyleConstants.setFirstLineIndent(def, 100.0f);

    Style nickname = mOutputDocument.addStyle("username", def);
    StyleConstants.setBold(nickname, true);
    StyleConstants.setForeground(nickname, new Color(50, 50, 220));

    Style highlight = mOutputDocument.addStyle("highlight", def);
    StyleConstants.setBold(highlight, true);
    StyleConstants.setBackground(highlight, new Color(150, 0, 0));

    Style join = mOutputDocument.addStyle("join", def);
    StyleConstants.setBold(join, true);
    StyleConstants.setForeground(join, new Color(20, 100, 20));

    Style leave = mOutputDocument.addStyle("leave", def);
    StyleConstants.setBold(leave, true);
    StyleConstants.setForeground(leave, new Color(100, 100, 20));

    Style topic = mOutputDocument.addStyle("topic", def);
    StyleConstants.setBold(topic, true);
    StyleConstants.setUnderline(topic, true);

    Style error = mOutputDocument.addStyle("error", def);
    StyleConstants.setBold(error, true);
    StyleConstants.setForeground(error, new Color(255, 0, 0));

    Style kick = mOutputDocument.addStyle("kick", def);
    StyleConstants.setBold(kick, true);
    StyleConstants.setForeground(kick, new Color(150, 0, 0));
}

private final boolean shouldScroll()
{
    int min = mOutputScroll.getVerticalScrollBar().getValue() + mOutputScroll.getVerticalScrollBar().getVisibleAmount();
    int max = mOutputScroll.getVerticalScrollBar().getMaximum();

    return min == max;
}

// ========================================================

public void displayMessage(String message)
{
    displayMessage(message, "");
}

public void displayMessage(String message, String style)
{
    displayMessage(message, style, true);
}

public void displayMessage(String message, String style, boolean addnewline)
{
    String newline = (addnewline ? "\n" : "");

    style = (style.equals("") ? "regular" : style);
    message = message.replace("\n", " ");

    // check for highlight

    try
    {
        mOutputDocument.insertString(mOutputDocument.getLength(),
                                        String.format("%s%s", message, newline),
                                        mOutputDocument.getStyle(style));
    }
    catch (Exception e) {}

    // if (shouldScroll())
    //  mOutputField.setCaretPosition(mOutputDocument.getLength());
}

public void run()
{
    while (true)
    {
        if (shouldScroll())
        {
            SwingUtilities.invokeLater(
                new Runnable()
                {
                    public void run()
                    {
                        mOutputScroll.getVerticalScrollBar().setValue(mOutputScroll.getVerticalScrollBar().getMaximum());
                    }
                });
        }

        try { Thread.sleep(500); }
        catch (InterruptedException e) { break; }
    }
}

public void valueChanged(ListSelectionEvent event)
{

}

public void actionPerformed(ActionEvent event)
{

}

public final int compareTo(Object o) { return this.toString().compareTo(o.toString()); }
}


Редактировать: Благодаря ссылке fireshadow52 на еще один похожий вопрос Я наконец-то заставил его работать именно так, как я хочу:

private boolean isViewAtBottom()
{
    JScrollBar sb = mOutputScroll.getVerticalScrollBar();
    int min = sb.getValue() + sb.getVisibleAmount();
    int max = sb.getMaximum();
    System.out.println(min + " " + max);
    return min == max;
}

private void scrollToBottom()
{
    SwingUtilities.invokeLater(
        new Runnable()
        {
            public void run()
            {
                mOutputScroll.getVerticalScrollBar().setValue(mOutputScroll.getVerticalScrollBar().getMaximum());
            }
        });
}

public void displayMessage(String message, String style, boolean prependnewline)
{
    String newline = (prependnewline ? "\n" : "");
    boolean scroll = isViewAtBottom() && prependnewline;

    style = (style.equals("") ? "regular" : style);
    message = message.replace("\n", " ");

    try
    {
        mOutputDocument.insertString(mOutputDocument.getLength(),
                                        String.format("%s%s", newline, message),
                                        mOutputDocument.getStyle(style));
    }
    catch (Exception e) {}

    if (scroll)
        scrollToBottom();
}

Опять же, спасибо за вашу помощь!

Ответы [ 3 ]

2 голосов
/ 09 января 2012

Если вы хотите прокручивать только когда находитесь внизу, тогда это должно помочь.

Используйте этот метод, чтобы проверить, находится ли полоса прокрутки в конце (или внизу), и если это так, прокрутите вниз автоматически, используя setCaretPosition(Component.getDocument().getLength());:

public boolean shouldScroll() {
    int minimumValue = scrollPane.getVerticalScrollBar().getValue() + scrollPane.getVerticalScrollBar().getVisibleAmount();
    int maximumValue = scrollPane.getVerticalScrollBar().getMaximum();
    return maximumValue == minimumValue;
}

Я нашел похожие результаты при использовании Google, который привел меня к методу, подобному этому, который работал в соответствии с запросом.

Редактировать: убедитесь, что это сделано в invokeLater(), поскольку его необходимо обновить, прежде чем выполнять прокрутку.

Полный пример того, что я использую:

public class ScrollTest extends Thread {

    private JFrame frame;

    private JTextArea textArea;

    private JScrollPane scrollPane;

    public static void main(String[] arguments) {
            new ScrollTest().run();
    }

    public ScrollTest() {
            textArea = new JTextArea(20, 20);
            scrollPane = new JScrollPane(textArea);

            frame = new JFrame("Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(scrollPane);
            frame.pack();
            frame.setVisible(true);
    }

    public void run() {
            while (true) {
                    textArea.append("" + Math.random() + "\n");
                    if (shouldScroll()) {
                            SwingUtilities.invokeLater(new Runnable() {

                                    @Override
                                    public void run() {
                                            scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getMaximum());
                                    }

                            });
                    }
                    try {
                            Thread.sleep(500);
                    } catch (Exception exception) {
                            exception.printStackTrace();
                    }
            }
    }

    public boolean shouldScroll() {
            int minimumValue = scrollPane.getVerticalScrollBar().getValue() + scrollPane.getVerticalScrollBar().getVisibleAmount();
            int maximumValue = scrollPane.getVerticalScrollBar().getMaximum();
            return maximumValue == minimumValue;
    }

}

1 голос
/ 09 января 2012

Вы можете использовать AdjustmentListener для прокрутки состояния, как показано в этом примере .

1 голос
/ 09 января 2012

JScrollBar имеет метод getValue (). Просто деформируйте его в SwingUtilities.invokeLater (), который будет вызываться после добавления текста.

...