我已经看过几次这个问题了,但我发现的答案在我看来有点“糟糕”。
所以,基本上我有一个我插入组件的JScrollPane。每次插入组件时,我都希望JScrollPane滚动到底部。很简单。
现在,合乎逻辑的做法是将一个监听器(componentAdded)添加到我要插入的容器中。
然后,那个听众就会滚动到底部。但是,这不起作用,因为此时组件高度尚未完成计算,因此滚动失败。我看到的答案通常涉及将滚动行放在一个(甚至几个链接的)“invokeLater”线程中。
在我看来,这似乎是一个“丑陋的黑客”。当所有高度计算完成后,确实应该有更好的方法来实际移动滚动,而不是仅仅将“滚动”延迟一段未知的时间?
我还阅读了一些你应该使用SwingWorker的答案,我从来没有真正理解过。请赐教:))
以下是一些代码供您修改(阅读“make work”):
JScrollPane scrollPane = new JScrollPane();
add(scrollPane);
JPanel container = new JPanel();
scrollPane.setViewportView(container);
container.addContainerListener(new ContainerAdapter() {
public void componentAdded(ContainerEvent e) {
JScrollPane scrollPane = (JScrollPane) value.getParent().getParent();
JScrollBar scrollBar = scrollPane.getVerticalScrollBar();
scrollBar.setValue(scrollBar.getMaximum());
}
});
JPanel hugePanel = new JPanel();
hugePanel.setPreferredSize(new Dimension(10000, 10000);
container.add(hugePanel);
更新: 添加了一些代码来测试理论。但是,它似乎工作正常,所以我想我的程序中的其他地方有问题:)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ContainerAdapter;
import java.awt.event.ContainerEvent;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.LineBorder;
public class ScrollTest extends JFrame {
private static final long serialVersionUID = -8538440132657016395L;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ScrollTest().setVisible(true);
}
});
}
public ScrollTest() {
UIManager.put("swing.boldMetal", Boolean.FALSE);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Scroll Test");
setSize(1000, 720);
setLocationRelativeTo(null);
JPanel container = new JPanel();
container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
add(container);
//Create 3 scollpanels
final JScrollPane scrollPane1 = new JScrollPane();
scrollPane1.setBorder(new LineBorder(Color.RED, 2));
container.add(scrollPane1);
final JScrollPane scrollPane2 = new JScrollPane();
scrollPane2.setBorder(new LineBorder(Color.GREEN, 2));
container.add(scrollPane2);
final JScrollPane scrollPane3 = new JScrollPane();
scrollPane3.setBorder(new LineBorder(Color.BLUE, 2));
container.add(scrollPane3);
//Create a jpanel inside each scrollpanel
JPanel wrapper1 = new JPanel();
wrapper1.setLayout(new BoxLayout(wrapper1, BoxLayout.Y_AXIS));
scrollPane1.setViewportView(wrapper1);
wrapper1.addContainerListener(new ContainerAdapter() {
public void componentAdded(ContainerEvent e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
scrollPane1.getVerticalScrollBar().setValue(scrollPane1.getVerticalScrollBar().getMaximum());
}
});
}
});
JPanel wrapper2 = new JPanel();
wrapper2.setLayout(new BoxLayout(wrapper2, BoxLayout.Y_AXIS));
scrollPane2.setViewportView(wrapper2);
wrapper2.addContainerListener(new ContainerAdapter() {
public void componentAdded(ContainerEvent e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
scrollPane2.getVerticalScrollBar().setValue(scrollPane2.getVerticalScrollBar().getMaximum());
}
});
}
});
JPanel wrapper3 = new JPanel();
wrapper3.setLayout(new BoxLayout(wrapper3, BoxLayout.Y_AXIS));
scrollPane3.setViewportView(wrapper3);
wrapper3.addContainerListener(new ContainerAdapter() {
public void componentAdded(ContainerEvent e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
scrollPane3.getVerticalScrollBar().setValue(scrollPane3.getVerticalScrollBar().getMaximum());
}
});
}
});
//Add come stuff into each wrapper
JPanel junk;
for(int x = 1; x <= 1000; x++) {
junk = new JPanel();
junk.setBorder(new LineBorder(Color.BLACK, 2));
junk.setPreferredSize(new Dimension(100, 40));
junk.setMaximumSize(junk.getPreferredSize());
wrapper1.add(junk);
}
for(int x = 1; x <= 1000; x++) {
junk = new JPanel();
junk.setBorder(new LineBorder(Color.BLACK, 2));
junk.setPreferredSize(new Dimension(100, 40));
junk.setMaximumSize(junk.getPreferredSize());
wrapper2.add(junk);
}
for(int x = 1; x <= 1000; x++) {
junk = new JPanel();
junk.setBorder(new LineBorder(Color.BLACK, 2));
junk.setPreferredSize(new Dimension(100, 40));
junk.setMaximumSize(junk.getPreferredSize());
wrapper3.add(junk);
}
}
}
答案 0 :(得分:5)
正确 - 没有浪费时间重新发明轮子 - 在任何你想要的地方滚动组件的方式如下:
wrapper1.addContainerListener(new ContainerAdapter() {
@Override
public void componentAdded(final ContainerEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JComponent comp = (JComponent) e.getChild();
Rectangle bounds = new Rectangle(comp.getBounds());
comp.scrollRectToVisible(bounds);
}
});
}
});
你要避免的气味:
至于invokeLater,引用它的api doc(由我添加的粗体):
导致doRun.run()在AWT事件上异步执行 调度线程。在所有挂起的AWT事件发生后,这将发生 已处理
因此,您可以相当确定在代码执行之前处理内部。