移动插入符号时保持 JScrollPane 的 (JTextPane) 滚动条到位?
Keeping a JScrollPane's (JTextPane) scroll bar in place when moving the Caret?
我在 JScrollPane 中有一个 JTextPane,还有一个将插入符号的位置从文档顶部移动到底部的 JButton。
我的目标是在单击按钮时保持滚动条的当前值。我试图通过在移动插入符号之前保存滚动条的值,然后在移动插入符号之后重置滚动条的值来实现这一点。
这是奇怪的部分:如果我在重置滚动条的值之前包含一个 JOptionPane(对话框弹出窗口),它就可以工作。没有弹出对话框,滚动条保留在底部。造成这种差异的对话框是做什么的?
注意:我测试过的一件事是,当对话框打开时,它会将焦点从文本窗格中移开。但是我尝试在移动插入符号后手动将焦点从文本窗格中移开,并且我尝试将所有组件设置为不可聚焦,但这些都没有什么不同。
我被难住了。祝你好运,谢谢!
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.DefaultCaret;
public class ScrollTest2 implements Runnable {
public static void main(String[] args) {
EventQueue.invokeLater(new ScrollTest2());
}
private JFrame frame;
private JScrollBar scrollBar;
private JTextPane textPane;
@Override
public void run() {
frame = new JFrame("Scroll Test 2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel mainPanel = new JPanel();
mainPanel.setPreferredSize(new Dimension(250, 100));
textPane = new JTextPane();
textPane.setEditable(false);
textPane.setText("hello\nhello\nhello\nhello\nhello\nhello\nhello\nhello\nhello\nhello");
textPane.setFont(new Font("Courier New", Font.BOLD, 12));
JScrollPane scrollPane = new JScrollPane(textPane);
scrollPane.setPreferredSize(new Dimension(80, 80));
mainPanel.add(scrollPane);
scrollBar = scrollPane.getVerticalScrollBar();
JButton button = new JButton("Click me");
button.addActionListener(new ButtonListener());
mainPanel.add(button);
return mainPanel;
}
public class ButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// Get the scroll bar's position
int scrollPos = scrollBar.getValue();
// The scroll bar is sent to the bottom.
textPane.setCaretPosition(0);
textPane.setCaretPosition(textPane.getDocument().getLength());
/**
* Comment out this line to test.
* When this line is present, the scroll bar is set to its original position (correct).
* When this line is commented out, the scroll bar remains at the bottom (incorrect).
*/
JOptionPane.showMessageDialog(frame, "test", "dialog", JOptionPane.PLAIN_MESSAGE);
// Set scroll bar to its original position.
scrollBar.setValue(scrollPos);
}
}
}
Swing 代码在 Event Dispatch Thread (EDT)
上执行。有时某些方法会将代码添加到 EDT 的末尾,因此代码不会按您期望的顺序执行。
我猜 setCaretPosition(...)
方法是这些方法之一,这意味着您重置滚动条值的代码实际上是在导致滚动条滚动的 setCaretPosition() 方法的逻辑之前执行的。
您可以将代码添加到 EDT 的末尾,如下所示:
//scrollBar.setValue(scrollPos);
SwingUtilities.invokeLater(() -> scrollBar.setValue(scrollPos));
将 JOptionPane 代码放入其中会以某种方式影响 EDT 和执行顺序。
我在 JScrollPane 中有一个 JTextPane,还有一个将插入符号的位置从文档顶部移动到底部的 JButton。
我的目标是在单击按钮时保持滚动条的当前值。我试图通过在移动插入符号之前保存滚动条的值,然后在移动插入符号之后重置滚动条的值来实现这一点。
这是奇怪的部分:如果我在重置滚动条的值之前包含一个 JOptionPane(对话框弹出窗口),它就可以工作。没有弹出对话框,滚动条保留在底部。造成这种差异的对话框是做什么的?
注意:我测试过的一件事是,当对话框打开时,它会将焦点从文本窗格中移开。但是我尝试在移动插入符号后手动将焦点从文本窗格中移开,并且我尝试将所有组件设置为不可聚焦,但这些都没有什么不同。
我被难住了。祝你好运,谢谢!
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.DefaultCaret;
public class ScrollTest2 implements Runnable {
public static void main(String[] args) {
EventQueue.invokeLater(new ScrollTest2());
}
private JFrame frame;
private JScrollBar scrollBar;
private JTextPane textPane;
@Override
public void run() {
frame = new JFrame("Scroll Test 2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel mainPanel = new JPanel();
mainPanel.setPreferredSize(new Dimension(250, 100));
textPane = new JTextPane();
textPane.setEditable(false);
textPane.setText("hello\nhello\nhello\nhello\nhello\nhello\nhello\nhello\nhello\nhello");
textPane.setFont(new Font("Courier New", Font.BOLD, 12));
JScrollPane scrollPane = new JScrollPane(textPane);
scrollPane.setPreferredSize(new Dimension(80, 80));
mainPanel.add(scrollPane);
scrollBar = scrollPane.getVerticalScrollBar();
JButton button = new JButton("Click me");
button.addActionListener(new ButtonListener());
mainPanel.add(button);
return mainPanel;
}
public class ButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// Get the scroll bar's position
int scrollPos = scrollBar.getValue();
// The scroll bar is sent to the bottom.
textPane.setCaretPosition(0);
textPane.setCaretPosition(textPane.getDocument().getLength());
/**
* Comment out this line to test.
* When this line is present, the scroll bar is set to its original position (correct).
* When this line is commented out, the scroll bar remains at the bottom (incorrect).
*/
JOptionPane.showMessageDialog(frame, "test", "dialog", JOptionPane.PLAIN_MESSAGE);
// Set scroll bar to its original position.
scrollBar.setValue(scrollPos);
}
}
}
Swing 代码在 Event Dispatch Thread (EDT)
上执行。有时某些方法会将代码添加到 EDT 的末尾,因此代码不会按您期望的顺序执行。
我猜 setCaretPosition(...)
方法是这些方法之一,这意味着您重置滚动条值的代码实际上是在导致滚动条滚动的 setCaretPosition() 方法的逻辑之前执行的。
您可以将代码添加到 EDT 的末尾,如下所示:
//scrollBar.setValue(scrollPos);
SwingUtilities.invokeLater(() -> scrollBar.setValue(scrollPos));
将 JOptionPane 代码放入其中会以某种方式影响 EDT 和执行顺序。