调整文本大小时保持底部的 JScrollPane (JTextPane) 滚动条?
Keeping a JScrollPane's (JTextPane) scroll bar at the bottom when resizing text?
非常奇怪的问题:我在 JScrollPane 中有一个 JTextPane,还有一个用于调整文本大小的 JSlider。如果我增加文本大小,滚动条会向上移动(正常)。我的目标是将滚动条保持在底部,if 它在调整文本大小之前位于底部。奇怪的是,如果我在 ChangeListener 的末尾放入 JOptionPane(对话框),我只能让它工作。如果我不包含对话框(注释掉第 92 行),当增加文本大小时滚动条仍然向上移动,显然忽略了第 100 行。
另一件事:如果我 单击 更改文本大小,对话框 window 会崩溃,弹出多次并搞砸滑块。但是,如果我使用 Tab 到 select 滑块,然后使用 箭头键 上下移动它,对话框 window 正常运行,滚动条将按照我的意愿运行。
所以,这里其实有两个谜团:
- 为什么 将滚动条发送到底部的第 100 行似乎只有在其前面有对话框弹出窗口(第 92 行)时才有效?
- 为什么 dialog/slider 在使用鼠标移动滑块时会变得很乱,但在使用 Tab+箭头键时却不会?
这快把我逼疯了。祝你好运,谢谢!
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class ScrollTest extends JFrame {
private JPanel mainPanel;
private JTextPane textPane;
private JScrollPane scrollPane;
private JScrollBar scrollBar;
private JSlider textSlider;
private JFrame dialogFrame;
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(
new Runnable() {
public void run() {
ScrollTest test = new ScrollTest();
test.pack();
test.setLocationRelativeTo(null);
test.setVisible(true);
}
});
}
public ScrollTest() {
super("Scroll Test");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
mainPanel = new JPanel();
mainPanel.setPreferredSize(new Dimension(250, 100));
this.add(mainPanel);
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, 10));
scrollPane = new JScrollPane(textPane);
scrollPane.setPreferredSize(new Dimension(80, 80));
mainPanel.add(scrollPane);
scrollBar = scrollPane.getVerticalScrollBar();
textSlider = new JSlider(10, 16, 10);
textSlider.setMajorTickSpacing(2);
textSlider.setPreferredSize(new Dimension(120, 40));
textSlider.setPaintLabels(true);
textSlider.setSnapToTicks(true);
mainPanel.add(textSlider);
dialogFrame = new JFrame();
textSlider.addChangeListener(
new ChangeListener() {
public void stateChanged(ChangeEvent e) {
/**
* Determine whether the scroll bar is at the bottom.
* Must be done this way because getValue() returns
* the position at the top of the knob.
*/
int value = scrollBar.getValue();
int max = scrollBar.getMaximum() - scrollBar.getVisibleAmount();
boolean startedAtBottom = value == max;
/**
* Resize the text.
* Increasing the text size moves the scroll bar up.
*/
switch (textSlider.getValue()) {
case 10: textPane.setFont(textPane.getFont().deriveFont(10f));
break;
case 12: textPane.setFont(textPane.getFont().deriveFont(12f));
break;
case 14: textPane.setFont(textPane.getFont().deriveFont(14f));
break;
case 16: textPane.setFont(textPane.getFont().deriveFont(16f));
break;
}
/**
* IMPORTANT: line 92 seems to determines whether line 100
* works properly (for some reason).
* Comment out line 92 to test.
*/
JOptionPane.showMessageDialog(dialogFrame, "test", "dialog", JOptionPane.PLAIN_MESSAGE);
/**
* Send the scroll bar to the bottom ONLY if it was at
* the bottom before the text was resized.
* If line 92 is commented out, this doesn't work when
* increasing text size.
*/
if (startedAtBottom) scrollBar.setValue(scrollBar.getMaximum());
}
});
}
}
我修改了您的代码以创建以下 GUI。
我所做的主要更改是在 stateChanged
方法中。我添加了滑块停止移动和 JFrame
包的测试。 JFrame
包确保所有 Swing 组件都已正确调整大小。
这是完整的可运行代码。
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTextPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ScrollTest implements Runnable {
public static void main(String[] args) {
EventQueue.invokeLater(new ScrollTest());
}
private JFrame frame;
private JScrollBar scrollBar;
private JTextPane textPane;
@Override
public void run() {
frame = new JFrame("Scroll Test");
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, 10));
JScrollPane scrollPane = new JScrollPane(textPane);
scrollPane.setPreferredSize(new Dimension(80, 80));
mainPanel.add(scrollPane);
scrollBar = scrollPane.getVerticalScrollBar();
JSlider textSlider = new JSlider(10, 16, 10);
textSlider.setMajorTickSpacing(2);
textSlider.setPreferredSize(new Dimension(120, 40));
textSlider.setPaintLabels(true);
textSlider.setSnapToTicks(true);
textSlider.addChangeListener(new FontSizeListener());
mainPanel.add(textSlider);
return mainPanel;
}
public class FontSizeListener implements ChangeListener {
@Override
public void stateChanged(ChangeEvent event) {
JSlider slider = (JSlider) event.getSource();
if (!slider.getValueIsAdjusting()) {
/**
* Determine whether the scroll bar is at the bottom. Must be done this way
* because getValue() returns the position at the top of the knob.
*/
int value = scrollBar.getValue();
int max = scrollBar.getMaximum() - scrollBar.getVisibleAmount();
boolean startedAtBottom = value == max;
/**
* Resize the text. Increasing the text size moves the scroll bar up.
*/
textPane.setFont(textPane.getFont().deriveFont((float) slider.getValue()));
frame.pack();
/**
* Send the scroll bar to the bottom ONLY if it was at the bottom before the
* text was resized. If line 92 is commented out, this doesn't work when
* increasing text size.
*/
if (startedAtBottom) {
max = scrollBar.getMaximum() + scrollBar.getVisibleAmount();
scrollBar.setValue(max);
}
}
}
}
}
非常奇怪的问题:我在 JScrollPane 中有一个 JTextPane,还有一个用于调整文本大小的 JSlider。如果我增加文本大小,滚动条会向上移动(正常)。我的目标是将滚动条保持在底部,if 它在调整文本大小之前位于底部。奇怪的是,如果我在 ChangeListener 的末尾放入 JOptionPane(对话框),我只能让它工作。如果我不包含对话框(注释掉第 92 行),当增加文本大小时滚动条仍然向上移动,显然忽略了第 100 行。
另一件事:如果我 单击 更改文本大小,对话框 window 会崩溃,弹出多次并搞砸滑块。但是,如果我使用 Tab 到 select 滑块,然后使用 箭头键 上下移动它,对话框 window 正常运行,滚动条将按照我的意愿运行。
所以,这里其实有两个谜团:
- 为什么 将滚动条发送到底部的第 100 行似乎只有在其前面有对话框弹出窗口(第 92 行)时才有效?
- 为什么 dialog/slider 在使用鼠标移动滑块时会变得很乱,但在使用 Tab+箭头键时却不会?
这快把我逼疯了。祝你好运,谢谢!
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class ScrollTest extends JFrame {
private JPanel mainPanel;
private JTextPane textPane;
private JScrollPane scrollPane;
private JScrollBar scrollBar;
private JSlider textSlider;
private JFrame dialogFrame;
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(
new Runnable() {
public void run() {
ScrollTest test = new ScrollTest();
test.pack();
test.setLocationRelativeTo(null);
test.setVisible(true);
}
});
}
public ScrollTest() {
super("Scroll Test");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
mainPanel = new JPanel();
mainPanel.setPreferredSize(new Dimension(250, 100));
this.add(mainPanel);
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, 10));
scrollPane = new JScrollPane(textPane);
scrollPane.setPreferredSize(new Dimension(80, 80));
mainPanel.add(scrollPane);
scrollBar = scrollPane.getVerticalScrollBar();
textSlider = new JSlider(10, 16, 10);
textSlider.setMajorTickSpacing(2);
textSlider.setPreferredSize(new Dimension(120, 40));
textSlider.setPaintLabels(true);
textSlider.setSnapToTicks(true);
mainPanel.add(textSlider);
dialogFrame = new JFrame();
textSlider.addChangeListener(
new ChangeListener() {
public void stateChanged(ChangeEvent e) {
/**
* Determine whether the scroll bar is at the bottom.
* Must be done this way because getValue() returns
* the position at the top of the knob.
*/
int value = scrollBar.getValue();
int max = scrollBar.getMaximum() - scrollBar.getVisibleAmount();
boolean startedAtBottom = value == max;
/**
* Resize the text.
* Increasing the text size moves the scroll bar up.
*/
switch (textSlider.getValue()) {
case 10: textPane.setFont(textPane.getFont().deriveFont(10f));
break;
case 12: textPane.setFont(textPane.getFont().deriveFont(12f));
break;
case 14: textPane.setFont(textPane.getFont().deriveFont(14f));
break;
case 16: textPane.setFont(textPane.getFont().deriveFont(16f));
break;
}
/**
* IMPORTANT: line 92 seems to determines whether line 100
* works properly (for some reason).
* Comment out line 92 to test.
*/
JOptionPane.showMessageDialog(dialogFrame, "test", "dialog", JOptionPane.PLAIN_MESSAGE);
/**
* Send the scroll bar to the bottom ONLY if it was at
* the bottom before the text was resized.
* If line 92 is commented out, this doesn't work when
* increasing text size.
*/
if (startedAtBottom) scrollBar.setValue(scrollBar.getMaximum());
}
});
}
}
我修改了您的代码以创建以下 GUI。
我所做的主要更改是在 stateChanged
方法中。我添加了滑块停止移动和 JFrame
包的测试。 JFrame
包确保所有 Swing 组件都已正确调整大小。
这是完整的可运行代码。
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTextPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ScrollTest implements Runnable {
public static void main(String[] args) {
EventQueue.invokeLater(new ScrollTest());
}
private JFrame frame;
private JScrollBar scrollBar;
private JTextPane textPane;
@Override
public void run() {
frame = new JFrame("Scroll Test");
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, 10));
JScrollPane scrollPane = new JScrollPane(textPane);
scrollPane.setPreferredSize(new Dimension(80, 80));
mainPanel.add(scrollPane);
scrollBar = scrollPane.getVerticalScrollBar();
JSlider textSlider = new JSlider(10, 16, 10);
textSlider.setMajorTickSpacing(2);
textSlider.setPreferredSize(new Dimension(120, 40));
textSlider.setPaintLabels(true);
textSlider.setSnapToTicks(true);
textSlider.addChangeListener(new FontSizeListener());
mainPanel.add(textSlider);
return mainPanel;
}
public class FontSizeListener implements ChangeListener {
@Override
public void stateChanged(ChangeEvent event) {
JSlider slider = (JSlider) event.getSource();
if (!slider.getValueIsAdjusting()) {
/**
* Determine whether the scroll bar is at the bottom. Must be done this way
* because getValue() returns the position at the top of the knob.
*/
int value = scrollBar.getValue();
int max = scrollBar.getMaximum() - scrollBar.getVisibleAmount();
boolean startedAtBottom = value == max;
/**
* Resize the text. Increasing the text size moves the scroll bar up.
*/
textPane.setFont(textPane.getFont().deriveFont((float) slider.getValue()));
frame.pack();
/**
* Send the scroll bar to the bottom ONLY if it was at the bottom before the
* text was resized. If line 92 is commented out, this doesn't work when
* increasing text size.
*/
if (startedAtBottom) {
max = scrollBar.getMaximum() + scrollBar.getVisibleAmount();
scrollBar.setValue(max);
}
}
}
}
}