智能 JScrollPane 自动滚动

发布于 2024-09-12 22:24:18 字数 2762 浏览 1 评论 0原文

我正在尝试在包含 JTextPane 的 JScrollPane 上实现智能自动滚动。 JTextPane 用于以颜色记录我的应用程序。然而,我在尝试进行智能自动滚动时遇到了困难。我所说的智能自动滚动并不是指每次发生变化时都盲目地自动滚动,我的意思是检查您是否一直向下滚动,然后自动滚动。但是,无论我做什么,它要么总是自动滚动,要么根本不滚动

作为测试脚本,这是设置(JFrame 已被省略)

final JTextPane textPane = new JTextPane();
textPane.setEditable(false);
final JScrollPane contentPane = new JScrollPane(textPane);
contentPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

这是丑陋的自动添加测试循环

while (true)
    try {
        Thread.sleep(1000);
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JScrollBar scrollBar = scroll;
                    boolean preCheck = ((scrollBar.getVisibleAmount() != scrollBar.getMaximum()) && (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum()));
                    System.out.println("Value: " + scroll.getValue()
                                    + " | Visible: " + scrollBar.getVisibleAmount()
                                    + " | Maximum: " + scrollBar.getMaximum()
                                    + " | Combined: " + (scrollBar.getValue() + scrollBar.getVisibleAmount())
                                    + " | Vis!=Max : " + (scrollBar.getVisibleAmount() != scrollBar.getMaximum())
                                    + " | Comb=Max: " + (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum())
                                    + " | Eval: " + preCheck);
                    StyledDocument doc = textPane.getStyledDocument();
                    doc.insertString(doc.getLength(), "FAGAHSIDFNJASDKFJSD\n", doc.getStyle(""));
                    if (!preCheck)
                            textPane.setCaretPosition(doc.getLength());
                } catch (BadLocationException ex) {
                            ex.printStackTrace();
                }
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }

它不漂亮,但它完成了工作。

这是相关的检查

 boolean preCheck = ((scrollBar.getVisibleAmount() != scrollBar.getMaximum()) && (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum()));
 if (preCheck)
     textPane.setCaretPosition(doc.getLength());

,这就是给我带来麻烦的部分。首先检查条形图是否可见但不可用(文本不足,使条形图达到完整长度),然后检查条形图的底部是否等于最大值。从理论上讲,这应该可行。然而,没有任何事情,包括移动支票,都没有得到我想要的结果。

有什么建议吗?

不是 这个,因为他们希望如此始终滚动,而不仅仅是有时。

I am attempting to implmement smart autoscrolling on a JScrollPane containing a JTextPane. The JTextPane is used for logging my app in color. However I'm running into a wall trying to do smart autoscrolling. By smart autoscrolling I don't mean blindly autoscrolling every time something changes, I mean checking to see if your scrolled all the way down, then autoscroll. However no matter what I do it either always autoscrolls or doesn't at all

As a test script, here's the setup (the JFrame has been left out)

final JTextPane textPane = new JTextPane();
textPane.setEditable(false);
final JScrollPane contentPane = new JScrollPane(textPane);
contentPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

And here's the ugly auto add test loop

while (true)
    try {
        Thread.sleep(1000);
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JScrollBar scrollBar = scroll;
                    boolean preCheck = ((scrollBar.getVisibleAmount() != scrollBar.getMaximum()) && (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum()));
                    System.out.println("Value: " + scroll.getValue()
                                    + " | Visible: " + scrollBar.getVisibleAmount()
                                    + " | Maximum: " + scrollBar.getMaximum()
                                    + " | Combined: " + (scrollBar.getValue() + scrollBar.getVisibleAmount())
                                    + " | Vis!=Max : " + (scrollBar.getVisibleAmount() != scrollBar.getMaximum())
                                    + " | Comb=Max: " + (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum())
                                    + " | Eval: " + preCheck);
                    StyledDocument doc = textPane.getStyledDocument();
                    doc.insertString(doc.getLength(), "FAGAHSIDFNJASDKFJSD\n", doc.getStyle(""));
                    if (!preCheck)
                            textPane.setCaretPosition(doc.getLength());
                } catch (BadLocationException ex) {
                            ex.printStackTrace();
                }
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }

