2010-01-11 110 views
7

我有一個JScrollPane,其JTextArea集作爲其視圖端口。維護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); 

這是我的需求幾乎是可以接受的,但要求用戶在文本區域內單擊以更改插入位置,以便滾動將保持狀態(這不太好)。

我該如何使用(垂直)滾動位置來代替?

+0

我才意識到:JTextArea中處理自己的滾動!這可能破壞了我們控制嵌入JScrollPane的努力。 – 2010-01-12 18:42:01

+0

好的,我得到了它的工作。查看我的更新2. – 2010-01-12 19:32:01

回答

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); 
     } 
    } 

} 
+0

我試過了: Point orig = scrollPane.getViewport()。getViewPosition(); textArea.setText(text); scrollPane.getViewport()。setViewPosition(orig); 它似乎沒有爲我做任何事情。通過該代碼,文本區域每次都會滾動到底部。 (並在swing事件調度線程中運行它)。 – Sami 2010-01-12 16:03:21

+0

[嘆氣]我可以看到我應該測試一下。我現在要做的是回到你身邊。 – 2010-01-12 18:35:28

+0

謝謝。我玩了這一點,似乎並不需要壓倒一切。我認爲由getViewPosition()返回的Point對象是共享的,而不是副本,所以我們需要克隆它,以獲得相同的效果:final Point orig =(Point)scrollPane.getViewport()。getViewPosition()。clone ); textArea.setText(文本); SwingUtilities.invokeLater(){new Runnable(){scrollPane.getViewport()。setViewPosition(orig); }}); ..說,這個解決方案工作99%的時間,但偶爾,當做快速更新時,它只是向下滾動回來。 – Sami 2010-01-12 20:01:46

12

我遇到同樣的問題,發現this answer包括很好的解決方案,在這種情況下工作:

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

這對我有效。 – Biscuit128 2012-01-31 13:33:49

+0

但不適合我。 – 2013-12-30 13:42:11

+0

作品。例如:http://tinybrain.de/1001004 – 2015-09-15 23:04:03