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

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

  1. 有必要对setPreferredSize
  2. 了setMinimumSize
  3. setMaximumSize

Swing组件上。 当我想要定义显示的组件之间的比例时,我没有看到它们使用的替代scheme。 我被告知这个:

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

我已经在网上search了一下,但是我还没有find有关这个主题的全面分析。 所以我有以下问题:

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

我希望已经清楚。

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

    是的应用程序代码。

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

    我不知道,我个人认为它是一个APIdevise意外。 对复合材料零部件有一些特别的想法, “稍微”,因为他们应该已经实现了自定义LayoutManager的需要。

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

    有些(不完整的,不幸的是,由于SwingLabs迁移到java.net导致链接被破坏)技术上的原因是在规则(hehe)中提到的,或者在我的答案的评论中find@bendicott 链接 。 从社会angular度来说,把不计其数的工作带到你不得不维护代码的不幸的人身上,而且必须找出一个破碎的布局。

  4. 我不认为任何的布局pipe理器可以完全满足所有需要的布局。 我真的需要为布局中的每一个小变化实现一个新的LayoutManager吗?

    是的,布局pipe理器的function足以满足“所有布局需求”的非常好的近似。 大三是JGoodies的FormLayout,MigLayout,DesignGridLayout。 所以不,在实践中,除了简单的高度专业化的环境之外,你很less编写LayoutManagers。

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

    (答案4是“否”。)

  6. 在我需要定义一个Component的子元素之间的比例的情况下(例如,子元素1应该使用10%的空间,子元素2 40%,子元素3 50%),是否可以在不实现自定义LayoutManager的情况下实现这一点?

    任何一个Big-Three都可以,甚至不能GridBag(从来没有打扰到真正的掌握,太less的权力太麻烦了)。

一些启发式:

  • 当你确实想要覆盖get[Preferred|Maximum|Minimum]Size() ,不要使用set[Preferred|Maximum|Minimum]Size() get[Preferred|Maximum|Minimum]Size() ,这可能与创build自己的组件一样。

  • 如下所示,不要使用set[Preferred|Maximum|Minimum]Size() ,如果可以依赖组件仔细覆盖的getPreferred|Maximum|Minimum]Size

  • 请使用set[Preferred|Maximum|Minimum]Size()来派生post- validate()几何,如下面和这里所示。

  • 如果组件没有首选大小,例如JDesktopPane ,则可能必须调整容器的大小,但是这样的select是任意的。 评论可能有助于澄清意图。

  • 当您发现您将不得不循环许多组件来获取派生大小时,请考虑备用或自定义布局,如这些注释中所述 。

在这里输入图像描述

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

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

不,没有任何forms的证据表明打电话或者不允许这些方法被覆盖。 事实上,甲骨文表示这些方法是用来给大小的提示: http : //docs.oracle.com/javase/tutorial/uiswing/layout/using.html#sizealignment 。

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

最重要的是,无论您如何指定组件的大小,都要确保组件的容器使用一个尊重组件所需大小的布局pipe理器。

方法已经定义了一个原因。 那我应该什么时候使用它们? 在哪个上下文中? 为了什么目的?

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

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

  • 许多布局pipe理员不注意组件所要求的最大尺寸。 但是, BoxLayoutSpringLayout 。 此外, GroupLayout能够在不触及组件的情况下明确设置最小,首选或最大尺寸。

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

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

  • JFrame不强制执行overriden getMinimumSize()只调用setMinimumSize(..)作品

我不认为任何的布局pipe理器可以完全满足所有需要的布局。 我真的需要为布局中的每一个小变化实现一个新的LayoutManager吗?

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

参考:

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

除less数例外,如果您正在使用这些方法,则可能需要对GUI进行微调,以便在特定的外观(以及系统特定的设置,例如您喜欢的桌面字体等)上看起来不错。 方法本身并不是天生的邪恶,但使用它们的典型原因 。 只要开始调整布局中的像素位置和大小,就会在其他平台上发生GUI中断(或者至less看起来很糟糕)的风险。

作为一个例子,尝试改变你的应用程序的默认外观。 即使只是在您的平台上提供的选项,您可能会惊讶的结果可以得到很好的performance。

