RatioFrame: A Swing window that maintains its size ratio.
When I got home from work today, I noticed that my all-time Java/XML idol asked an interesting question on his blog this morning: How do you create a Swing JFrame that will maintain a given ratio while being resized?
Obviously, I felt this was right up my alley, so I got to work immediately with my usual development lifecycle.
- Put on jogging shorts.
- Fire up personal GPS device.
- Go for 6 mile jog around riverwalk while thinking about problem.
- Shower.
- Fire up BBEdit.
- Make Ant build script.
- Code.
- Post about it.
Here’s the result:
Launch RatioFrame Webstart Demo
The link above will launch a very small Java Webstart app that consists of one window containing a button. If you resize the window, it will retain it’s current ratio. If you click the button, a new ratio will be generated that will be respected for all resizes until the app closes or you click the button again.
You can download the source and ant build file if you like. The ant build file contains targets for signing jars, and an example jnlp webstart file is included.
The key was adding the following ComponentListener to the JFrame like so:
protected void addListener() {
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent evt) {
int w = getWidth();
int h = getHeight();
if (w > h)
setSize(w,(int)(ratio*w));
else
setSize((int)(ratio*h),h);
}
});
}
So why is Elliot my all-time Java/XML idol? Besides his awesome websites, he’s a great author.
Update: I’ve noticed that on my PowerBook running Java 1.5 Tiger, the window resizes perfectly smoothly, while on my PowerBook running Java 1.4, it is quite jumpy and unattractive.
Actually, according to my coworker, it jumps around like over-caffeinated epileptic crack whores. Strong words from a man wearing a kilt. No kidding.
My other coworker just walked in stating: “I might smell a little bit worse than normal today, I’ve been chasing donkeys all morning.”
Yes, I have two PowerBooks. No, you can’t have one.
Update 2: Bad news… the live resizing at the given ratio does not seem to be working on Windows with either the Windows LookAndFeel or the default Metal LookAndFeel. It does work in Metal and the Mac LookAndFeel on Mac OS X though… that’s weird.
Here’s the Java source:
package org.ditchnet.cafeaulait;
import java.util.Random;
import java.awt.Dimension;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.UIManager;
import javax.swing.SwingUtilities;
public class RatioFrame extends JFrame {
private static final Random rng = new Random();
private final Dimension screen = getToolkit().getScreenSize();
private float ratio;
private JButton b;
public RatioFrame(String name) {
super(name);
doRatioResize();
configure();
addListener();
initButton();
}
protected void generateRandomRatio() {
do {
ratio = rng.nextFloat();
} while (ratio < 0.2f);
}
protected void doRatioResize() {
generateRandomRatio();
int w = rng.nextInt(300);
int h = (int)(w * ratio);
setSize(w+400,h+400);
center();
}
protected void center() {
setLocation((int)(screen.getWidth()/2-getWidth()/2),
(int)(screen.getHeight()/2-getHeight()/2));
}
protected void configure() {
getRootPane().setDoubleBuffered(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getToolkit().setDynamicLayout(true);
}
protected void addListener() {
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent evt) {
int w = getWidth();
int h = getHeight();
if (w > h)
setSize(w,(int)(ratio*w));
else
setSize((int)(ratio*h),h);
}
});
}
protected void initButton() {
b = new JButton();
b.setFocusPainted(false);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
doRatioResize();
setButtonText();
validate();
}
});
setButtonText();
getContentPane().add(b);
}
protected void setButtonText() {
b.setText("<html>Current Ratio is: " + ratio +
"<br />Click me to generate new ratio.");
}
public static void main(String[] args) {
try {
String laf = UIManager.getSystemLookAndFeelClassName();
UIManager.setLookAndFeel(laf);
} catch (Exception e) { }
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new RatioFrame("RatioFrame");
f.pack();
f.setVisible(true);
}
});
}
}
2 Comments
Jump to comment form | comments rss [?] | trackback uri [?]