How to Best Position Swing Guis

How to best position Swing GUIs?

To my eye, a GUI in the middle of the screen looks so.. "splash-screen'ish". I keep waiting for them to disappear and the real GUI to appear!

Since Java 1.5 we've had access to Window.setLocationByPlatform(boolean). which..

Sets whether this Window should appear at the default location for the native windowing system or at the current location (returned by getLocation) the next time the Window is made visible. This behavior resembles a native window shown without programmatically setting its location. Most windowing systems cascade windows if their locations are not explicitly set. The actual location is determined once the window is shown on the screen.

Have a look at the effect of this example that puts 3 GUIs into the default positions as chosen by the OS - on Windows 7, Linux with Gnome & Mac OS X.

Stacked windows on Windows 7 Sample Image
Stacked windows on Mac OS X

(3 lots of) 3 GUIs neatly stacked. This represents 'the path of least surprise' for the end user, since it is how the OS might position 3 instances of the default plain-text editor (or anything else, for that matter). My thanks to trashgod for the Linux & Mac. images.

Here is the simple code used:

import javax.swing.*;

class WhereToPutTheGui {

public static void initGui() {
for (int ii=1; ii<4; ii++) {
JFrame f = new JFrame("Frame " + ii);
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
String s =
"os.name: " + System.getProperty("os.name") +
"\nos.version: " + System.getProperty("os.version");
f.add(new JTextArea(s,3,28)); // suggest a size
f.pack();
// Let the OS handle the positioning!
f.setLocationByPlatform(true);
f.setVisible(true);
}
}

public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {}
initGui();
}
});
}
}

Positioning components in Swing GUIs

There are a number of layout managers that might be capable of providing you with what you need.

  • MigLayout
  • JGoodies FormLayout
  • GridBagLayout

For, GridBagLayout would be my choice (I'm biased, as I've been using this layout manager for the past 12 years ;))

Sample Image

public class TestLayout17 {

public static void main(String[] args) {
new TestLayout17();
}

public TestLayout17() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public class TestPane extends JPanel {

public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(2, 2, 2, 2);

add(new JLabel("Label 1"), gbc);
gbc.gridx++;
add(new JLabel("Label 2"), gbc);

gbc.gridx = 0;
gbc.gridy++;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(new JTextField(10), gbc);
gbc.gridx++;
add(new JTextField(10), gbc);

gbc.gridx = 0;
gbc.gridy++;
gbc.fill = GridBagConstraints.NONE;
gbc.gridwidth = 2;
add(new JButton("Click"), gbc);

}

}

}

I also agree with Eng.Fouad's suggestion of using compound containers to make your life easier in the long run

You might find Laying Out Components Within a Container a worth while read.

Freely position Swing elements

if you want to use absolute positions:

myPanel.setLayout(null);
JButton myButton = new JButton(myPanel);
myButton.setBounds(left, top, width, height);

How do I position my title on top of the GUI window?

Panels are rearranged on an intermediary Layout-manager(Border)

There are also various solutions based on Layout-used, but the main idea is the same

import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MainMenu {

public static void main(String[] args) {

JFrame frame = new JFrame("Main Menu");
//define a new layout
frame.setLayout(new BorderLayout());
//panel for label
JPanel topPanel = new JPanel();
JPanel panel = new JPanel();
panel.setBounds(30, 80, 400, 570);
panel.setLayout(new GridLayout(3, 2, 15, 15));
panel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);

JButton meals = new JButton("Meals");
meals.setFont(new Font("Helvetica", Font.BOLD, 24));
meals.setOpaque(true);

JButton reminder = new JButton ("Reminders");
reminder.setFont(new Font("Helvetica", Font.BOLD, 24));
reminder.setOpaque(true);

JButton shop = new JButton ("Shop");
shop.setFont(new Font("Helvetica", Font.BOLD, 24));
shop.setOpaque(true);

JButton sleep = new JButton ("Sleep Timer");
sleep.setFont(new Font("Helvetica", Font.BOLD, 24));
sleep.setOpaque(true);

JButton account = new JButton ("My Account");
account.setFont(new Font("Helvetica", Font.BOLD, 24));
account.setOpaque(true);

JButton aboutus = new JButton ("About Us");
aboutus.setFont(new Font("Helvetica", Font.BOLD, 24));
aboutus.setOpaque(true);

JLabel label = new JLabel("Welcome back!");

panel.add(meals);
panel.add(reminder);
panel.add(shop);
panel.add(sleep);
panel.add(account);
panel.add(aboutus);

topPanel.add(label);
topPanel.setSize(200, 30);
//rearrange panels on frame
frame.add(topPanel, BorderLayout.NORTH);
frame.add(panel, BorderLayout.CENTER);

frame.setSize(480, 720);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

}


