Java JTextPane+;JScrollPane:取消/激活自动滚动
我目前正在用Java编写一个简单的聊天,目前我被困在这个问题上: 我希望我的输出JTextPane的行为与您在良好聊天中所期望的一样,即默认情况下,当新文本到达时,文本自动滚动(使用outputfield.setCaretPosition(outputdocument.getLength()),但当用户向上滚动时,这应该被禁用,当然,当用户再次向下滚动时,会重新启用 我试着玩弄滚动条等等,但我似乎找不到一种方法来检测滚动条是否在底部 我只需创建一个JTextPane、一个JScrollPane并将一个放入另一个中,即可创建可滚动的输出区域Java JTextPane+;JScrollPane:取消/激活自动滚动,java,swing,jtextpane,scrollpane,Java,Swing,Jtextpane,Scrollpane,我目前正在用Java编写一个简单的聊天,目前我被困在这个问题上: 我希望我的输出JTextPane的行为与您在良好聊天中所期望的一样,即默认情况下,当新文本到达时,文本自动滚动(使用outputfield.setCaretPosition(outputdocument.getLength()),但当用户向上滚动时,这应该被禁用,当然,当用户再次向下滚动时,会重新启用 我试着玩弄滚动条等等,但我似乎找不到一种方法来检测滚动条是否在底部 我只需创建一个JTextPane、一个JScrollPane并
JTextPane outputpane = new JTextPane
...
JScrollPane outputscroll = new JScrollPane(outputpane);
outputscroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
编辑:以下是我用来创建组件和显示新消息的代码:
// lots of imports
public class RoomFrame extends JFrame implements ListSelectionListener, ActionListener, Comparable, Runnable
{
public static final String DEFAULT_FONT = "Courier";
public static final int DEFAULT_FONT_SIZE = 12;
private JTextPane mOutputField;
private JScrollPane mOutputScroll;
private StyledDocument mOutputDocument;
public RoomFrame(...)
{
super(...);
createGUI();
setOutputStyles();
setVisible(true);
new Thread(this).start();
}
// ========================================================
private void createGUI()
{
Color borderhighlightouter = new Color(220, 220, 220);
Color borderhighlightinner = new Color(170, 170, 170);
Color bordershadowouter = new Color(120, 120, 120);
Color bordershadowinner = new Color(170, 170, 170);
setLayout(new GridBagLayout());
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setSize(500, 400);
// -----------------------------
// Output
mOutputField = new JTextPane();
mOutputField.setEditable(false);
mOutputField.setBackground(new Color(245, 245, 245));
mOutputDocument = mOutputField.getStyledDocument();
mOutputScroll = new JScrollPane(mOutputField);
mOutputScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
mOutputScroll.setPreferredSize(new Dimension(250, 250));
mOutputScroll.setMinimumSize(new Dimension(50, 50));
mOutputScroll.setOpaque(false);
mOutputScroll.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 1, 0), BorderFactory.createBevelBorder(BevelBorder.LOWERED, borderhighlightouter, borderhighlightinner, bordershadowouter, bordershadowinner)));
getContentPane().add(mOutputScroll);
}
private void setOutputStyles()
{
Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
Style regular = mOutputDocument.addStyle("regular", def);
StyleConstants.setFontFamily(def, DEFAULT_FONT);
StyleConstants.setFontSize(def, DEFAULT_FONT_SIZE);
StyleConstants.setFirstLineIndent(def, 100.0f);
Style nickname = mOutputDocument.addStyle("username", def);
StyleConstants.setBold(nickname, true);
StyleConstants.setForeground(nickname, new Color(50, 50, 220));
Style highlight = mOutputDocument.addStyle("highlight", def);
StyleConstants.setBold(highlight, true);
StyleConstants.setBackground(highlight, new Color(150, 0, 0));
Style join = mOutputDocument.addStyle("join", def);
StyleConstants.setBold(join, true);
StyleConstants.setForeground(join, new Color(20, 100, 20));
Style leave = mOutputDocument.addStyle("leave", def);
StyleConstants.setBold(leave, true);
StyleConstants.setForeground(leave, new Color(100, 100, 20));
Style topic = mOutputDocument.addStyle("topic", def);
StyleConstants.setBold(topic, true);
StyleConstants.setUnderline(topic, true);
Style error = mOutputDocument.addStyle("error", def);
StyleConstants.setBold(error, true);
StyleConstants.setForeground(error, new Color(255, 0, 0));
Style kick = mOutputDocument.addStyle("kick", def);
StyleConstants.setBold(kick, true);
StyleConstants.setForeground(kick, new Color(150, 0, 0));
}
private final boolean shouldScroll()
{
int min = mOutputScroll.getVerticalScrollBar().getValue() + mOutputScroll.getVerticalScrollBar().getVisibleAmount();
int max = mOutputScroll.getVerticalScrollBar().getMaximum();
return min == max;
}
// ========================================================
public void displayMessage(String message)
{
displayMessage(message, "");
}
public void displayMessage(String message, String style)
{
displayMessage(message, style, true);
}
public void displayMessage(String message, String style, boolean addnewline)
{
String newline = (addnewline ? "\n" : "");
style = (style.equals("") ? "regular" : style);
message = message.replace("\n", " ");
// check for highlight
try
{
mOutputDocument.insertString(mOutputDocument.getLength(),
String.format("%s%s", message, newline),
mOutputDocument.getStyle(style));
}
catch (Exception e) {}
// if (shouldScroll())
// mOutputField.setCaretPosition(mOutputDocument.getLength());
}
public void run()
{
while (true)
{
if (shouldScroll())
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
mOutputScroll.getVerticalScrollBar().setValue(mOutputScroll.getVerticalScrollBar().getMaximum());
}
});
}
try { Thread.sleep(500); }
catch (InterruptedException e) { break; }
}
}
public void valueChanged(ListSelectionEvent event)
{
}
public void actionPerformed(ActionEvent event)
{
}
public final int compareTo(Object o) { return this.toString().compareTo(o.toString()); }
}
编辑:多亏fireshadow52的链接,我终于让它按照我想要的方式工作:
private boolean isViewAtBottom()
{
JScrollBar sb = mOutputScroll.getVerticalScrollBar();
int min = sb.getValue() + sb.getVisibleAmount();
int max = sb.getMaximum();
System.out.println(min + " " + max);
return min == max;
}
private void scrollToBottom()
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
mOutputScroll.getVerticalScrollBar().setValue(mOutputScroll.getVerticalScrollBar().getMaximum());
}
});
}
public void displayMessage(String message, String style, boolean prependnewline)
{
String newline = (prependnewline ? "\n" : "");
boolean scroll = isViewAtBottom() && prependnewline;
style = (style.equals("") ? "regular" : style);
message = message.replace("\n", " ");
try
{
mOutputDocument.insertString(mOutputDocument.getLength(),
String.format("%s%s", newline, message),
mOutputDocument.getStyle(style));
}
catch (Exception e) {}
if (scroll)
scrollToBottom();
}
再次感谢你的帮助 JScrollBar有getValue()方法。只需在SwingUtilities.invokeLater()中扭曲它,以便在文本追加后调用。如果您只想在底部滚动,那么这应该会有所帮助 使用此方法检查并查看滚动条是否位于末尾(或底部),如果是,请使用
setCaretPosition(Component.getDocument().getLength())自动向下滚动代码>:
我在使用谷歌时发现了类似的结果,这让我找到了一种类似于此的方法,它可以按要求工作
编辑:确保它是在invokeLater()
中完成的,因为在滚动完成之前需要更新它
我使用的完整示例如下:
public class ScrollTest extends Thread {
private JFrame frame;
private JTextArea textArea;
private JScrollPane scrollPane;
public static void main(String[] arguments) {
new ScrollTest().run();
}
public ScrollTest() {
textArea = new JTextArea(20, 20);
scrollPane = new JScrollPane(textArea);
frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane);
frame.pack();
frame.setVisible(true);
}
public void run() {
while (true) {
textArea.append("" + Math.random() + "\n");
if (shouldScroll()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getMaximum());
}
});
}
try {
Thread.sleep(500);
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
public boolean shouldScroll() {
int minimumValue = scrollPane.getVerticalScrollBar().getValue() + scrollPane.getVerticalScrollBar().getVisibleAmount();
int maximumValue = scrollPane.getVerticalScrollBar().getMaximum();
return maximumValue == minimumValue;
}
}您可以使用条件滚动,如图所示。您的意思是自动滚动到底部吗???如您所述,setCaretPosition(Component.getDocument().getLength());我会做的。只要在每次有新数据输入时调用此函数即可。否则,首先我尝试使用这个技术:,它很好,但没有上面提到的一行简单的文本好。我的意思是,文本应该只在滚动条位于底部时滚动。如果只使用setCaretPosition(Component.getDocument().getLength());它将始终滚动到底部,即使用户向上滚动查看。天哪,谢谢!终于成功了!在添加内容之前,我不太明白这会如何帮助我解决问题。存储垂直滚动条的值,并在添加后将其设置回原来的值。这似乎也不起作用。如果我像If(shouldScroll())那样使用mOutputField.setCaretPosition(mOutputDocument.getLength())代码>它根本不会滚动。我必须手动设置滚动条的最大值吗?编辑:嗯,我应该在哪里使用invokeLater()?我对swing还是新手。我已经更新了我的帖子,加入了我使用过的示例,效果很好。现在它不会显示任何文本。我想这可能是因为。)我使用了一个JTextPane和。)一个方法displayMessage(…),它使用moutDocument.insertString(moutDocument.getLength(),String.format(“%s%s”,message,newline),moutDocument.getStyle(style))代码>以显示新文本?编辑:好的,问题是因为它没有在线程中运行。现在它显示了文本,但仍然没有像应该的那样滚动…我假设这是因为您使用了JTextPane。我将textArea.append(…)
替换为textArea.getDocument().insertString(textArea.getDocument().getLength(),“”+Math.random()+“\n”,null)代码>并且它仍然可以正常工作。你介意发布你正在使用的东西吗?我编辑了这篇文章。它只包含与新文本输出相关的内容。
public class ScrollTest extends Thread {
private JFrame frame;
private JTextArea textArea;
private JScrollPane scrollPane;
public static void main(String[] arguments) {
new ScrollTest().run();
}
public ScrollTest() {
textArea = new JTextArea(20, 20);
scrollPane = new JScrollPane(textArea);
frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane);
frame.pack();
frame.setVisible(true);
}
public void run() {
while (true) {
textArea.append("" + Math.random() + "\n");
if (shouldScroll()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getMaximum());
}
});
}
try {
Thread.sleep(500);
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
public boolean shouldScroll() {
int minimumValue = scrollPane.getVerticalScrollBar().getValue() + scrollPane.getVerticalScrollBar().getVisibleAmount();
int maximumValue = scrollPane.getVerticalScrollBar().getMaximum();
return maximumValue == minimumValue;
}