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

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

  1. setPreferredSize
  2. setMinimumSize
  3. setMaximumSize

Swing组件。当我想要定义显示的组件之间的比例时,我没有看到任何替代它们的使用。有人告诉我:

对于布局,答案总是一样的:使用一个合适的 LayoutManager < / p >

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

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

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

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

如果你在Java Swing的布局上遇到了麻烦,那么我可以强烈推荐由Karsten Lentzsch 在这里免费提供的JGoodies FormLayout作为表单免费库的一部分。

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

你可以在在这里中找到Karsten的文档,还有一些来自eclipse 在这里的很好的文档。

    <李> < blockquote >

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

    <李> < blockquote >

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

    我不知道,我个人认为这是一个API设计事故。复合组件对子尺寸有特殊的想法。“稍微”,因为他们应该用自定义的LayoutManager实现他们的需求 <李> < blockquote >

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

    一些技术原因(不完整,不幸的是链接由于SwingLabs迁移到java.net而中断)例如在规则(呵呵)链接 @bendicott中提到,在他/她对我的答案的评论中发现。在社交方面,你的不幸的同事不得不维护代码并追踪一个坏的布局,这给他带来了大量的工作 <李> < blockquote >

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

    是的,有足够强大的layoutmanager来满足“所有布局需求”。最大的三个是JGoodies FormLayout, MigLayout, DesignGridLayout。因此,在实践中,除了简单的高度专门化的环境,您很少编写LayoutManagers <李> < blockquote >

    如果4的答案是“是”,这是否会导致LayoutManager类的激增,从而变得难以维护?

    (4的答案是“否”)

    <李> < blockquote >

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

    三巨头中的任何一个都可以,甚至连GridBag都不能(从未费心去真正掌握,太麻烦了,太弱了)

以下是一些启发:

  • 当你真的想要覆盖get[Preferred|Maximum|Minimum]Size()时,不要使用set[Preferred|Maximum|Minimum]Size(),就像在创建自己的组件时所做的那样,如在这里所示。

  • 当你可以依赖于一个组件小心覆盖的getPreferred|Maximum|Minimum]Size时,不要使用set[Preferred|Maximum|Minimum]Size(),如在这里和下面所示。

  • 确实使用set[Preferred|Maximum|Minimum]Size()来派生后-validate()几何,如下所示和在这里

  • 如果一个组件没有首选的大小,例如JDesktopPane,你可能必须调整容器的大小,调用pack(),但任何这样的选择都是任意的。注释可以帮助阐明意图。

  • 当你发现你必须循环遍历许多组件以获得派生的大小时,考虑替代或自定义布局,如这些评论中提到的。

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

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

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

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

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

方法的定义是有原因的。那么什么时候使用它们呢? 在什么情况下?为了什么目的?< / p >

当你需要为容器布局管理器提供自定义大小提示时,组件将被很好地布局

使用这些方法的负面后果到底是什么?(我 只能想着在不同的系统之间添加可移植性 屏幕分辨率)。< / p >
  • 许多布局管理器不注意组件所要求的最大大小。然而,BoxLayoutSpringLayout可以。此外,GroupLayout提供了显式设置最小、首选或最大大小的能力,而不涉及组件。

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

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

  • JFrame不会强制重写getMinimumSize(),只在其工作时调用setMinimumSize(..)

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

如果你说的实现是指使用,那么是的。不是一个LayoutManger可以处理所有的事情,每个LayoutManager都有它的优点和缺点,因此每个LayoutManager都可以一起使用来生成最终的布局。

参考:

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

http://thebadprogrammer.com/swing-layout-manager-sizing/ < a href = " http://thebadprogrammer.com/swing-layout-manager-sizing/ " > < / >

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

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

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

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

例如,尝试更改应用程序的默认外观。即使只是使用平台上可用的选项,您也可能会惊讶于结果呈现的糟糕程度。

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

综上所述,您当然可以设想这些方法是合理的。同样,它们本身并不是邪恶的,但它们的使用通常是的危险信号,表明潜在的GUI问题。只要确保你意识到如果/当你使用它们时可能会出现很高的并发症,并且总是尝试和思考是否有另一种外观和感觉独立的解决方案来解决你的问题——通常你会发现这些方法是不必要的。

顺便说一下,如果你发现自己对标准的布局管理器感到沮丧,有很多好的免费、开源的第三方管理器,例如JGoodies”FormLayoutMigLayout。一些GUI构建器甚至内置了对第三方布局管理器的支持——例如,Eclipse的WindowBuilder GUI编辑器附带了对FormLayoutMigLayout的支持。

我的看法与公认的答案不同。

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

不要避免!它们的作用是向布局管理器表达组件的大小限制。如果你不使用任何布局管理器,你可以避免使用它们,并尝试自己管理可视化布局。

不幸的是,Swing没有提供合理的默认尺寸。但是,与其设置组件的尺寸,不如使用合理的默认值降低您自己的组件。(在这种情况下,在你的子类中调用setXXX。)或者,您可以重写getXXX方法以达到同样的效果。

2)定义方法是有原因的。那么什么时候使用它们呢?在什么情况下?为了什么目的?

< p >。当您创建一个组件时,根据该组件的使用设置其实际的最小/首选/最大大小。例如,如果你有一个JTextField输入国家符号,如英国,其首选的大小应宽到适合两个字符(与当前字体等),但可能是没有意义的,让它增长任何更大。毕竟,国家的象征是两个字母。 相反,如果你有一个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,它允许您以百分比、像素和其他单位设置此类约束。

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

定义这些方法是有原因的。那么什么时候使用它们呢?在什么情况下?为了什么目的? 我相信添加它们是为了给布局管理器提供提示。它们可能是由于历史原因而编写的,因为布局管理器是新的,人们并不完全信任它们。我认识一些开发人员,他们避免使用布局管理器,并手动放置所有东西,只是因为他们不想费心学习新的范例。

.这是个糟糕的主意

使用这些方法的负面后果到底是什么?(我只能考虑在不同屏幕分辨率的系统之间增加可移植性)。 它们是无效的,而且会产生糟糕的布局,物体会被挤压或拉伸到非自然的大小。而且布局会很脆弱。改变窗口大小有时会破坏布局,把东西放在错误的地方

你不应该“实现”一个新的LayoutManager。您应该实例化现有的。我经常在一个窗口中使用多个布局管理器。每个JPanel都有自己的布局管理器。有些人不喜欢嵌套布局,因为它们很难维护。当我使用它们时,我给每个对象提供了自己的创建方法,以便更容易看到每个对象的功能。但我从未“实现”一个布局管理器。我只是实例化了它们。

如果4的答案是“是”,这是否会导致LayoutManager类的激增,这将变得难以维护? 如果你正在为布局中的轻微变化实现新的布局管理器类,那么你使用它们是错误的。如果你只是实现新的布局管理器,你可能做错了什么。我唯一一次扩展LayoutManager类,它是添加一个缩放滑块到JScrollPane

在一种情况下,我需要定义一个组件的子组件之间的比例(例如,child1应该使用10%的空间,child2 40%,child3 50%),是否有可能在不实现自定义布局管理器的情况下实现这一点? JSplitPane有一种方法可以指定每个组件应该获得的百分比。默认情况下,分隔符是可移动的,但如果您愿意,可以将其关闭。我不怎么使用这个功能。我通常有一些组件占用一个设置的大小,其余的空间由滚动窗格占用。滚动窗格大小将随窗口大小调整。如果您有两个并排的滚动窗格,您可以将它们放在JSplitPane中,并指定当用户展开和收缩窗口时给予每个窗格的新空间的百分比