Output:


Sample Image

Best practice for setting JFrame locations

Here is an example that incorporates the advice of:

  1. Hovercraft Full Of Eels - set location by platform.

  2. Aardvocate Akintayo Olu - serialize the location.

But goes on to add 2 tweaks:

  1. Serialize the width/height as well.
  2. If the frame is maximized at time of close, it is restored before getting the bounds. (I detest apps. that serialize options but do not take that into account. The user is sitting there clicking the 'Maximize / Restore' button & wondering why nothing is happening!)

The 4 points combined offer the best user experience!

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Properties;
import java.io.*;

class RestoreMe {

/** This will end up in the current directory
A more sensible location is a sub-directory of user.home.
(left as an exercise for the reader) */
public static final String fileName = "options.prop";

/** Store location & size of UI */
public static void storeOptions(Frame f) throws Exception {
File file = new File(fileName);
Properties p = new Properties();
// restore the frame from 'full screen' first!
f.setExtendedState(Frame.NORMAL);
Rectangle r = f.getBounds();
int x = (int)r.getX();
int y = (int)r.getY();
int w = (int)r.getWidth();
int h = (int)r.getHeight();

p.setProperty("x", "" + x);
p.setProperty("y", "" + y);
p.setProperty("w", "" + w);
p.setProperty("h", "" + h);

BufferedWriter br = new BufferedWriter(new FileWriter(file));
p.store(br, "Properties of the user frame");
}

/** Restore location & size of UI */
public static void restoreOptions(Frame f) throws IOException {
File file = new File(fileName);
Properties p = new Properties();
BufferedReader br = new BufferedReader(new FileReader(file));
p.load(br);

int x = Integer.parseInt(p.getProperty("x"));
int y = Integer.parseInt(p.getProperty("y"));
int w = Integer.parseInt(p.getProperty("w"));
int h = Integer.parseInt(p.getProperty("h"));

Rectangle r = new Rectangle(x,y,w,h);

f.setBounds(r);
}

public static void main(String[] args) {
final JFrame f = new JFrame("Good Location & Size");
f.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
f.addWindowListener( new WindowAdapter() {
public void windowClosing(WindowEvent we) {
try {
storeOptions(f);
} catch(Exception e) {
e.printStackTrace();
}
System.exit(0);
}
});
JTextArea ta = new JTextArea(20,50);
f.add(ta);
f.pack();

File optionsFile = new File(fileName);
if (optionsFile.exists()) {
try {
restoreOptions(f);
} catch(IOException ioe) {
ioe.printStackTrace();
}
} else {
f.setLocationByPlatform(true);
}
f.setVisible(true);
}
}

Top tips for designing GUIs?

well I personally follow these simple rules:

  • be consistent throughout the application DO NOT CHANGE BEHAVIOUR/LAYOUT
  • information flow: from top to bottom from left to right (in western-countries)
  • not too much info on a page (like a ppt-presentation)
  • big letters (so old people can read them too)
  • KISS (whoever can use a videorecorder can use this page/form/etc.)
  • use relaxing colors like blue, green, etc. (not bright-red or neon-pink)
  • Structure (can change of course but as a first draft mostly it is):

    • top -> navigation/menu
    • left -> navigation/info
    • middle -> content
    • bottom -> status
    • bottom right -> buttons


Related Topics



Leave a reply



Submit