Its not pretty, but it gets the job done.

Here's though the relevant check

 boolean preCheck = ((scrollBar.getVisibleAmount() != scrollBar.getMaximum()) && (scrollBar.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum()));
 if (preCheck)
     textPane.setCaretPosition(doc.getLength());

Thats the part thats been giving me trouble. There is first a check to see if the bar is visible but unusable (not enough text, making the bar the full length), then if the bottom of the bar is equal to the maximum. In theory, that should work. However nothing, including moving the check around, has gotten the results I would like.

Any suggestions?

NOT A DUPLICATE of this or this, as they are wanting it to always scroll, not just sometimes.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

丢了幸福的猪 2024-09-19 22:24:18

编辑:

我用更灵活的版本替换了以下代码,该版本适用于 JScrollPane 中的任何组件。查看:智能滚动

import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import javax.swing.*;
import javax.swing.text.*;

public class ScrollControl implements AdjustmentListener
{
    private JScrollBar scrollBar;
    private JTextComponent textComponent;
    private int previousExtent = -1;

    public ScrollControl(JScrollPane scrollPane)
    {
        Component view = scrollPane.getViewport().getView();

        if (! (view instanceof JTextComponent))
            throw new IllegalArgumentException("Scrollpane must contain a JTextComponent");

        textComponent = (JTextComponent)view;

        scrollBar = scrollPane.getVerticalScrollBar();
        scrollBar.addAdjustmentListener( this );
    }

    @Override
    public void adjustmentValueChanged(final AdjustmentEvent e)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                checkScrollBar(e);
            }
        });
    }

    private void checkScrollBar(AdjustmentEvent e)
    {
        //  The scroll bar model contains information needed to determine the
        //  caret update policy.

        JScrollBar scrollBar = (JScrollBar)e.getSource();
        BoundedRangeModel model = scrollBar.getModel();
        int value = model.getValue();
        int extent = model.getExtent();
        int maximum = model.getMaximum();
        DefaultCaret caret = (DefaultCaret)textComponent.getCaret();

        //  When the size of the viewport changes there is no need to change the
        //  caret update policy.

        if (previousExtent != extent)
        {
            //  When the height of a scrollpane is decreased the scrollbar is
            //  moved up from the bottom for some reason. Reposition the
            //  scrollbar at the bottom

            if (extent < previousExtent
            &&  caret.getUpdatePolicy() == DefaultCaret.UPDATE_WHEN_ON_EDT)
            {
                scrollBar.setValue( maximum );
            }

            previousExtent = extent;
            return;
        }

        //  Text components will not scroll to the bottom of a scroll pane when
        //  a bottom inset is used. Therefore the location of the scrollbar,
        //  the height of the viewport, and the bottom inset value must be
        //  considered when determining if the scrollbar is at the bottom.

        int bottom = textComponent.getInsets().bottom;

        if (value + extent + bottom < maximum)
        {
            if (caret.getUpdatePolicy() != DefaultCaret.NEVER_UPDATE)
                caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
        }
        else
        {
            if (caret.getUpdatePolicy() != DefaultCaret.UPDATE_WHEN_ON_EDT)
            {
                caret.setDot(textComponent.getDocument().getLength());
                caret.setUpdatePolicy(DefaultCaret.UPDATE_WHEN_ON_EDT);
            }
        }
    }

    private static void createAndShowUI()
    {
        JPanel center = new JPanel( new GridLayout(1, 2) );
        String text = "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n";

        final JTextArea textArea = new JTextArea();
        textArea.setText( text );
        textArea.setEditable( false );
        center.add( createScrollPane( textArea ) );
        System.out.println(textArea.getInsets());

        final JTextPane textPane = new JTextPane();
        textPane.setText( text );
        textPane.setEditable( false );
        center.add( createScrollPane( textPane )  );
        textPane.setMargin( new Insets(5, 3, 7, 3) );
        System.out.println(textPane.getInsets());

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(center, BorderLayout.CENTER);
        frame.setSize(500, 200);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        Timer timer = new Timer(2000, new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                try
                {
                    Date now = new Date();
                    textArea.getDocument().insertString(textArea.getDocument().getLength(), "\n" + now.toString(), null);
                    textPane.getDocument().insertString(textPane.getDocument().getLength(), "\n" + now.toString(), null);
                }
                catch (BadLocationException e1) {}
            }
        });
        timer.start();
    }

    private static JComponent createScrollPane(JComponent component)
    {
        JScrollPane scrollPane = new JScrollPane(component);
        new ScrollControl( scrollPane );

        return scrollPane;
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}

