我有一个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);
这几乎可以满足我的需求,但要求用户在文本区域内单击以更改插入位置,以便滚动将保持状态(这不是很好)。
我如何使用(垂直)滚动位置代替?
答案 0 :(得分:13)
我遇到了同样的问题,发现this answer包含一个很好的解决方案,适用于这种情况:
DefaultCaret caret = (DefaultCaret) jTextArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
答案 1 :(得分:6)
这是从API文档拼凑而未经测试的:
getViewport()
上使用JScrollPane
来抓住视口。Viewport.getViewPosition()
获取左上角坐标。这些是绝对的,而不是滚动文本的百分比。Viewport.addChangeListener()
在左上角位置发生变化时收到通知(除此之外)。您可能想要创建一种机制来区分用户更改和程序所做的更改。Viewport.setViewPosition()
将左上角位置设置为干扰前的位置。<强>更新强>
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);
}
}
}