Java JTextPane + JScrollPane:取消/激活自动滚动

2023-12-09

我目前正在用 Java 编写一个简单的聊天,目前我陷入了这个问题: 我希望我的输出 JTextPane 的行为就像您期望它从良好的聊天中获得的那样,即默认情况下,当新文本到达时文本会自动滚动(使用 outputfield.setCaretPosition(outputdocument.getLength())),但是当用户向上滚动时应该被禁用,当然当用户再次滚动到底部时重新启用。

我尝试摆弄 ScrollBars 等,但我似乎找不到一种方法来检测 ScrollBar 是否位于底部。

我只需创建一个 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()); }
}


Edit:感谢 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();
}

再次感谢您的所有帮助!


如果您只想在底部时滚动,那么这个should help.

使用此方法检查滚动条是否位于末尾(或底部),如果是,则使用自动向下滚动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;
    }

}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java JTextPane + JScrollPane:取消/激活自动滚动 的相关文章

随机推荐

  • PHP:用逗号分隔数组[重复]

    这个问题在这里已经有答案了 我有这段代码 应该显示数组中的值列表 后跟逗号和空格 但是我不希望最后一个后面有逗号和空格 例如我想要tag1 tag2 tag3代替tag1 tag2 tag3 这是我的代码
  • 使用 .net 正则表达式替换字符串中的文本

    我尝试在 net 中使用 regexp 来查找字符串并用某些标记替换字符串 例如 myString 这是我想要更改 和 的文本示例 我如何找到带有 之间标记的文本 并为每个文本执行一些操作来替换它 在数据库中搜索并替换任何找到的匹配项 我想
  • Python 3.x 中的 as 命令有什么作用?

    看过很多次但一直不明白是什么意思as命令在 Python 3 x 中执行 你能用简单的英语解释一下吗 它本身不是命令 而是用作命令一部分的关键字with陈述 with open myfile txt as f text f read 之后的
  • 如何查找使用 TextChanged 添加的文本

    我希望在文本框中的文本和变量中的字符串之间进行同步 我找到了如何获取更改字符串的索引 在文本框中 添加的长度和删除的长度 但如何才能真正找到添加的字符串 到目前为止 我已经使用了 TextChangedEventArgs Changes 并
  • 在 WPF 中动态生成的 DataGrid.Columns 中显示图像

    我必须从查询中转换信息数据 并根据从底层数据库读取的值显示图像 假设我的查询中有这些数据 Identifiant ProcessId AlarmLevel BOUDA25 100 1 BOUDA25 110 1 BOUDA25 130 1
  • 如何在ng-repeat中动态更新ng-model?

    我在我的角度页面中面临动态 ng model 值的一些问题 这是我的示例 JSON mytabs name tab1 values value value1 value value2 value value3 value value4 na
  • 引起原因:使用 lombok 时 java.lang.ClassNotFoundException: com.sun.tools.javac.code.TypeTags

    我在 pom xml 中有以下依赖项
  • Excel VBA ADO SQL - From 子句中的语法错误

    VBA ADO 中的以下 SQL 给出 From 子句中的语法错误 错误 Sub RunSQL2 Dim cn As ADODB Connection Dim rs As ADODB Recordset Dim strFile As Str
  • 如何编辑一篇博客文章而不是其他博客文章的 CSS 以获得 5 星级评级系统?

    我最近创建了我自己的博客使用 Google 的 Blogger 当我读完一本特定的书时 我有一个本书的我读过的部分我想要一个静态的地方5星评级系统代替某种形式 也许与CSS 我可以定位每本书 这样我就能够显示1 至 5 星供访客查看 我不知
  • 将 std::mutex 用于由 boost::asio 管理的线程池

    不知何故的后续这个问题 我只是想知道是否可以使用std mutex在由 a 处理的函数中boost asio io service 使用股线是有点不切实际的 从我在升压参考我会说没关系 既然它指出 异步完成处理程序只会从当前正在调用 io
  • 下单后如何获取盈透证券(IBPY)的交易价格和佣金?

    http interactivebrokers github io tws api 也许是一个有用的链接 这张图片来自盈透证券的java API指南 我想要的数字是交易日志中的价格和佣金 from ib opt import Connect
  • Javascript 检测用户是否更改选项卡

    我正在编写一个用于在线测验的网页 我的基本要求是 如果用户更改选项卡或打开新闻窗口 即使没有最小化其浏览器 即如果该人试图从其他窗口 选项卡查看答案 它也必须触发一个事件 停止测验 我怎样才能做到这一点 Note 尽量避免在您的答案中包含前
  • 如何让 Google Cloud Functions 保持温暖?

    我知道这可能会错过使用 Cloud Functions 的初衷 但在我的具体情况下 我使用 Cloud Functions 是因为这是我将 Next js 与 Firebase Hosting 桥接的唯一方法 我不需要使其具有成本效益 等等
  • 放大和缩小按钮 - highcharts

    我在气泡图中有自定义缩放按钮 放大 缩小 当用户单击 放大 按钮时 应该在图表中从左到右进行缩放 类似地 缩小应该以这种方式发生 这与立即发生缩小的重置缩放功能相反 小提琴链接 https jsfiddle net abcdlearner
  • 将数据发布到 colorbox iframe?

    这是我正在使用的代码 从我见过的其他例子来看 这应该有效 但事实并非如此 并且已经确保我使用的是最新的彩盒 function updateFolderCate ID Type colorbox iframe true scrolling f
  • 如何使用 System.out.printf?

    我的老师希望我们在格式方法 在最底部 中显示我们的值 但问题是我们有一个子程序 她没有向我们展示如何使用它 而且我的老师也没有提供帮助 任何建议或帮助将不胜感激 public class SphereCalculations public
  • Android 增强现实应用程序从屏幕点转换列表位置

    我有问题 找不到解决方案 我制作了一个 Android 增强现实应用程序 在我的应用程序中 我有一个兴趣点列表 每个兴趣点都是一个位置 经度 纬度 海拔 我知道我的位置并且我有 相机的 方位角 现在我的问题是 如何在屏幕上绘制这些点 gps
  • pthread_create 并传递一个整数作为最后一个参数

    我有以下功能 void foo void i int a int i int main pthread t thread int i pthread create thread 0 foo void i 编译时 存在一些关于转换的错误 vo
  • 部署到 IIS 7 时诊断跟踪日志记录不起作用

    我正在将站点从 IIS 6 迁移到 IIS 7 但无法查看所有日志 我的system diagnostics配置如下
  • Java JTextPane + JScrollPane:取消/激活自动滚动

    我目前正在用 Java 编写一个简单的聊天 目前我陷入了这个问题 我希望我的输出 JTextPane 的行为就像您期望它从良好的聊天中获得的那样 即默认情况下 当新文本到达时文本会自动滚动 使用 outputfield setCaretPo