I have a GUI set up like this:
- JFrame
- JScrollPane, in the CENTER of the JFrame's default BorderLayout
- Vertical Box, as in Box.getVerticalBox()
- A bunch of JPanels
- Vertical Box, as in Box.getVerticalBox()
- JScrollPane, in the CENTER of the JFrame's default BorderLayout
In response to KeyEvents, the program should zoom in or out. I am attempting to do this by calling setPreferredSize() on the JPanels, but it doesn't "take." What happens is that the contents of each JPanel is zoomed in or out as they should be, but the overall layout doesn't change. That is, if a JPanel was 100x100 before the zoom, and it was to have zoomed out to be 50x50, then the contents will be drawn half as large (correctly), but the area of the JPanel taken is still 100x100. The other JPanels aren't "pushed up."
Oddly, if I manually resize the window, then things settle to what they should be. I thought the problem might be that I need to call some combination of invalidate(), validate(), revalidate(), pack(), doLayout(), etc., but none of these seem to have any effect.
I've looked and there are plenty of questions like this, and some answers, but nothing that seems to help. It's probably something obvious, but I am at a loss.
Here is a minimal example.
import javax.swing.SwingUtilities;
public class Main {
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MainWindow mWindow = new MainWindow();
}
});
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JComponent;
import javax.swing.Box;
public class MainWindow extends JFrame implements KeyListener {
public static int numPages = 4;
public static float[] zoomOptions = {
18, 27, 36, 54, 72, 96, 120, 144,
180, 216,252, 288, 360, 432, 504, 576
};
public static int zoomIndex = 4;
private PagePanel[] pagePanels = null;
private JScrollPane scroller = null;
public MainWindow() {
this.setBackground(Color.WHITE);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.addKeyListener(this);
this.pagePanels = new PagePanel[numPages];
for (int i = 0; i < this.numPages; i++)
{
pagePanels[i] = new PagePanel(i);
pagePanels[i].setReferenceSize(300,200);
pagePanels[i].resizeTo();
}
JComponent vbox = Box.createVerticalBox();
for (int i = 0; i < pagePanels.length; i++)
vbox.add(pagePanels[i]);
this.scroller = new JScrollPane(vbox);
this.add(scroller);
this.setPreferredSize(new Dimension(500,500));
this.pack();
this.setVisible(true);
this.setFocusable(true);
}
private void resizeAllSubPanels() {
for (int i = 0; i < this.pagePanels.length; i++)
pagePanels[i].resizeTo();
// Tried various things here to force adjustment of layout.
}
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) { }
public void keyPressed(KeyEvent e) {
char c = e.getKeyChar();
if ((c == '-') || (c == '_'))
{
if (this.zoomIndex > 0)
--zoomIndex;
resizeAllSubPanels();
this.repaint();
}
else if ((c == '+') || (c == '='))
{
if (this.zoomIndex + 1 < this.zoomOptions.length)
++zoomIndex;
resizeAllSubPanels();
this.repaint();
}
}
}
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Dimension;
import javax.swing.JPanel;
public class PagePanel extends JPanel {
private int referenceHeight = -1;
private int referenceWidth = -1;
public PagePanel(int pageNum) {
// Will be using absolute positioning. Could this be the problem?
this.setLayout(null);
}
public void setReferenceSize(int h,int w) {
this.referenceHeight = h;
this.referenceWidth = w;
}
public void resizeTo() {
float zoomRatio = MainWindow.zoomOptions[MainWindow.zoomIndex] / 72.0f;
int w = (int) (referenceWidth * zoomRatio);
int h = (int) (referenceHeight * zoomRatio);
this.setPreferredSize(new Dimension(w,h));
this.repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.clearRect(0,0,this.getSize().width,this.getSize().height);
float zoomRatio = MainWindow.zoomOptions[MainWindow.zoomIndex] / 72.0f;
g.fillOval(
(int) (10 * zoomRatio),
(int) (10 * zoomRatio),
(int) (100 * zoomRatio),
(int) (100 * zoomRatio));
Rectangle r = this.getVisibleRect();
g.drawRect(r.x,r.y,r.width,r.height);
}
}