问题
Several times I\'ve been criticized for having suggested the use of the following methods:
- setPreferredSize
- setMinimumSize
- setMaximumSize
on Swing
components. I don\'t see any alternative to their use when I want to define proportions between displayed components. I have been told this:
With layouts the answer is always the same: use a suitable LayoutManager
I have searched the web a little bit, but I haven\'t found any comprehensive analysis of the subject. So I have the following questions:
- Should I completely avoid the use of those methods?
- The methods have been defined for a reason. So when should I use them? In which context? For what purposes?
- What exactly are the negative consequences of using those methods? (I can only think adding portability between systems with different screen resolution).
- 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 the answer to 4 is \"yes\", won\'t this lead to a proliferation of LayoutManager classes which will become difficult to maintain?
- In a situation where I need to define proportions between children of a Component (eg, child1 should use 10% of space, child2 40% ,child3 50%), is it possible to achieve that without implementing a custom LayoutManager?
回答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).
回答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());
}
}
}
}
回答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
回答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
.
回答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.
回答6:
These methods are poorly understood by most people. You should absolutely not ignore these methods. It is up to the layout manager if they honor these methods. This page has a table that shows which layout managers honor which of those methods:
http://thebadprogrammer.com/swing-layout-manager-sizing/
I have been writing Swing code for 8+ years and the layout managers included in the JDK have always served my needs. I have never needed a 3rd party layout manager to achieve my layouts.
I will say that you shouldn't try to give the layout manager hints with these methods until you are sure you need them. Do your layout without giving any sizing hints (i.e. let the layout manager do its job) and then you can make minor corrections if you need to.
回答7:
In a situation where I need to define proportions between children of a Component (child 1 should use 10% of space, child2 40% ,child3 50%), is it possible to achieve that without implementing a custom layout manager?
Maybe GridBagLayout
would satisfy your needs. Besides that, there's a ton of layout managers on the web, and I bet there's one that fits your requirements.
回答8:
I am seeing it differenty than the accepted answer.
1) Should I completely avoid the use of those methods?
Never avoid! They're there to express the size constraints of your components to the layout manager. You can avoid using them if you're not using any layout manager and try to manage the visual layout on your own.
Unfortunately, Swing is not coming with reasonable default dimensions. However, instead of setting the dimensions of a component, it is better OOP to descend your own component with reasonable defaults. (In that case you call setXXX in your descendant class.) Alternatively, you can override the getXXX methods for the same effect.
2) The methods have been defined for a reason. So when should I use them? In which context? For what purposes?
Always. When you create a component, set its realistic min/preferred/max size according to the use of that component. For example, if you have a JTextField for entering country symbols such as UK, its preferred size shall be as wide to fit two chars (with the current font, etc.) but probably it is meaningless to let it grow any bigger. After all, country symbols are two chars. As opposite, if you have a JTextField for entering e.g. a customer name, it can have a preferred size for like the pixel size for 20 chars, but can grow to bigger if the layout is resized, so set the maximum size to more. At the same time, having a 0px wide JTextField is pointless, so set a realistic minimum size (I would say the pixel size of 2 chars).
3) What exactly are the negative consequences of using those methods?
(I can only think adding portability between systems with different screen resolution).
No negative consequences. These are hints for the layout manager.
4) 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 ?
No, definitely not. The usual approach is to cascade different basic layoutmanagers such as horizontal and vertical layout.
For example, the layout below:
<pre>
+--------------+--------+
| ###JTABLE### | [Add] |
| ...data... |[Remove]|
| ...data... | |
| ...data... | |
+--------------+--------+
</pre>
is having two parts. The left and right parts are a horizontal layout. The right part is a JPanel added to the horizontal layout, and this JPanel is having a vertical layout which lays out the buttons vertically.
Of course, this can grow tricky with a real life layout. Therefore grid-based layout managers such as MigLayout are much better if you're about to develop anything serious.
5) If the answer to 4 is "yes", won't this lead to a proliferation of LayoutManager classes which will become difficult to maintain?
No, you definitely shall not develop layout managers, unless you need something very special.
6) In a situation where I need to define proportions...
between children of a Component (eg, child1 should use 10% of space, child2 40% ,child3 50%), is it possible to achieve that without implementing a custom LayoutManager?
Basically, once the preferred sizes are set right, you may not want to do anything in percentage. Simply, because percentages are pointless (e.g. it is pointless to have a JTextField 10% of the window size - since one can shrink the window so that JTextField becomes 0px wide, or can expand the window so that the JTextField is across two displays on a multi-display setup).
But, may times you may use the percentages to control sizes of bigger building blocks of your gui (panels, for example).
You can use JSplitPane where you can pre-set the ratio of the two sides. Or, you can use MigLayout which allows you to set such constraints in percentage, pixels, and other units.
回答9:
Should I completely avoid the use of those methods?
I wouldn't say "avoid" them. I'd say that if you think you need them, you're probably doing something wrong. Component sizes are determined in context. For example, Text component sizes are determined by the number of rows and columns you specify, combined with the font you may have chosen. Your button and label size will be the size of the graphic, if you set one, or the space needed to display the text you set. Each component has a natural size, and the layout managers will use those to lay everything out without you needing to specify sizes. The main exception is the JScrollPane, which has a size independent of whatever it contains. For those, I will sometimes call setSize()
, and let that size determine the initial window size, by calling JFrame.pack()
. Usually, I will let the window size determine the JScrollPane size. The user will determine the size of the window. Many layout managers ignore the sizes you set anyway, so they often don't do much good.
The methods have been defined for a reason. So when should I use them? In which context? For what purposes? I believe they were added to provide hints to the layout managers. They may have been written for historical reasons, because layout managers were new, and people didn't fully trust them. I know a few developers who avoided layout managers and placed everything manually, just because they didn't want to bother with learning a new paradigm. It's a terrible idea.
What exactly are the negative consequences of using those methods? (I can only think adding portability between systems with different screen resolution). They're ineffective, and they produce bad layouts, with objects getting squeezed or stretched to non-natural sizes. And the layouts will be brittle. Changes to the window size will sometimes break the layout and put things in the wrong places.
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 ? You shouldn't "implement" a new LayoutManager. You should instantiate existing ones. I often use several layout managers in a single window. Each JPanel will have its own layout manager. Some people balk at nested layouts, because they're hard to maintain. When I use them, I give each one its own creation method to make it easier to see what each one does. But I never "implement" a layout manager. I just instantiate them.
If the answer to 4 is "yes", won't this lead to a proliferation of LayoutManager classes which will become difficult to maintain? If you're implementing new layout manager classes for slight variations in layout, you're using them wrong. If you're just implementing new layout managers, you're probably doing something wrong. The only time I've extended a LayoutManager class, it was to add a zoom slider to a JScrollPane.
In a situation where I need to define proportions between children of a Component (eg, child1 should use 10% of space, child2 40% ,child3 50%), is it possible to achieve that without implementing a custom LayoutManager? The JSplitPane has a way of specifying the percentage each component should get. The divider is movable by default, but you can turn that off if you want. I don't use that feature much. I usually have some components that take up a set size, and the rest of the space is taken up by a scroll pane. The scroll pane size will adjust with the window size. If you have two scroll panes side by side, you can put them in a JSplitPane and specify the percentage of new space given to each one as the user expands and contracts the windows.
来源:https://stackoverflow.com/questions/7229226/should-i-avoid-the-use-of-setpreferredmaximumminimumsize-methods-in-java-swi