I82*_*uch 10 java user-interface swing jscrollpane
我试图模仿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);
Run Code Online (Sandbox Code Playgroud)
在处理新文本的方法中:
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());
}
Run Code Online (Sandbox Code Playgroud)
现在,这是有效的,但由于两个原因,它很笨拙而且不理想.
在你问之前,是的,我已经阅读了关于文本区域滚动的这篇博客文章,但默认滚动到底部行为不是我想要的.
其他相关的(但在我看来,在这方面并不完全有用)问题: 在jscrollpane上设置滚动条 使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);
}
}
});
}
Run Code Online (Sandbox Code Playgroud)
我还有问题#2.
看一下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
}
Run Code Online (Sandbox Code Playgroud)
小智 5
根据之前的答案和进一步的 Google 搜索,我做了一个测试,其中线程连续将字符串附加到 JScrollPane 中的 JTextArea。我认为在这里使用 invokeLater 很重要,因为在滚动到该值之前,JScrollBar 需要使用其新的最大值进行更新。使用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;
}
}
Run Code Online (Sandbox Code Playgroud)