维护JTextArea滚动位置

Sam*_*ami 7 java user-interface swing

我有一个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);
Run Code Online (Sandbox Code Playgroud)

这几乎可以满足我的需求,但要求用户在文本区域内单击以更改插入位置,以便滚动将保持状态(这不是很好).

我如何使用(垂直)滚动位置来代替?

bdu*_*riu 13

我遇到了同样的问题,发现这个答案包含一个很好的解决方案,适用于这种情况:

DefaultCaret caret = (DefaultCaret) jTextArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
Run Code Online (Sandbox Code Playgroud)


Car*_*icz 6

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

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

更新:

  • 要阻止JTextArea滚动,您可能希望覆盖其getScrollableTracksViewport{Height|Width}()返回的方法false.

更新2:

以下代码可以满足您的需求.令人惊讶的是,我必须付出多少努力才能让它发挥作用:

  • 显然setViewPosition必须推迟使用,invokeLater因为如果它太早完成,文本更新将在它之后,并使其效果无效.
  • 另外,由于一些奇怪的原因可能与并发有关,我必须Runnable在构造函数中将正确的值传递给我的类.我一直在使用"全局"实例orig并且一直将我的位置设置为0,0.

public class Sami extends JFrame implements ActionListener {

   public static void main(String[] args) {
      (new Sami()).setVisible(true);
   }

   private JTextArea textArea;
   private JScrollPane scrollPane;
   private JButton moreTextButton = new JButton("More text!");
   private StringBuffer text = new StringBuffer("0 Silly random text.\n");
   private Point orig = new Point(0, 0);

   public Sami() {
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      getContentPane().setLayout(new BorderLayout());
      this.textArea = new JTextArea() {
         @Override
         public boolean getScrollableTracksViewportHeight() {
            return false;
         }
         @Override
         public boolean getScrollableTracksViewportWidth() {
            return false;
         }
      };
      this.scrollPane = new JScrollPane(this.textArea);
      getContentPane().add(this.scrollPane, BorderLayout.CENTER);
      this.moreTextButton.addActionListener(this);
      getContentPane().add(this.moreTextButton, BorderLayout.SOUTH);
      setSize(400, 300);
   }

   @Override
   public void actionPerformed(ActionEvent arg0) {
      int lineCount = this.text.toString().split("[\\r\\n]").length;
      this.text.append(lineCount + "The quick brown fox jumped over the lazy dog.\n");
      Point orig = this.scrollPane.getViewport().getViewPosition();
      // System.out.println("Orig: " + orig);
      this.textArea.setText(text.toString());
      SwingUtilities.invokeLater(new LaterUpdater(orig));
   }

   class LaterUpdater implements Runnable {
      private Point o;
      public LaterUpdater(Point o) {
         this.o = o;
      }
      public void run() {
         // System.out.println("Set to: " + o);
         Sami.this.scrollPane.getViewport().setViewPosition(o);
      }
   } 

}
Run Code Online (Sandbox Code Playgroud)