ChatGPT解决这个技术问题 Extra ChatGPT

Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?

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?


C
Community

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).


I'm not entirely sure I agree with this advice in at least two situations. 1) Custom rendered components 2) Using a JEditorPane with HTML that does not itself suggest a width. OTOH I am not sure if I've missed something. I'll carefully review the replies on the thread, but was interested if you had any comments, particularly on the latter case.
@Andrew Thompson 1) custom comps: it's the comp itself that's responsible for returning useful layout hints, if they dont the impl is buggy 2) even core comps are buggy ;-) 3) I dont mind white space (though it wasn't intentional this time, thanks :-)
I can't believe the accepted answer is the one that says to avoid using the setXXX() methods. Sometimes you simply need them to provide a hint to the layout manager. If you are laying out a panel then you should absolutely feel free to use these methods when necessary. Saying that I think if you use the appropriate layout manager you will find yourself not needing these methods very often, but on occasion you simply need them. Try putting a JComboBox or JSpinner in a X_AXIS BoxLayout and not use them, believe you will find you need setMaximumSize() there.
@Michael no, I absolutely don't need it - the answer is always to use a decent LayoutManager and do any fine-tweaking on the manager level (vs. the component-level)
You keep saying "use a decent LayoutManager and tell it what sizes you want" all over stackoverflow, but you never give any specific examples of a "decent" LayoutManager. And none of the standard managers permit controlling the sizes directly.
C
Community

A few heuristics:

Don't use set[Preferred|Maximum|Minimum]Size() when you really mean to override get[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 overridden getPreferred|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.

https://i.stack.imgur.com/hYh36.png

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

disagree (as you might have guessed :-) with reasoning by "external factors": the XXSize properties is meant to express internal only needs. Tweaking those from the outside is mis-using, aka hacking. If you want a (Internal- or J-) frame with a a specifc size relative to it's preferred ... size the frame, not the content
@kleopatra: just to be a little bit insistent: if setXXSize methods should never been used from outside, why haven't been declared private or protected? Isn't this a lack of design? Doesn't the public modifier implicitly tell the user that can use those methods?
I have to agree with @kleopatra: setPreferredSize() always replaces the component's calculation with an arbitrary choice.
@trashgod +100 to you I think there is no problem with overriding these methods, even calling them (but of course this would mean you have a custom component thus overriding would be better)
@DavidKroukamp: Thank you. I defer to kleopatra's greater experience, but I see the value in examining the contrary view critically.
Y
Yoon5oo

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 and SpringLayout 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 overriden getMinimumSize() only calling setMinimumSize(..) 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


provide customized size hints that's a contradiction in itself: providing sizing hints (in px!) is the exclusive task of the component. It calculates them based on internal state details that no other party except itself can know (nor can keep track of). From client perspective, the means of customization can be a suitable LayoutManager and/or specialize api on the component that allows configuring size requirements in terms of "semantic" size-relevant properties, f.i. number of rows/columns in a text component
@kleopatra I would still love to know why Oracle then tells us how to use these methods and the correct way to use them. We might have our own preferences but we cant say the designers ment it to not be used when there is no evidence to suggest this. But thats why I put up the bounty see maybe if it would attract others that could give a information from a credible source where oracle states to not use these methods at all (thus making it bad practice if you do, for example setMinimumSize has to be called on things like JSplitPane etc this can be seen in the Oracle tutorial of split panes.
@David: I've come to see the setXxxSize methods as a red flag that I'm may wind up here, even when the docs suggest it. I almost always should have overridden getXxxSize, where one has access to the required geometry; and even short examples get recycled more than I want to think about. +1 for mentioning variation among layout managers and citing the tutorial.
D'oh, in my comment above, I meant to cite the answer here.
+1 "No, there is no formal evidence to suggest calling or overriding these methods is not allowed." Spot on. The post tagged as answer is plain BS.
J
Jason C

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.


+1 a considerate answer - just disagree with they aren't inherently evil simply because they are :-) Typically, outside clients have no chance whatsoever to guess - and mere assumptions are as close as outsiders can get - halfway correct layout hints: only the components themselves do have all the info at all times to return anything useful. And since the moment outsiders interfere it's their responsiblity to keep those hints up-to-date which they can't.
Well, you know, I have a more "guns don't kill people, people kill people" kind of view of these sorts of things. :) If somebody uses these methods, they need to be aware of things like the good points you raise about unpredictable usage of layout hints (which is why situations where these methods are appropriate are rare indeed).
I think setting preferred size is not a big red flag at all. Not setting it is a big red flag instead. Let me elaborate. The layout manager - no matter how smart it is - has no idea about the logical function of a gui widget. For example, the layout manager can not distinguish between a JTextField for entering ZIP code, and a JTextField for entering a name. Similarly it can not distinguish between a tool button next to a JTextField or a big OK button at the bottom of a form. Therefore either a better widget set, or some sizing hints are needed, no?
@GeeBee Nope, that's a prime example of a red flag, in fact. What you should do is use JTextField.setColumns to set the column count, which adjusts the preferred size accordingly. If you do setPreferredSize then you're hard-coding a size, which will break your layouts depending on the platform font size. Here are some text fields in a grid bag layout with setColumns called appropriately. For your buttons, use appropriate layouts/grid weights to control the sizes.
@GeeBee And now, with that done correctly, notice how the text field widths are decreased when I decrease the font size: i.snag.gy/ZULulh.jpg. Even changing the font size on the fly now works automatically, rather than you, say, having to recalculate all text field widths and explicitly call setPreferredSize again for each, all you have to do is invalidate the layout and their sizes will adjust.
T
Tom

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.


M
Michael

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.


either there's a slight misconception (on your part) or misunderstanding (on my part), take your choice :-) You keep repeating (here, in your blog, in your answer related to BoxLayout) the set XXSize as being important - actually the LayoutManager is (or not) interested in the XXSize, that is the sizing hint independent on how it came about (internal calculation by the component or manually forced by application code)
I am not sure I understand what you are getting at here. I mention above that the XXSize() methods are just hints. I really see nothing wrong with giving a layout manager a little hint, if needed. In my swing code, you will find an occasional setXXSize() method. Not many, but every once in a while I find they are needed. JComboBox and JSpinner frequently need hints. Especially a JComboBox that is populated after it is realized. You seem to be against any and all usage of these methods and I don't know why. (maybe I am the one missing the boat on this I guess).
not the methods are the hints, the properties are: the components should report something reasonable for all hints, some (as f.i. JComboBox) don't - in returning maxInteger or so. That's a bug and should be fixed by the combo. As to your habit: be sure to be far away when the maintainer collegue has to clean that up :) Hard-coded hints tend to wreck the layout at the slightest change and are hard to detect as the reason for a broken layout.
T
Thomas

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.


