13

私は、Adium や私が見た他のほとんどのチャット クライアントの機能を模倣しようとしています。この機能では、新しいメッセージが着信するとスクロールバーが一番下に進みますが、それは既にそこにいる場合のみです。言い換えれば、数行上にスクロールして読んでいる場合、新しいメッセージが入ってきても、あなたの位置が画面の一番下にジャンプすることはありません。それは迷惑だろう。しかし、一番下までスクロールすると、常に最新のメッセージを表示したいとプログラムが想定し、それに応じて自動スクロールします。

私はこれを真似しようとしてしばらく苦労しました。プラットフォームは、あらゆる犠牲を払ってこの動作と戦っているようです。私ができる最善のことは次のとおりです。

コンストラクターで:

JTextArea chatArea = new JTextArea();
JScrollPane chatAreaScrollPane = new JScrollPane(chatArea);

// We will manually handle advancing chat window
DefaultCaret caret = (DefaultCaret) chatArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);

入ってくる新しいテキストを処理するメソッド:

boolean atBottom = isViewAtBottom();

// Append the text using styles etc to the chatArea

if (atBottom) {
    scrollViewportToBottom();
} 


public boolean isAtBottom() {
    // Is the last line of text the last line of text visible?
    Adjustable sb = chatAreaScrollPane.getVerticalScrollBar();

    int val = sb.getValue();
    int lowest = val + sb.getVisibleAmount();
    int maxVal = sb.getMaximum();

    boolean atBottom = maxVal == lowest;
    return atBottom;
}


private void scrollToBottom() {
    chatArea.setCaretPosition(chatArea.getDocument().getLength());
}

さて、これは機能しますが、2 つの理由からぎこちなく、理想的ではありません。

  1. キャレットの位置を設定すると、ユーザーがチャット領域で行った選択がすべて消去されます。彼がコピー/貼り付けをしようとしている場合、これは非常にイライラするだろうと想像できます.
  2. スクロール ペインの進行はテキストが挿入された後に発生するため、スクロールバーが間違った位置にある一瞬があり、その後視覚的に最後に向かってジャンプします。これは理想的ではありません。

あなたが尋ねる前に、はい、私はこのブログ投稿を読みましたText Area Scrollingですが、デフォルトのスクロールから下への動作は私が望むものではありません。

その他の関連する (ただし、私の考えでは、この点で完全に役立つわけではありません) 質問: jscrollpane にスクロール バーを設定 する

この点で何か助けていただければ幸いです。

編集:

Devon_C_Miller のアドバイスに従って、一番下までスクロールする方法が改善され、問題 #1 が解決されました。

private void scrollToBottom() {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
       public void run() {
           try {
               int endPosition = chatArea.getDocument().getLength();
               Rectangle bottom = chatArea.modelToView(endPosition);
               chatArea.scrollRectToVisible(bottom);
           }
           catch (BadLocationException e) {
               System.err.println("Could not scroll to " + e);
           }
       }
    });
}

私はまだ問題#2を持っています。

4

7 に答える 7

7

scrollRectToVisible(Rectangle r)modelToView(int pos)を見てください

これにより、ユーザーの選択を妨げることなく、探しているものが得られるはずです。

スクロールバーについては、スクロールと追加を別のイベントで強制的に発生させてみてください。例えば:

if (atBottom) {
    // append new line & do scroll
    SwingUtilities.invokerLater(new Runnable(){
        public void run() {
            // append text
        }});
} else {
    // append newline
    // append text
}
于 2010-04-19T20:38:43.310 に答える
4

以前の回答とさらに Google の作業に基づいて、スレッドが JScrollPane の JTextArea に文字列を継続的に追加するテストを作成しました。この値にスクロールする前に、新しい最大値で JScrollBar を更新する必要があるため、ここで invokeLater を使用することが重要だと思います。これは、invokeLater を使用して AWT スレッドが処理します。

private class AppendThread extends Thread {
    public void run() {
      while (true) {
        String s = "random number = " + Math.random() + "\n";
        boolean scrollDown = textAreaBottomIsVisible();
        textArea.append(s);
        if (scrollDown) {
          javax.swing.SwingUtilities.invokeLater(new Runnable() {
                  public void run() {
                      JScrollBar bar = scrollPane.getVerticalScrollBar();
                      bar.setValue(bar.getMaximum());
                  }
              }
          );
        }
        try {
          Thread.sleep(1000);
        } catch (Exception e) {
          System.err.println("exception = " + e);
        }
      }
    }

    private boolean textAreaBottomIsVisible() {
      Adjustable sb = scrollPane.getVerticalScrollBar();
      int val = sb.getValue();
      int lowest = val + sb.getVisibleAmount();
      int maxVal = sb.getMaximum();
      boolean atBottom = maxVal == lowest;
      return atBottom;
    }
}
于 2011-12-14T13:30:37.997 に答える
1

私はここ数日、この問題に対するさまざまなアプローチを試みてきました。私が見つけた最善の解決策は、JScrollPane のビューポートのレイアウト マネージャーを置き換えて、必要なすべてのスクロール動作を実装することです。ジャンプするスクロールバーのノブがなくなり、非常に高速になりました。非常に高速であるため、別の競合状態を回避する必要がありました。詳細はこちら: http://www.java-forums.org/awt-swing/56808-scroll-bar-knob-jumpy-when-component-growing.html

于 2012-03-19T04:29:54.877 に答える
1

この例をチェックしてください。

ただし、より効率的な次のコードを使用するようにコードを変更します。

caret.setDot(textArea.getDocument().getLength());
于 2010-04-19T20:50:39.073 に答える
0
public static void scrollToBottom(JComponent component) 
{
    Rectangle visibleRect = component.getVisibleRect();
    visibleRect.y = component.getHeight() - visibleRect.height;
    component.scrollRectToVisible(visibleRect);
}

ここで完全な詳細を見つけることができます: How to make JTextPane autoscroll only when scroll bar is at below and scroll lock is off?

于 2012-11-28T02:23:39.213 に答える
0
DefaultCaret caret = (DefaultCaret)textArea.getCaret();  
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); 
于 2013-02-28T18:46:12.330 に答える