Edit:

I replaced the following code with a more flexible version that will work on any component in a JScrollPane. Check out: Smart Scrolling.

import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import javax.swing.*;
import javax.swing.text.*;

public class ScrollControl implements AdjustmentListener
{
    private JScrollBar scrollBar;
    private JTextComponent textComponent;
    private int previousExtent = -1;

    public ScrollControl(JScrollPane scrollPane)
    {
        Component view = scrollPane.getViewport().getView();

        if (! (view instanceof JTextComponent))
            throw new IllegalArgumentException("Scrollpane must contain a JTextComponent");

        textComponent = (JTextComponent)view;

        scrollBar = scrollPane.getVerticalScrollBar();
        scrollBar.addAdjustmentListener( this );
    }

    @Override
    public void adjustmentValueChanged(final AdjustmentEvent e)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                checkScrollBar(e);
            }
        });
    }

    private void checkScrollBar(AdjustmentEvent e)
    {
        //  The scroll bar model contains information needed to determine the
        //  caret update policy.

        JScrollBar scrollBar = (JScrollBar)e.getSource();
        BoundedRangeModel model = scrollBar.getModel();
        int value = model.getValue();
        int extent = model.getExtent();
        int maximum = model.getMaximum();
        DefaultCaret caret = (DefaultCaret)textComponent.getCaret();

        //  When the size of the viewport changes there is no need to change the
        //  caret update policy.

        if (previousExtent != extent)
        {
            //  When the height of a scrollpane is decreased the scrollbar is
            //  moved up from the bottom for some reason. Reposition the
            //  scrollbar at the bottom

            if (extent < previousExtent
            &&  caret.getUpdatePolicy() == DefaultCaret.UPDATE_WHEN_ON_EDT)
            {
                scrollBar.setValue( maximum );
            }

            previousExtent = extent;
            return;
        }

        //  Text components will not scroll to the bottom of a scroll pane when
        //  a bottom inset is used. Therefore the location of the scrollbar,
        //  the height of the viewport, and the bottom inset value must be
        //  considered when determining if the scrollbar is at the bottom.

        int bottom = textComponent.getInsets().bottom;

        if (value + extent + bottom < maximum)
        {
            if (caret.getUpdatePolicy() != DefaultCaret.NEVER_UPDATE)
                caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
        }
        else
        {
            if (caret.getUpdatePolicy() != DefaultCaret.UPDATE_WHEN_ON_EDT)
            {
                caret.setDot(textComponent.getDocument().getLength());
                caret.setUpdatePolicy(DefaultCaret.UPDATE_WHEN_ON_EDT);
            }
        }
    }

    private static void createAndShowUI()
    {
        JPanel center = new JPanel( new GridLayout(1, 2) );
        String text = "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n";

        final JTextArea textArea = new JTextArea();
        textArea.setText( text );
        textArea.setEditable( false );
        center.add( createScrollPane( textArea ) );
        System.out.println(textArea.getInsets());

        final JTextPane textPane = new JTextPane();
        textPane.setText( text );
        textPane.setEditable( false );
        center.add( createScrollPane( textPane )  );
        textPane.setMargin( new Insets(5, 3, 7, 3) );
        System.out.println(textPane.getInsets());

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(center, BorderLayout.CENTER);
        frame.setSize(500, 200);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        Timer timer = new Timer(2000, new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                try
                {
                    Date now = new Date();
                    textArea.getDocument().insertString(textArea.getDocument().getLength(), "\n" + now.toString(), null);
                    textPane.getDocument().insertString(textPane.getDocument().getLength(), "\n" + now.toString(), null);
                }
                catch (BadLocationException e1) {}
            }
        });
        timer.start();
    }

    private static JComponent createScrollPane(JComponent component)
    {
        JScrollPane scrollPane = new JScrollPane(component);
        new ScrollControl( scrollPane );

        return scrollPane;
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文