thanks for the answer. I have to assume that you meant also: "do not use setPreferredSize at all", right?
GridBagLayout uses constraints, where you can specify the "weight" both in X and Y for a given component, so the LayoutManager can decide what to do with the extra space on a resize. BUT you still need/can use setPreferredSize to determine the preferred size of each component, note that "preferred" doesn't mean it's going to be honored always. For special cases you may also need setMinimumSize and setMaximumSize anyway. They are not evil, don't buy into that. docs.oracle.com/javase/tutorial/uiswing/layout/gridbag.html
C
Community

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.


Really? This was at 0? This is better than the accepted answer that has 100+ ups. That basically says "Thou shalt nevar use setPreferredSize!" which is TERRIBLE. In my line of work, there are a LOT of specific size requirements, such as "Buttons on touch screens must be [X] by [Y] with [M]x[N] spacing between" safety requirement. btnBar.setPreferredSize( dimTouchBtn ); is the best way to do that. Straightforward, no need for custom layout manager. I use mostly GridBagLayout with some BorderLayout and BoxLayout, nesting when convenient. This is a strong combination and easy to use.
I was hasty in my above comment. This was the first answer I saw after accepted answer. Why does SO not sort by vote count anymore? I thought that was one of the core reasons for vote count originally. I still stand by original comment nonetheless; this is one of the better answers. Except for its point #6 - that one not as great. There are plenty (minority can still be a large number) of reasons to resize, and GridBagLayout supports that well in most cases I've had.
I think resize vs. not resize shall not be treaten as a religious decision. There are use cases when nobody ever wants to resize anything, e.g. if you develop a GUI for a kiosk, with a pre-set fixed resolution. If you have different targets, such as industrial HMI displays and the case of "buttons", then yeah, you have to have at least 1cm by 1cm for a button on a touch screen. In this case, DPI of the screen sets how you resize. Other inputs such as text fields may not be resized vertically at all, and sometimes (such as a zipcode) horizontal resize is pointless as well.
M
MiguelMunoz

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.