JScrollPane添加组件后如何动态滚动到末尾?
How to make JScrollPane dynamically scroll to the end after adding components?
这是我的 ScrollPane 代码
public class CustomScrollPane extends JScrollPane {
private static CustomScrollPane instance = null;
public CustomScrollPane () {
super(panel.getInstance()); // a panel that the scrollpane wraps around
this.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
// hide the vertical scroll bar
this.getVerticalScrollBar().setPreferredSize(new Dimension(0, 0));
}
public static CustomScrollPane getInstance() {
if (instance == null)
instance = new CustomScrollPane ();
return instance;
}
我尝试执行 getVerticalScrollbar.setValue(getVerticalScrollbar().getMaximum())
但是当我向其中添加 JLabel 时它没有滚动到末尾。我试图让它在新的 JLabel 添加到面板后始终滚动到屏幕底部。我没有使用 JTextArea,因为我希望每一行都有不同的前景色,所以我使用了 JLabels。
我也试过添加这个方法
public void scrollToBottom() { getVerticalScrollbar().getMaximum(); }
但它只是冻结了 ScrollPane,我无法滚动。
如有任何帮助,我们将不胜感激!
我不确定您希望通过扩展 JScrollPane
实现什么,不确定它是否真的是它的核心职责,毕竟,例如,您可以让它的“视口视图”自己完成。 ..
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
JPanel contentPane = new JPanel(new GridLayout(-1, 8));
for (int index = 0; index < 1000; index++) {
contentPane.add(new SqaurePane());
}
add(new JScrollPane(contentPane));
JButton top = new JButton("Top");
JButton bottom = new JButton("Bottom");
JPanel actionPane = new JPanel();
actionPane.add(top);
actionPane.add(bottom);
add(actionPane, BorderLayout.SOUTH);
top.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
contentPane.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
}
});
bottom.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
contentPane.scrollRectToVisible(new Rectangle(0, contentPane.getHeight(), 0, 0));
}
});
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
static List<Color> COLORS = new ArrayList<>(Arrays.asList(new Color[] {
Color.BLACK,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.RED,
Color.WHITE,
Color.YELLOW,
}));
public class SqaurePane extends JPanel {
public SqaurePane() {
Collections.shuffle(COLORS);
setBackground(COLORS.get(0));
}
@Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
}
}
魔法就在这里...
top.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
contentPane.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
}
});
bottom.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
contentPane.scrollRectToVisible(new Rectangle(0, contentPane.getHeight(), 0, 0));
}
});
在这里,我要求 contentPane
(包含所有方块的容器)根据我的需要“滚动到一个可见的矩形”
现在,在您的情况下,当您将新组件添加到“容器”时,您需要指示组件滚动到将显示新组件的位置。
这会有点复杂,因为您需要先触发布局通道,这样您才能获得容器的新尺寸
容器中的动态添加...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
JPanel contentPane = new JPanel(new GridLayout(-1, 8));
add(new JScrollPane(contentPane));
for (int index = 0; index < 8 * 8; index++) {
contentPane.add(new SqaurePane());
}
Timer timer = new Timer(500, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SqaurePane sqaurePane = new SqaurePane();
contentPane.add(sqaurePane);
contentPane.revalidate();
// There is an issue with how the layout pass runs, this
// "seems" to be getting pushed onto the EDT later, which
// is messing up the scroll logic.
// So, instead, we push this on to the EDT to be executed
// "later" after the layout pass has run. Yes, I tried
// calling doLayout directly, but, for the first element
// of each row, it wouldn't work correctly
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
contentPane.scrollRectToVisible(new Rectangle(0, sqaurePane.getY(), 1, sqaurePane.getHeight()));
}
});
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
static List<Color> COLORS = new ArrayList<>(Arrays.asList(new Color[]{
Color.BLACK,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.RED,
Color.WHITE,
Color.YELLOW,}));
public class SqaurePane extends JPanel {
public SqaurePane() {
Collections.shuffle(COLORS);
setBackground(COLORS.get(0));
}
@Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
}
}
现在,如果您真的想解耦这个概念,您可以使用 ContainerListener
并对通过它添加的新组件做出反应
这是我的 ScrollPane 代码
public class CustomScrollPane extends JScrollPane {
private static CustomScrollPane instance = null;
public CustomScrollPane () {
super(panel.getInstance()); // a panel that the scrollpane wraps around
this.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
// hide the vertical scroll bar
this.getVerticalScrollBar().setPreferredSize(new Dimension(0, 0));
}
public static CustomScrollPane getInstance() {
if (instance == null)
instance = new CustomScrollPane ();
return instance;
}
我尝试执行 getVerticalScrollbar.setValue(getVerticalScrollbar().getMaximum())
但是当我向其中添加 JLabel 时它没有滚动到末尾。我试图让它在新的 JLabel 添加到面板后始终滚动到屏幕底部。我没有使用 JTextArea,因为我希望每一行都有不同的前景色,所以我使用了 JLabels。
我也试过添加这个方法
public void scrollToBottom() { getVerticalScrollbar().getMaximum(); }
但它只是冻结了 ScrollPane,我无法滚动。
如有任何帮助,我们将不胜感激!
我不确定您希望通过扩展 JScrollPane
实现什么,不确定它是否真的是它的核心职责,毕竟,例如,您可以让它的“视口视图”自己完成。 ..
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
JPanel contentPane = new JPanel(new GridLayout(-1, 8));
for (int index = 0; index < 1000; index++) {
contentPane.add(new SqaurePane());
}
add(new JScrollPane(contentPane));
JButton top = new JButton("Top");
JButton bottom = new JButton("Bottom");
JPanel actionPane = new JPanel();
actionPane.add(top);
actionPane.add(bottom);
add(actionPane, BorderLayout.SOUTH);
top.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
contentPane.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
}
});
bottom.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
contentPane.scrollRectToVisible(new Rectangle(0, contentPane.getHeight(), 0, 0));
}
});
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
static List<Color> COLORS = new ArrayList<>(Arrays.asList(new Color[] {
Color.BLACK,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.RED,
Color.WHITE,
Color.YELLOW,
}));
public class SqaurePane extends JPanel {
public SqaurePane() {
Collections.shuffle(COLORS);
setBackground(COLORS.get(0));
}
@Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
}
}
魔法就在这里...
top.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
contentPane.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
}
});
bottom.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
contentPane.scrollRectToVisible(new Rectangle(0, contentPane.getHeight(), 0, 0));
}
});
在这里,我要求 contentPane
(包含所有方块的容器)根据我的需要“滚动到一个可见的矩形”
现在,在您的情况下,当您将新组件添加到“容器”时,您需要指示组件滚动到将显示新组件的位置。
这会有点复杂,因为您需要先触发布局通道,这样您才能获得容器的新尺寸
容器中的动态添加...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
JPanel contentPane = new JPanel(new GridLayout(-1, 8));
add(new JScrollPane(contentPane));
for (int index = 0; index < 8 * 8; index++) {
contentPane.add(new SqaurePane());
}
Timer timer = new Timer(500, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SqaurePane sqaurePane = new SqaurePane();
contentPane.add(sqaurePane);
contentPane.revalidate();
// There is an issue with how the layout pass runs, this
// "seems" to be getting pushed onto the EDT later, which
// is messing up the scroll logic.
// So, instead, we push this on to the EDT to be executed
// "later" after the layout pass has run. Yes, I tried
// calling doLayout directly, but, for the first element
// of each row, it wouldn't work correctly
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
contentPane.scrollRectToVisible(new Rectangle(0, sqaurePane.getY(), 1, sqaurePane.getHeight()));
}
});
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
static List<Color> COLORS = new ArrayList<>(Arrays.asList(new Color[]{
Color.BLACK,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.RED,
Color.WHITE,
Color.YELLOW,}));
public class SqaurePane extends JPanel {
public SqaurePane() {
Collections.shuffle(COLORS);
setBackground(COLORS.get(0));
}
@Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
}
}
现在,如果您真的想解耦这个概念,您可以使用 ContainerListener
并对通过它添加的新组件做出反应