Java 保持区域滚动位置

Java 保持区域滚动位置,java,user-interface,swing,Java,User Interface,Swing,我有一个JScrollPane,其视图端口设置为JTextArea 我大约每秒更新一次JTextArea上显示的(多行)文本。每次文本更新时,JScrollPane都会一直到文本的底部 相反,我想算出当前显示为原始文本中第一行的行号,并将该行作为文本更新后显示的第一行(或者如果新文本没有那么多行,则一直滚动到底) 我的第一次尝试是获取当前插入符号位置,根据该位置绘制线条,然后设置文本区域以显示该线条: int currentPos = textArea.getCaretPosition(

我有一个JScrollPane,其视图端口设置为JTextArea

我大约每秒更新一次JTextArea上显示的(多行)文本。每次文本更新时,JScrollPane都会一直到文本的底部

相反,我想算出当前显示为原始文本中第一行的行号,并将该行作为文本更新后显示的第一行(或者如果新文本没有那么多行,则一直滚动到底)

我的第一次尝试是获取当前插入符号位置,根据该位置绘制线条,然后设置文本区域以显示该线条:

    int currentPos = textArea.getCaretPosition();
    int currentLine = 0;
    try {
        for(int i = 0; i < textArea.getLineCount(); i++) {
            if((currentPos >= textArea.getLineStartOffset(i)) && (currentPos < gameStateTextArea.getLineEndOffset(i))) {
                currentLine = i;
                break;
            }
        }
    } catch(Exception e) { }

    textArea.setText(text);

    int newLine = Math.min(currentLine, textArea.getLineCount());
    int newOffset = 0;
    try {
        newOffset = textArea.getLineStartOffset(newLine);
    } catch(Exception e) { }

    textArea.setCaretPosition(newOffset);
int currentPos=textArea.getCaretPosition();
int currentLine=0;
试一试{
对于(int i=0;i=textArea.getLineStartOffset(i))&(currentPos
这几乎可以满足我的需要,但需要用户在文本区域内单击以更改插入符号的位置,这样滚动将保持状态(这不好)


我该如何使用(垂直)滚动位置来实现这一点呢?

这是从API文档中拼凑而成的,未经测试:

  • JScrollPane
    上使用
    getViewport()
    获取视口
  • 使用
    Viewport.getViewPosition()
    获取左上角的坐标。这些是绝对值,而不是滚动文本的百分比
  • 使用
    Viewport.addChangeListener()
    在左上角位置发生变化时收到通知(除其他外)。当然,您可能需要创建一种机制来区分用户更改和程序所做的更改
  • 使用
    Viewport.setViewPosition()
    将左上角位置设置为干扰前的位置
更新:

  • 要停止JTextArea的滚动,您可能需要覆盖其
    getScrollableTracksViewport{Height | Width}()
    方法以返回
    false
更新2:

下面的代码符合您的要求。令人惊讶的是,为了让它正常工作,我费了这么多的劲:

  • 显然,
    setViewPosition
    必须使用
    invokeLater
    延迟,因为如果太早进行,文本更新将在它之后进行,并使其效果无效
  • 另外,出于某种奇怪的原因(可能与并发性有关),我必须在构造函数中将正确的值传递给我的
    Runnable
    类。我一直在使用
    orig
    的“全局”实例,并一直将我的位置设置为0,0


我遇到了同样的问题,并发现其中包含了一个很好的解决方案,在这种情况下有效:

DefaultCaret caret = (DefaultCaret) jTextArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);

我试过:Point orig=scrollPane.getViewport().getViewPosition();textArea.setText(text);scrollPane.getViewport().setViewPosition(orig);这似乎对我没有任何帮助。有了这些代码,文本区域每次都会一直滚动到底。(并在swing事件调度线程中运行它)。[叹气]我可以看出我应该对此进行测试。我现在就去做,然后再打给你。谢谢。我在这方面做了一些尝试,但似乎并不需要重写。我认为getViewPosition()返回的点对象是共享的,而不是副本,因此我们需要克隆它,以获得相同的效果:最终点orig=(点)scrollPane.getViewport().getViewPosition().clone();textArea.setText(text);SwingUtilities.invokeLater(){new Runnable(){scrollPane.getViewport().setViewPosition(orig);}}}})。。尽管如此,该解决方案99%的时间都有效,但偶尔在进行快速更新时,它会一直向下滚动。works 99%的时间注释既适用于使用克隆的代码,也适用于您答案中的代码。无论如何,对于我的实际目的来说,它已经足够好了,所以我稍后会接受答案。不管怎样,我仍然对如何让它100%的工作充满好奇。我也有类似的问题。由于某些原因,只有在从分派线程调用
setText
时,文本区域才会滚动到底部。请注意,
setText
是线程安全的。当不使用调度线程时,一切正常。我真的很想理解为什么。我刚刚意识到:JTextArea处理自己的滚动!这可能会破坏我们控制嵌入JScrollPane的努力。好的,我让它工作了。请参阅我的更新2。此解决方案比公认的解决方案简单得多。这是一个更好的答案,它很简单,工作正常
DefaultCaret caret = (DefaultCaret) jTextArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);