Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
Solution 1:
-
Should I completely avoid the use of those methods?
Yes for application code.
-
The methods have been defined for a reason. So when should I use them? In which context? For what purposes?
I don't know, personally I think of it as an API design accident. Slightly forced by compound components having special ideas about child sizes. "Slightly", because they should have implemented their needs with a custom LayoutManager.
-
What exactly are the negative consequences of using those methods? (I can only think adding portability between systems with different screen resolution.)
Some (incomplete, and unfortunately the links are broken due to migration of SwingLabs to java.net) technical reasons are for instance mentioned in the Rules (hehe) or in the link @bendicott found in his/her comment to my answer. Socially, posing tons of work onto your unfortunate fellow who has to maintain the code and has to track down a broken layout.
-
I don't think any LayoutManager can exactly satisfy all desired layout needs. Do I really need to implement a new LayoutManager for every little variation on my layout?
Yes, there are LayoutManagers powerful enough to satisfy a very good approximation to "all layout needs". The big three are JGoodies FormLayout, MigLayout, DesignGridLayout. So no, in practice, you rarely write LayoutManagers except for simple highly specialized environments.
-
If the answer to 4 is "yes", won't this lead to a proliferation of LayoutManager classes which will become difficult to maintain?
(The answer to 4 is "no".)
-
In a situation where I need to define proportions between children of a Component (for example, child 1 should use 10% of space, child 2 40%, child 3 50%), is it possible to achieve that without implementing a custom LayoutManager?
Any of the Big-Three can, can't even GridBag (never bothered to really master, too much trouble for too little power).
Solution 2:
A few heuristics:
Don't use
set[Preferred|Maximum|Minimum]Size()
when you really mean to overrideget[Preferred|Maximum|Minimum]Size()
, as might be done in creating your own component, shown here.Don't use
set[Preferred|Maximum|Minimum]Size()
when you could rely on a component's carefully overriddengetPreferred|Maximum|Minimum]Size
, as shown here and below.Do use
set[Preferred|Maximum|Minimum]Size()
to derive post-validate()
geometry, as shown below and here.If a component has no preferred size, e.g.
JDesktopPane
, you may have to size the container, but any such choice is arbitrary. A comment may help clarify the intent.Consider alternate or custom layouts when you find that you would have to loop through many components to obtain derived sizes, as mentioned in these comments.
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
/**
* @see https://stackoverflow.com/questions/7229226
* @see https://stackoverflow.com/questions/7228843
*/
public class DesignTest {
private List<JTextField> list = new ArrayList<JTextField>();
private JPanel panel = new JPanel();
private JScrollPane sp = new JScrollPane(panel);
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
DesignTest id = new DesignTest();
id.create("My Project");
}
});
}
private void addField(String name) {
JTextField jtf = new JTextField(16);
panel.add(new JLabel(name, JLabel.LEFT));
panel.add(jtf);
list.add(jtf);
}
private void create(String strProjectName) {
panel.setLayout(new GridLayout(0, 1));
addField("First Name:");
addField("Last Name:");
addField("Address:");
addField("City:");
addField("Zip Code:");
addField("Phone:");
addField("Email Id:");
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("permanentFocusOwner",
new FocusDrivenScroller(panel));
// Show half the fields
sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
sp.validate();
Dimension d = sp.getPreferredSize();
d.setSize(d.width, d.height / 2);
sp.setPreferredSize(d);
JInternalFrame internaFrame = new JInternalFrame();
internaFrame.add(sp);
internaFrame.pack();
internaFrame.setVisible(true);
JDesktopPane desktopPane = new JDesktopPane();
desktopPane.add(internaFrame);
JFrame frmtest = new JFrame();
frmtest.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frmtest.add(desktopPane);
frmtest.pack();
// User's preference should be read from java.util.prefs.Preferences
frmtest.setSize(400, 300);
frmtest.setLocationRelativeTo(null);
frmtest.setVisible(true);
list.get(0).requestFocusInWindow();
}
private static class FocusDrivenScroller implements PropertyChangeListener {
private JComponent parent;
public FocusDrivenScroller(JComponent parent) {
this.parent = parent;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
Component focused = (Component) evt.getNewValue();
if (focused != null
&& SwingUtilities.isDescendingFrom(focused, parent)) {
parent.scrollRectToVisible(focused.getBounds());
}
}
}
}
Solution 3:
Should I completely avoid the use of those methods?
No, there is no formal evidence to suggest calling or overriding these methods is not allowed. In fact, Oracle says these methods are used for giving size hints: http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html#sizealignment.
They may also be overridden (which is the best practice for Swing) when extending a Swing component (rather than calling the method on the custom component instance)
Most importantly no matter how you specify your component's size, be sure that your component's container uses a layout manager that respects the requested size of the component.
The methods have been defined for a reason. So when should I use them? In which context? For what purposes?
When you need to provide customized size hints to the containers Layout manager so that the component will be laid out well
What exactly are the negative consequences of using those methods? (I can only think to add portability between systems with different screen resolution).
Many layout managers do not pay attention to a component's requested maximum size. However,
BoxLayout
andSpringLayout
do. Furthermore,GroupLayout
provides the ability to set the minimum, preferred or maximum size explicitly, without touching the component.Make sure that you really need to set the component's exact size. Each Swing component has a different preferred size, depending on the font it uses and the look and feel. Thus having a set size might produce varied looks of the UI on different Systems
sometimes problems can be encountered with
GridBagLayout
and text fields, wherein if the size of the container is smaller than the preferred size, the minimum size gets used, which can cause text fields to shrink quite substantially.JFrame
does not enforce overridengetMinimumSize()
only callingsetMinimumSize(..)
on its works
I don't think any LayoutManager can exactly satisfy all desired layout needs. Do I really need to implement a new LayoutManager for every little variation on my layout?
If by implementing you mean using then yes. Not one LayoutManger
can handle everything, each LayoutManager
has its pros and cons thus each can be used together to produce the final layout.
Reference:
- http://docs.oracle.com/javase/tutorial/uiswing/layout/problems.html
Solution 4:
There are a lot of good answers here but I want to add a little more about the reasons why you should normally avoid these (the question just came up again in a duplicate topic):
With few exceptions, if you are using these methods you are probably fine-tuning your GUI to look good on a specific look-and-feel (and with your system-specific settings, e.g. your preferred desktop font, etc.). The methods themselves aren't inherently evil, but the typical reasons for using them are. As soon as you start tuning pixel positions and sizes in a layout you run the risk of your GUI breaking (or at minimum, looking bad), on other platforms.
As an example of this, try changing your application's default look-and-feel. Even just with the options available on your platform, you may be surprised at how poorly the results can be rendered.
So, in the name of keeping your GUI functional and nice-looking on all platforms (remember, one of the major benefits of Java is its cross-platformness), you should rely on layout managers, etc., to automatically adjust the sizes of your components so that it renders correctly outside of your specific development environment.
All that said, you can certainly conceive of situations where these methods are justified. Again, they aren't inherently evil, but their usage is normally a big red flag indicating potential GUI issues. Just make sure you are aware of the high potential for complications if/when you use them, and always try and think if there is another look-and-feel-independent solution to your problems -- more often than not you will find that these methods are not necessary.
By the way, if you find yourself getting frustrated with standard layout managers, there are a lot of good free, open-source third-party ones, for example JGoodies' FormLayout
, or MigLayout
. Some GUI builders even have built-in support for third-party layout managers -- Eclipse's WindowBuilder GUI editor, for example, ships with support for FormLayout
and MigLayout
.
Solution 5:
If you are having trouble with layouts in Java Swing, then I can highly recommend the JGoodies FormLayout
provided freely as part of the Forms freeware library by Karsten Lentzsch here.
This very popular layout manager is extremely flexible, allowing for very polished Java UIs to be developed.
You'll find Karsten's documentation in here, and some rather good documentation from eclipse here.