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

时间:2011-08-29 11:02:32

标签: java swing layout-manager

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

  1. 了setPreferredSize
  2. 了setMinimumSize
  3. setMaximumSize
  4. Swing组件上。当我想在显示的组件之间定义比例时,我没有看到任何替代它们的用法。有人告诉我:

      

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

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

    1. 我应该完全避免使用这些方法吗?
    2. 这些方法的定义是有原因的。那我什么时候应该使用它们?在哪种情况下?出于什么目的?
    3. 使用这些方法的负面后果究竟是什么? (我只能考虑在具有不同屏幕分辨率的系统之间添加可移植性)。
    4. 我认为任何LayoutManager都不能完全满足所有需要的布局需求。我是否真的需要为布局上的每个小变化实现一个新的LayoutManager?
    5. 如果对4的回答是“是”,这是否会导致LayoutManager类的扩散变得难以维护?
    6. 在我需要定义Component的子节点之间的比例的情况下(例如,child1应该使用10%的空间,child2 40%,child3 50%),是否可以在不实现自定义LayoutManager的情况下实现这一点? / LI>

9 个答案:

答案 0 :(得分:233)

  1.   

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

    是应用程序代码。

  2.   

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

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

  3.   

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

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

  4.   

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

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

  5.   

    如果对4的回答是“是”,这是否会导致LayoutManager类的扩散变得难以维护?

    (4的回答是“不”。)

  6.   

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

    任何一个Big-Three都可以,甚至不能GridBag(从不打扰真正掌握,太少的力量太麻烦)。

答案 1 :(得分:98)

一些启发式方法:

  • 当您真正想要覆盖set[Preferred|Maximum|Minimum]Size()时,请不要使用get[Preferred|Maximum|Minimum]Size(),就像创建自己的组件时所做的那样,显示为here

  • 如果您可以依赖于set[Preferred|Maximum|Minimum]Size()所示的getPreferred|Maximum|Minimum]Size组件,请不要使用set[Preferred|Maximum|Minimum]Size()

  • 使用validate()派生后JDesktopPane几何图形,如下所示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()); } } } } ,您可能需要调整容器的大小,但任何此类选择都是任意的。评论可能有助于澄清意图。

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

enter image description here

{{1}}

答案 2 :(得分:46)

  

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

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

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

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

  

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

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

  

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

  • 许多布局管理员不关注组件请求的最大大小。但是,BoxLayoutSpringLayout会这样做。此外,GroupLayout可以明确设置最小,首选或最大大小,而无需触及组件。

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

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

  • JFrame未强制覆盖getMinimumSize()仅在其作品上调用setMinimumSize(..)

  

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

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

<强>参考:

答案 3 :(得分:25)

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

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

作为示例,请尝试更改应用程序的默认外观。即使只是平台上提供的选项,您也可能会对结果的呈现效果感到惊讶。

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

所有这一切,你当然可以设想这些方法合理的情况。同样,它们本身并不是邪恶的,但它们的使用通常是红旗,表明潜在的GUI问题。只要确保你在使用它们的时候意识到并发症的可能性很大,并且总是试着想一想你的问题是否有另一种看起来独立的解决方案 - 你经常会发现这些方法是没有必要的。

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

答案 4 :(得分:19)

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

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

你会在here找到Karsten的文档,并在eclipse here找到一些相当不错的文档。

答案 5 :(得分:16)

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

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

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

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

答案 6 :(得分:15)

  

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

也许GridBagLayout可以满足您的需求。除此之外,网上还有大量的布局管理人员,我打赌有一个适合您的要求。

答案 7 :(得分:6)

我认为它与接受的答案不同。

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的回答是&#34;是&#34;,那么这会导致LayoutManager类的扩散变得难以维护吗?

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

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

在组件的子项之间

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

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

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

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

答案 8 :(得分:0)

我应该完全避免使用这些方法吗? 我不会说“避免”它们。我想说的是,如果您认为自己需要它们,那可能是您做错了什么。组件的大小取决于上下文。例如,文本组件的大小取决于您指定的行数和列数以及您可能选择的字体。如果设置了一个,则按钮和标签的大小将是图形的大小,或者是显示设置的文本所需的空间。每个组件都有一个自然的大小,布局管理器将使用这些大小来布局所有内容,而无需指定大小。主要的例外是JScrollPane,它的大小与其所包含的内容无关。对于这些,我有时会调用setSize(),并通过调用JFrame.pack()来确定初始窗口的大小。通常,我将让窗口大小确定JScrollPane的大小。用户将确定窗口的大小。无论如何,许多布局管理器都会忽略您设置的尺寸,因此它们通常做得不好。

由于某种原因定义了方法。那么我什么时候应该使用它们呢?在哪种情况下?是出于什么目的? 我相信添加它们是为了向布局管理器提供提示。他们之所以写是因为历史原因,因为布局经理是新来的,人们并不完全信任他们。我知道一些开发人员避免布局管理器,而是手动放置所有内容,只是因为他们不想为学习新的范式而烦恼。这是一个可怕的主意。

使用这些方法的负面影响到底是什么? (我只能考虑在具有不同屏幕分辨率的系统之间增加可移植性。 它们效果不佳,并且布局不好,物体被挤压或拉伸到非自然尺寸。而且布局会很脆弱。更改窗口大小有时会破坏布局并将东西放置在错误的位置。

我认为任何LayoutManager都无法完全满足所有所需的布局需求。我真的需要为我的布局上的每个小变化实现一个新的LayoutManager吗?您不应该“实现”一个新的LayoutManager。您应该实例化现有的。我经常在一个窗口中使用多个布局管理器。每个JPanel都有自己的布局管理器。有些人不愿使用嵌套布局,因为它们很难维护。当我使用它们时,我给每个人自己的创建方法,以便更轻松地查看每个人的工作。但是我从来没有“实现”布局管理器。我只是实例化它们。

如果对4的回答为“是”,这是否会导致LayoutManager类激增,而这将变得难以维护? 如果您要实现新的布局管理器类以使布局略有变化,那么您使用的是错误的。如果您只是实施新的布局管理器,则可能是做错了什么。我唯一的一次扩展LayoutManager类是在JScrollPane中添加缩放滑块。

在一种情况下,我需要定义一个组件的子代之间的比例(例如,child1应该使用10%的空间,child2 40%,child3 50%的空间),是否有可能实现而无需实现自定义LayoutManager?? JSplitPane有一种方法可以指定每个组件应获得的百分比。默认情况下,分隔线是可移动的,但是您可以根据需要将其关闭。我没有太多使用该功能。我通常有一些组件会占据一定的大小,其余空间则由滚动窗格占据。滚动窗格的大小将根据窗口大小进行调整。如果有两个并排的滚动窗格,则可以将它们放在JSplitPane中,并指定随着用户扩展和收缩窗口而分配给每个滚动窗格的新空间的百分比。