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

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?


当前回答

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

其他回答

在这种情况下,我需要定义一个组件的孩子之间的比例(孩子1应该使用10%的空间,孩子2 40%,孩子3 50%),是否有可能在不实现自定义布局管理器的情况下实现?

也许GridBagLayout可以满足你的需求。除此之外,网络上有大量的布局管理器,我打赌有一个符合你的要求。

如果你在Java Swing的布局上遇到了麻烦,那么我可以强烈推荐由Karsten Lentzsch免费提供的JGoodies FormLayout,它是Forms免费软件库的一部分。

这个非常流行的布局管理器非常灵活,可以开发非常精致的Java ui。

您将在这里找到Karsten的文档,以及eclipse的一些相当不错的文档。

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

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

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

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

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

以下是一些启发:

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