所以,为了保持GUI的function和所有平台上的漂亮外观(记住,Java的主要好处之一是它的跨平台性),您应该依靠布局pipe理器等来自动调整您的组件,以便在特定的开发环境之外正确渲染。

所有这一切,你当然可以想象这些方法是合理的情况。 再次,它们本质上并不是邪恶的,但是它们的使用通常是显示潜在的GUI问题的红旗。 只要确保自己在使用这些复杂function时意识到了复杂性的巨大潜力,并总是试着去思考如果还有另外一个与您的问题无关的独立的解决scheme – 往往会发现这些方法是没有必要的。

顺便说一句,如果你发现自己对标准版面pipe理者感到沮丧,那么有很多好的免费的开源第三方版本,例如JGoodies的FormLayout或者MigLayout 。 有些GUI构build器甚至还内置了对第三方布局pipe理器的支持 – 例如,Eclipse的WindowBuilder GUI编辑器支持FormLayoutMigLayout

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

这个非常受欢迎的布局pipe理器非常灵活,可以开发非常精美的Java UI。

你会在这个PDF文件中findKarsten的文档,还有一些来自google的相当不错的文档。

在我需要定义一个Component的子元素之间的比例的情况下(子元素1应该使用10%的空间,child2 40%,child3 50%),是否有可能在不实现自定义布局pipe理器的情况下实现?

也许GridBagLayout将满足您的需求。 除此之外,networking上还有很多布局pipe理员,我敢打赌有一个符合你的要求。

这些方法很难被大多数人所了解。 你绝对不应该忽视这些方法。 如果他们遵守这些方法,由布局经理来决定。 这个页面有一个表格,显示了哪些布局pipe理器遵循哪些方法:

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

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

我会说,除非您确定需要这些方法,否则不应该尝试使用这些方法给布局pipe理员提示。 做你的布局没有给任何尺寸提示(即让布局经理工作),然后你可以做一些小的改正,如果你需要的话。

我看到的不同于接受的答案。

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

切勿避免! 他们在那里向布局pipe理器expression组件的大小限制。 如果您不使用任何布局pipe理器并尝试自行pipe理可视布局,则可以避免使用它们。

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

2)方法已被定义为一个原因。 那我应该什么时候使用它们? 在哪个上下文中? 为了什么目的?

总是。 创build组件时,根据组件的使用情况设置其实际的最小/首选/最大大小。 例如,如果您有用于input国家/地区符号(例如英国)的JTextField,则它的首选大小应该宽到适合两个字符(包括当前的字体等),但是可能让它变大一些是没有意义的。 毕竟,国家的符号是两个字符。 相反,如果您有用于input客户名称的JTextField,则它可以具有像20个字符的像素大小的首选大小,但如果布局resize,则可以增大到更大,因此将最大大小设置为更大。 同时,有一个0px宽的JTextField是毫无意义的,所以设置一个现实的最小尺寸(我会说2个字符的像素大小)。

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

(我只能想到添加不同屏幕分辨率的系统之间的可移植性)。

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

4)我不认为任何的布局pipe理器可以完全满足所有需要的布局。

我真的需要为布局中的每一个小变化实现一个新的LayoutManager吗?

不,绝对不是。 通常的做法是级联不同的基本布局pipe理者,例如水平和垂直布局。

例如,下面的布局:

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

有两个部分。 左侧和右侧部分是水平布局。 右边部分是添加到水平布局的JPanel,这个JPanel是垂直布局的垂直布局。

当然,这可能会变得棘手,实际的生活布局。 因此,如果您要开发任何严重的事情,那么基于网格的布局pipe理器(如MigLayout)会更好。

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

不,你绝对不能开发布局pipe理者,除非你需要非常特别的东西。

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

一个Component的子元素之间(例如,child1应该使用10%的空间,child2 40%,child3 50%),是否有可能实现,而不实现一个自定义LayoutManager?

基本上,一旦适当的大小设置,你可能不想做任何事情的百分比。 简单地说,因为百分比是毫无意义的(例如,将JTextField的大小设置为窗口大小的10%是毫无意义的 – 因为可以缩小窗口的大小,使得JTextField的宽度变为0px,或者可以展开窗口,使JTextField跨越多显示器设置)。

但是,有时可能会使用百分比来控制gui(面板)的较大构build块的大小。

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