I have seen this question a couple of times, but the answers I found are a bit "bad" in my opinion.
So, basically I have a JScrollPane that I insert components to. Each time I insert a component, I want the JScrollPane to scroll to the bottom. Simple enough.
Now, the logical thing to do would be to add a listener (componentAdded) to the container that I am inserting to.
That listener would then simply scroll to the bottom. However, this will not work, as the component height has not been finished calculating at this time, thus the scrolling fails.
The answers I have seen to this usually involves putting the scroll-row in one (or even several chained) "invokeLater" threads.
This seems to me like an "ugly hack". Surely there should be a better way to actually move the scroll once all the height calculations are done, instead of just "delaying" the scroll for a unknown amount of time?
I also read some answers that you should work with the SwingWorker, which I never really understood. Please enlighten me :)
Here is some code for you to modify (read "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);
UPDATE: Added some code to test the theory. However, it seems to work fine, so I guess I have a problem somewhere else in my program :)
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);
}
}
}