首页 文章

我应该避免在Java Swing中使用set(Preferred | Maximum | Minimum)Size方法吗?

提问于
浏览
452

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

  • setPreferredSize

  • setMinimumSize

  • setMaximumSize

Swing 组件上 . 当我想在显示的组件之间定义比例时,我没有看到任何替代它们的用法 . 我被告知这个:

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

我在网上搜索了一下,但我还没有找到任何关于这个主题的综合分析 . 所以我有以下问题:

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

  • 已定义方法的原因 . 那我什么时候应该使用它们?在哪种情况下?为了什么目的?

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

  • 我认为任何LayoutManager都不能完全满足所有需要的布局需求 . 我是否真的需要为我的布局上的每个小变化实现一个新的LayoutManager?

  • 如果4的答案是"yes",这是否会导致LayoutManager类的扩散变得难以维护?

  • 在我需要定义组件子级之间的比例的情况下(例如,child1应该使用10%的空间,child2 40%,child3 50%),是否可以在不实现自定义LayoutManager的情况下实现这一点?

9 回答

  • 229

    如果您在Java Swing中遇到布局问题,那么我强烈推荐Karsten Lentzsch here自由提供的JGoodies FormLayout 作为Forms免费软件库的一部分 .

    这个非常流行的布局管理器非常灵活,允许开发非常精美的Java UI .

    here中的'll find Karsten'文档,以及eclipse here的一些相当不错的文档 .

  • 96

    我看到它与接受的答案不同 .

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

    永远不要避免它们用于向布局管理器表达组件的大小限制 . 如果您没有使用任何布局管理器并尝试自行管理可视布局,则可以避免使用它们 .

    不幸的是,Swing没有合理的默认尺寸 . 但是,不是设置组件的尺寸,而是以合理的默认值下降自己的组件是更好的OOP . (在这种情况下,您在后代类中调用setXXX . )或者,您可以覆盖getXXX方法以获得相同的效果 .

    2)方法已经定义了一个原因 . 那我什么时候应该使用它们?在哪种情况下?为了什么目的?

    总是 . 创建组件时,请根据该组件的用途设置其实际的最小/首选/最大大小 . 例如,如果您有一个JTextField用于输入国家/地区符号(例如UK),则其首选大小应该宽到适合两个字符(使用当前字体等),但可能让它变得更大没有意义 . 毕竟,国家符号是两个字符 . 相反,如果您有一个JTextField用于输入例如客户名称,它可以具有20个字符的像素大小的首选大小,但是如果调整大小,则可以增大到更大,因此将最大大小设置为更多 . 同时,拥有0px宽的JTextField是没有意义的,所以设置一个真实的最小尺寸(我会说像素大小为2个字符) .

    3)使用这些方法的负面后果究竟是什么?

    (我只能考虑在具有不同屏幕分辨率的系统之间添加可移植性) .

    没有负面后果 . 这些是布局管理器的提示 .

    4)我认为任何LayoutManager都不能完全满足所有需要的布局需求 .

    我是否真的需要为我的布局上的每个小变化实现一个新的LayoutManager?

    不,绝对不是 . 通常的方法是级联不同的基本布局管理器,例如水平和垂直布局 .

    例如,下面的布局:

    <pre>
    +--------------+--------+
    | ###JTABLE### | [Add]  | 
    | ...data...   |[Remove]|
    | ...data...   |        |
    | ...data...   |        |
    +--------------+--------+
    </pre>
    

    有两个部分 . 左右部分是水平布局 . 右边部分是添加到水平布局的JPanel,这个JPanel有一个垂直布局,垂直布局按钮 .

    当然,这可能会因现实生活布局而变得棘手 . 因此,如果您要开发任何严肃的东西,基于网格的布局管理器(如MigLayout)要好得多 .

    5)如果对4的回答是“是”,这是否会导致更难以维护的LayoutManager类?

    不,你肯定不会开发布局管理器,除非你需要非常特别的东西 .

    6)在我需要定义比例的情况下......

    组件的子项之间(例如,child1应该使用10%的空间,child2 40%,child3 50%),是否可以在不实现自定义LayoutManager的情况下实现这一点?

    基本上,一旦首选尺寸设置正确,您可能不需要以百分比做任何事情 . 简单地说,因为百分比是没有意义的(例如,JTextField占窗口大小的10%是没有意义的 - 因为可以缩小窗口以使JTextField变为0px宽,或者可以扩展窗口以使JTextField跨越两个显示器多显示器设置) .

    但是,有时您可以使用百分比来控制gui(例如面板)的更大构建块的大小 .

    您可以使用JSplitPane,您可以在其中预先设置双方的比例 . 或者,您可以使用MigLayout,它允许您以百分比,像素和其他单位设置此类约束 .

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

    是应用程序代码 .

    • 已定义方法的原因 . 那我什么时候应该使用它们?在哪种情况下?为了什么目的?

    我不知道,我个人认为这是一个API设计事故 . 稍微强迫复合组件对孩子大小有特殊想法 . “稍微”,因为他们应该使用自定义LayoutManager实现他们的需求 .

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

    一些(不完整的,不幸的是链接因SwingLabs迁移到java.net而被破坏)技术原因例如在Rules (hehe)link @bendicott中提到他/她对my answer的评论 . 在社交方面,为你不幸的家伙提供大量的工作,他必须维护代码并且必须追踪破碎的布局 .

    • 我认为任何LayoutManager都不能完全满足所有需要的布局需求 . 我是否真的需要为我的布局上的每个小变化实现一个新的LayoutManager?

    是的,有一些强大的LayoutManagers可以满足“所有布局需求”的非常好的近似 . 三巨头是JGoodies FormLayout,MigLayout,DesignGridLayout . 所以不,在实践中,除了简单的高度专业化的环境外,你很少编写LayoutManagers .

    • 如果4的答案是“是”,这是否会导致LayoutManager类的扩散,这将难以维护?

    (4的答案是“不” . )

    • 在我需要定义组件的子项之间的比例的情况下(例如,子项1应使用10%的空间,子项2 40%,子项3 50%),是否可以在不实现自定义的情况下实现该比例布局管理?

    任何一个三巨头都可以,甚至不能GridBag(从来不打算真正掌握,太少的力量太麻烦) .

  • 0

    大多数人都很难理解这些方法 . 你绝对不应该忽略这些方法 . 如果他们遵守这些方法,则由布局管理员决定 . 此页面有一个表格,显示哪些布局管理员会尊重以下哪些方法:

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

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

    我会说你不应该尝试用这些方法给出布局管理器提示,直到你确定需要它们为止 . 在没有给出任何大小提示的情况下进行布局(即让布局管理器完成其工作),然后如果需要,可以进行小的修正 .

  • 19

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

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

    作为示例,请尝试更改应用程序的默认外观 . 即使您的平台上有可用的选项,您也可能会惊讶于结果的呈现效果不佳 .

    因此,在所有平台上保持GUI功能和漂亮的名称(请记住,Java的主要优点之一是它的跨平台性),您应该依赖布局管理器等来自动调整大小 . 您的组件,以便它在您的特定开发环境之外正确呈现 .

    总而言之,你当然可以设想这些方法合理的情况 . 同样,它们本身并不是邪恶的,但它们的使用通常是指示潜在GUI问题的红旗 . 只要确保你在使用它们的时候意识到并发症的可能性很高,并且总是试着想一想你的问题是否有另一种独立于外观和感觉的解决方案 - 通常你会发现这些方法是没有必要的 .

    顺便说一句,如果你发现自己对于标准布局管理器感到沮丧,有许多优秀的免费开源第三方管理器,例如JGoodies' FormLayoutMigLayout . 一些GUI构建器甚至内置了对第三方布局管理器的支持 - 例如,Eclipse的WindowBuilder GUI编辑器支持 FormLayoutMigLayout .

  • 45

    在我需要定义组件的子项之间的比例(子项1应使用10%的空间,child2 40%,child3 50%)的情况下,是否可以在不实现自定义布局管理器的情况下实现这一点?

    也许 GridBagLayout 会满足您的需求 . 除此之外,'s a ton of layout managers on the web, and I bet there'符合您的要求 .

  • 6

    一些启发式:

    • 当你真的要覆盖 get[Preferred|Maximum|Minimum]Size() 时,不要使用 set[Preferred|Maximum|Minimum]Size() ,这可能与创建自己的组件有关,如here所示 .

    • 当您可以依赖于小心重写的组件 getPreferred|Maximum|Minimum]Size 时,请不要使用 set[Preferred|Maximum|Minimum]Size() ,如here及以下所示 .

    • 使用 set[Preferred|Maximum|Minimum]Size() 派生后 validate() 几何,如下所示和here .

    • 如果某个组件没有首选大小,例如 JDesktopPane ,您可能需要调整容器大小,但任何此类选择都是任意的 . 评论可能有助于澄清意图 .

    • 当您发现必须遍历许多组件以获取派生大小时,请考虑备用或自定义布局,如comments中所述 .

    enter image description here

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

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

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

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

    最重要的是,无论您如何指定组件的大小,请确保组件的容器使用的布局管理器遵循所请求的组件大小 .

    这些方法已被定义为一个原因 . 那我什么时候应该使用它们?在哪种情况下?为了什么目的?

    当您需要为容器布局管理器提供自定义大小提示时,以便组件布局良好

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

    • 许多布局管理器不关注组件请求的最大大小 . 但是, BoxLayoutSpringLayout 这样做 . 此外, GroupLayout 提供了明确设置最小,首选或最大大小的功能,而无需触及组件 .

    • 确保您确实需要设置组件的确切大小 . 每个Swing组件都有不同的首选大小,具体取决于它使用的字体和外观 . 因此,设置大小可能会在不同的系统上产生不同的UI外观

    • 有时 GridBagLayout 和文本字段可能会遇到问题,其中如果容器的大小小于首选大小,则使用最小大小,这可能导致文本字段显着缩小 .

    • JFrame 没有强制覆盖 getMinimumSize() 仅在其工作中调用 setMinimumSize(..)

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

    如果通过实施你的意思是使用然后是的 . 没有一个 LayoutManger 可以处理所有事情,每个 LayoutManager 都有它的优点和缺点,因此每个都可以一起使用来产生最终的布局 .

    Reference:

  • 15

    Should I completely avoid the use of those methods? 我不会说如果你认为你需要它们,你可能会做错事 . 组件大小根据上下文确定 . 例如,文本组件大小由您指定的行数和列数以及您可能选择的字体组成 . 您的按钮和标签大小将是图形的大小,如果您设置了一个,或显示您设置的文本所需的空间 . 每个组件都有一个自然的大小,布局管理器将使用它们来完成所有内容,而无需指定大小 . 主要的例外是JScrollPane,其大小与其包含的内容无关 . 对于那些,我有时会调用 setSize() ,并通过调用 JFrame.pack() 让该大小确定初始窗口大小 . 通常,我会让窗口大小确定JScrollPane的大小 . 用户将确定窗口的大小 . 许多布局管理员都会忽略您设置的大小,因此他们通常不会做太多好事 .

    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 ? 你不应该难以维持 . 当我使用它们时,我会给每个人一个自己的创建方法,以便更容易看到每个人做什么 . 但我从来没有"implement"布局经理 . 我只是实例化它们 .

    If the answer to 4 is "yes", won't this lead to a proliferation of LayoutManager classes which will become difficult to maintain? 如果你__40449_重新使用它们错了 . 如果你__40450_可能做错了什么 . 我唯一一次扩展了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? JSplitPane有一种方法可以指定每个组件应该获得的百分比 . 默认情况下,分隔符可以移动,但如果需要,可以将其关闭 . 我没有太多使用这个功能 . 我通常有一些组件占用一定的大小,剩下的空间由一个滚动窗格占用 . 滚动窗格大小将根据窗口大小进行调整 . 如果您有两个并排的滚动窗格,则可以将它们放在JSplitPane中,并指定在用户展开和收缩窗口时为每个窗格指定的新空间百分比 .

相关问题