我应该避免在Java Swing中使用set(Preferred | Maximum | Minimum)Size方法吗?
有好几次因为build议使用以下方法而受到批评:
- 有必要对setPreferredSize
- 了setMinimumSize
- setMaximumSize
在Swing
组件上。 当我想要定义显示的组件之间的比例时,我没有看到它们使用的替代scheme。 我被告知这个:
对于布局,答案总是相同的:使用合适的LayoutManager
我已经在网上search了一下,但是我还没有find有关这个主题的全面分析。 所以我有以下问题:
- 我应该完全避免使用这些方法吗?
- 方法已经定义了一个原因。 那我应该什么时候使用它们? 在哪个上下文中? 为了什么目的?
- 使用这些方法的负面后果究竟是什么? (我只能想到添加不同屏幕分辨率的系统之间的可移植性)。
- 我不认为任何的布局pipe理器可以完全满足所有需要的布局。 我真的需要为布局中的每一个小变化实现一个新的LayoutManager吗?
- 如果4的答案是“是”,这是否会导致LayoutManager类的扩散,这将变得难以维护?
- 在我需要定义Component之间的比例的情况下(例如,child1应该使用10%的空间,child2 40%,child3 50%),是否可以在不实现自定义LayoutManager的情况下实现这一点?
我希望已经清楚。
-
我应该完全避免使用这些方法吗?
是的应用程序代码。
-
方法已经定义了一个原因。 那我应该什么时候使用它们? 在哪个上下文中? 为了什么目的?
我不知道,我个人认为它是一个APIdevise意外。 对复合材料零部件有一些特别的想法, “稍微”,因为他们应该已经实现了自定义LayoutManager的需要。
-
使用这些方法的负面后果究竟是什么? (我只能认为添加不同屏幕分辨率的系统之间的可移植性。)
有些(不完整的,不幸的是,由于SwingLabs迁移到java.net导致链接被破坏)技术上的原因是在规则(hehe)中提到的,或者在我的答案的评论中find@bendicott 链接 。 从社会angular度来说,把不计其数的工作带到你不得不维护代码的不幸的人身上,而且必须找出一个破碎的布局。
-
我不认为任何的布局pipe理器可以完全满足所有需要的布局。 我真的需要为布局中的每一个小变化实现一个新的LayoutManager吗?
是的,布局pipe理器的function足以满足“所有布局需求”的非常好的近似。 大三是JGoodies的FormLayout,MigLayout,DesignGridLayout。 所以不,在实践中,除了简单的高度专业化的环境之外,你很less编写LayoutManagers。
-
如果4的答案是“是”,这是否会导致LayoutManager类的扩散,这将变得难以维护?
(答案4是“否”。)
-
在我需要定义一个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理员不注意组件所要求的最大尺寸。 但是,
BoxLayout
和SpringLayout
。 此外,GroupLayout
能够在不触及组件的情况下明确设置最小,首选或最大尺寸。 -
确保您确实需要设置组件的确切大小。 每个Swing组件都有不同的首选大小,具体取决于它使用的字体和外观。 因此,设置大小可能会在不同系统上产生不同UI的外观
-
有时
GridBagLayout
和文本字段会遇到问题,其中如果容器的大小小于首选大小,则会使用最小大小,这可能导致文本字段显着缩小。 -
JFrame
不强制执行overridengetMinimumSize()
只调用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编辑器支持FormLayout
和MigLayout
。
如果您在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,它允许您以百分比,像素和其他单位设置这些约束。