2010-01-11 4 views
7

У меня есть JScrollPane с JTextArea, установленным как его порт представления.Maintaing 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. –

+0

Хорошо, у меня все получилось. См. Мое обновление 2. –

ответ

6

Это кусочки, непроверенный, из документации API:

  • использование getViewport() на вашем JScrollPane, чтобы ухватить просмотр.
  • используйте Viewport.getViewPosition(), чтобы получить левые верхние координаты. Это абсолютный, а не процент прокручиваемого текста.
  • использовать Viewport.addChangeListener() чтобы получать уведомление, если позиция сверху слева изменяется (между прочим). Конечно, вы можете создать механизм, чтобы отличать пользовательские изменения от изменений, которые ваша программа делает.
  • использовать Viewport.setViewPosition(), чтобы установить верхнее левое положение туда, где оно было до нарушения.

Update:

  • Чтобы остановить JTextArea прокрутки, вы можете переопределить его getScrollableTracksViewport{Height|Width}() методы для возврата false.

Update 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 (текст); scrollPane.getViewport(). SetViewPosition (orig); Он просто ничего не делает для меня. С помощью этого кода область текста каждый раз прокручивается до самого низа. (И запустив его внутри потока свинг-событий). – Sami

+0

[вздох] Я вижу, что должен был проверить это. Я собираюсь сделать это сейчас и вернуться к вам. –

+0

Спасибо. Я немного поиграл с этим, и, похоже, не требуется переопределение. Я думаю, что объект Point, возвращаемый getViewPosition(), является общим, а не копией, поэтому нам нужно клонировать его, чтобы получить тот же эффект: final Point orig = (Point) scrollPane.getViewport(). GetViewPosition(). Clone (); textArea.setText (текст); SwingUtilities.invokeLater() {new Runnable() {scrollPane.getViewport(). SetViewPosition (orig); }}); .. Сказал, что это решение работает в 99% случаев, но время от времени, при выполнении быстрых обновлений, оно просто прокручивается назад. – Sami

12

я столкнулся с той же проблемой и обнаружил, что this answer включает хорошее решение, которое работает в этом случае:

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

это сработало для меня. – Biscuit128

+0

Но не для меня. –

+0

Работы. Пример: http://tinybrain.de/1001004 –

Смежные вопросы