Best Practice for Setting Jframe Locations

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);
}
}

aligning Jframe to different locations

Toolkit.getDefaultToolkit().getScreenSize(); returns the full screen size of the default screen, it doesn't take into consideration other screen elements like the dock or taskbar, which aren't always at the bottom of the screen and aren't always the same size

A better solution might be to make use of Toolkit.getDefaultToolkit().getScreenInsets and GraphicsConfiguration

public static Rectangle getScreenViewableBounds(Window window) {
return getScreenViewableBounds((Component) window);
}

public static Rectangle getScreenViewableBounds(Component comp) {
return getScreenViewableBounds(getGraphicsDevice(comp));
}

public static Rectangle getScreenViewableBounds(GraphicsDevice gd) {
Rectangle bounds = new Rectangle(0, 0, 0, 0);
if (gd == null) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
gd = ge.getDefaultScreenDevice();
}

if (gd != null) {
GraphicsConfiguration gc = gd.getDefaultConfiguration();
bounds = gc.getBounds();

Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
bounds.x += insets.left;
bounds.y += insets.top;
bounds.width -= (insets.left + insets.right);
bounds.height -= (insets.top + insets.bottom);
}

return bounds;
}

This will return the "safe viewable" range of a specific screen. If you pass it null, it will use the "default" screen

// Safe viewable area for default screen
Rectangle bounds = getScreenViewableBounds(null);
int x = bounds.x + ((bounds.width - getWidth());
int y = bounds.y + ((bounds.width - getHeight());

setLocation(x, y);

Which would place the window in the bottom/right hand position of the window, BUT, place it aligned to the taskbar (because most people have it aligned along the bottom)

For weird people, like me, who place the taskbar at the top of the screen

// Safe viewable area for default screen
Rectangle bounds = getScreenViewableBounds(null);
int x = bounds.x;
int y = bounds.y;

setLocation(x, y);

will place the window at the top/left corner of the viewable, aligned below the the taskbar

And yes, I've seen too many developers use setLocation(0, 0) and place the window under the taskbar/menu bar, their names become "mud"

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();
}
});
}
}

How can I set jFrame Location at right of Screen?

You can do this yourself by calculating the position based on the size of the screen and the frame.

static void setLocationToTopRight(JFrame frame) {
GraphicsConfiguration config = frame.getGraphicsConfiguration();
Rectangle bounds = config.getBounds();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);

int x = bounds.x + bounds.width - insets.right - frame.getWidth();
int y = bounds.y + insets.top;
frame.setLocation(x, y);
}

The screen insets include places where a window is not expected to occupy, like task bars and the Mac menu bar. It's possible there is an OS where you can place something on the right side of the screen which would interfere with the window placement if you didn't subtract the insets. (I think Ubuntu can do that, actually, but I don't remember whether a window is placed on top of or behind the menu.)


Here's a simple MCVE demonstrating all four edges. Pressing one of the four buttons anchors the JFrame to that edge.

package mcve;

import javax.swing.*;
import java.awt.*;

public class WindowPlacement {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Window Placement");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

JButton top = new JButton("Top");
JButton left = new JButton("Left");
JButton bottom = new JButton("Bottom");
JButton right = new JButton("Right");

frame.add(top, BorderLayout.NORTH);
frame.add(left, BorderLayout.WEST);
frame.add(bottom, BorderLayout.SOUTH);
frame.add(right, BorderLayout.EAST);

top.addActionListener(e -> setLocationToTop(frame));
left.addActionListener(e -> setLocationToLeft(frame));
bottom.addActionListener(e -> setLocationToBottom(frame));
right.addActionListener(e -> setLocationToRight(frame));

frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}

// Also see:
// https://docs.oracle.com/javase/9/docs/api/java/awt/GraphicsEnvironment.html#getMaximumWindowBounds--
static Rectangle getMaxWindowBounds(JFrame frame) {
GraphicsConfiguration config = frame.getGraphicsConfiguration();
Rectangle bounds = config.getBounds();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);
bounds.x += insets.left;
bounds.y += insets.top;
bounds.width -= insets.left + insets.right;
bounds.height -= insets.top + insets.bottom;
return bounds;
}

static void setLocationToTop(JFrame frame) {
frame.setLocation(frame.getX(), getMaxWindowBounds(frame).y);
}

static void setLocationToLeft(JFrame frame) {
frame.setLocation(getMaxWindowBounds(frame).x, frame.getY());
}

static void setLocationToBottom(JFrame frame) {
Rectangle bounds = getMaxWindowBounds(frame);
frame.setLocation(frame.getX(), bounds.y + bounds.height - frame.getHeight());
}

static void setLocationToRight(JFrame frame) {
Rectangle bounds = getMaxWindowBounds(frame);
frame.setLocation(bounds.x + bounds.width - frame.getWidth(), frame.getY());
}
}

JFrame set location at center of window on MAC OSX

Location should be set after you packed your frame (wich calculates size of the frame).
And after that it should be made visible (it is hidden by default otherwize).

 pack();
setLocationRelativeTo(null);
setVisible(true);

How to manually set locations of JComponents etc

Your JFrame's layout is set to null, but the startPanel's one is not. So first of all, use:

startPanel.setLayout(null);

Now, instead of component.setLocation(x, y), use component.setBounds(x, y, width, height), so you also set a size for them.

But as has been said in the comments, it would be preferable to use layout managers instead of null layout.

Best Practice With JFrame Constructors?

Unfortunately there are a lot of bad books out there. And a lot of bad code.

You should not abuse inheritance by using it where not necessary. (Okay, there is the Double Brace idiom, which is complete inheritance abuse.) This applies to JFrame, JPanel, Thread and practically everything except java.lang.Object.

Also it is an extremely good idea to make fields private and where possible final. It turns out that references to components generally don't need to be stored in fields, at least not like this.



Related Topics



Leave a reply



Submit