有几次,我因为建议使用以下方法而受到批评:

setPreferredSize setMinimumSize setMaximumSize

Swing组件。当我想要定义显示的组件之间的比例时,我没有看到任何替代它们的使用。有人告诉我:

对于布局,答案总是一样的:使用合适的 LayoutManager

我在网上搜索了一下,但我还没有找到任何关于这个主题的全面分析。我有以下几个问题:

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?


当前回答

我应该完全避免使用这些方法吗?

不,没有正式的证据表明不允许调用或重写这些方法。事实上,Oracle说这些方法是用来给出大小提示的:http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html#sizealignment。

在扩展Swing组件时(而不是在自定义组件实例上调用方法),它们也可能被重写(这是Swing的最佳实践)。

最重要的是,无论如何指定组件的大小,都要确保组件的容器使用的布局管理器符合所请求的组件大小。

定义这些方法是有原因的。那么什么时候使用它们呢? 在什么情况下?为了什么目的?

当你需要为容器布局管理器提供自定义大小提示时,组件将被很好地布局

使用这些方法的负面后果究竟是什么?(我 只能想着在不同的系统之间添加可移植性 屏幕分辨率)。

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

我认为任何LayoutManager都不能完全满足所有所需的布局 的需求。我真的需要实现一个新的LayoutManager每个 我的布局有点变化?

如果你说的实现是指使用,那么是的。不是一个LayoutManager可以处理所有的事情,每个LayoutManager都有它的优点和缺点,因此每个LayoutManager都可以一起使用来生成最终的布局。

参考:

http://docs.oracle.com/javase/tutorial/uiswing/layout/problems.html

其他回答

这里有很多很好的答案,但我想补充一点关于为什么你应该避免这些问题的原因(这个问题又出现在一个重复的主题中):

除了少数例外,如果你正在使用这些方法,你可能正在微调你的GUI,使其在特定的外观上看起来更好(以及与系统相关的设置,例如你首选的桌面字体等)。这些方法本身并不是邪恶的,但是使用它们的典型原因是邪恶的。一旦你开始调整布局中的像素位置和大小,你就会在其他平台上面临GUI崩溃(或者至少看起来很糟糕)的风险。

例如,尝试更改应用程序的默认外观。即使只是使用平台上可用的选项,您也可能会惊讶于结果呈现的糟糕程度。

因此,为了保持GUI在所有平台上的功能性和美观性(记住,Java的主要好处之一是它的跨平台性),您应该依靠布局管理器等来自动调整组件的大小,以便在特定的开发环境之外正确地呈现。

综上所述,您当然可以设想这些方法是合理的。同样,它们本身并不是邪恶的,但它们的使用通常是指示潜在GUI问题的一个大危险信号。只要确保你意识到如果/当你使用它们时可能会出现很高的并发症,并且总是尝试和思考是否有另一种外观和感觉独立的解决方案来解决你的问题——通常你会发现这些方法是不必要的。

顺便说一下,如果你发现自己对标准的布局管理器感到沮丧,有很多好的免费、开源的第三方布局器,例如JGoodies的FormLayout或MigLayout。一些GUI构建器甚至内置了对第三方布局管理器的支持——例如,Eclipse的WindowBuilder GUI编辑器附带了对FormLayout和MigLayout的支持。

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

以下是一些启发:

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, after invoking pack(), 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());
            }
        }
    }
}

大多数人对这些方法知之甚少。您绝对不应该忽略这些方法。这取决于布局管理器是否遵守这些方法。这个页面有一个表,显示了哪些布局管理器尊重哪些方法:

http://thebadprogrammer.com/swing-layout-manager-sizing/

我已经写了8年多的Swing代码,JDK中包含的布局管理器一直满足我的需求。我从来没有需要第三方布局管理器来实现我的布局。

我会说,你不应该尝试给布局管理器提示这些方法,直到你确定你需要他们。做你的布局,不给任何大小提示(即让布局管理器做它的工作),然后你可以做一些小的修改,如果你需要。

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.

定义这些方法是有原因的。那么什么时候使用它们呢?在什么情况下?为了什么目的? 我相信添加它们是为了给布局管理器提供提示。它们可能是由于历史原因而编写的,因为布局管理器是新的,人们并不完全信任它们。我认识一些开发人员,他们避免使用布局管理器,并手动放置所有东西,只是因为他们不想费心学习新的范例。这是个糟糕的主意。

使用这些方法的负面后果究竟是什么?(我只能考虑在不同屏幕分辨率的系统之间增加可移植性)。 它们是无效的,而且会产生糟糕的布局,物体会被挤压或拉伸到非自然的大小。而且布局会很脆弱。更改窗口大小有时会破坏布局,并将内容放在错误的位置。

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.

如果4的答案是“是”,这是否会导致LayoutManager类的激增,从而变得难以维护? 如果你正在为布局中的轻微变化实现新的布局管理器类,那么你使用它们是错误的。如果你只是实现新的布局管理器,你可能做错了什么。我唯一一次扩展LayoutManager类,是向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.