提问人:Heisenbug 提问时间:8/29/2011 最后编辑:Ben the CoderHeisenbug 更新时间:4/11/2023 访问量:67464
我应该避免使用set(Preferred|最大值|Java Swing 中的最小)大小方法?
Should I avoid the use of set(Preferred|Maximum|Minimum) size methods in Java Swing?
问:
有几次,我因为建议使用以下方法而受到批评:
setPreferredSize()
setMinimumSize()
setMaximumSize()
在组件上。当我想定义显示组件之间的比例时,我看不到任何替代方法。我被告知:Swing
对于布局,答案总是一样的:使用合适的 LayoutManager。
我在网上搜索了一下,但没有找到任何关于这个主题的全面分析。所以我有以下问题:
- 我应该完全避免使用这些方法吗?
- 定义这些方法是有原因的。那么我应该什么时候使用它们呢?在什么情况下?出于什么目的?
- 使用这些方法的负面后果究竟是什么?(我只能考虑在具有不同屏幕分辨率的系统之间增加可移植性)。
- 我不认为任何 LayoutManager 可以完全满足所有所需的布局需求。我真的需要为布局上的每个小变化实现一个新的 LayoutManager 吗?
- 如果 4 的答案是“是”,这不会导致 LayoutManager 类的激增而变得难以维护吗?
- 在我需要定义组件的子项之间的比例的情况下(例如,child1 应使用 10% 的空间,child2 40%,child3 50%),是否可以在不实现自定义 LayoutManager 的情况下实现这一点?
答:
在我需要定义组件的子项之间的比例的情况下(子项 1 应使用 10% 的空间,子项 2 应使用 40%,子项 3 应使用 50%),是否可以在不实现自定义布局管理器的情况下实现这一点?
也许会满足您的需求。除此之外,网络上还有大量的布局管理器,我敢打赌有一个适合您的要求。GridBagLayout
评论
如果您在 Java Swing 中遇到布局问题,那么我强烈推荐 Karsten Lentzsch 作为 Forms 免费软件库的一部分免费提供的 JGoodies。FormLayout
这个非常流行的布局管理器非常灵活,可以开发非常精美的 Java UI。
你可以在这里找到 Karsten 的文档,以及一些来自 eclipse 的相当不错的文档。
我应该完全避免使用这些方法吗?
是,用于应用程序代码。
定义这些方法是有原因的。那么我应该什么时候使用它们呢?在什么情况下?出于什么目的?
我不知道,我个人认为这是 API 设计事故。对儿童尺寸有特殊想法的复合组件略微强迫。“稍微”,因为他们应该使用自定义 LayoutManager 实现他们的需求。
使用这些方法的负面后果究竟是什么?(我只能考虑在具有不同屏幕分辨率的系统之间增加可移植性。
例如,一些(不完整,不幸的是,由于 SwingLabs 迁移到 java.net,链接已损坏)技术原因在规则中提到(呵呵)或在他/她对我的答案的评论中找到的链接@bendicott中提到。在社交上,将大量工作交给你不幸的家伙,他必须维护代码,并且必须追踪一个损坏的布局。
我不认为任何 LayoutManager 可以完全满足所有所需的布局需求。我真的需要为布局上的每个小变化实现一个新的 LayoutManager 吗?
是的,有一些 LayoutManager 足够强大,可以很好地满足“所有布局需求”的近似值。三大分别是 JGoodies、FormLayout、MigLayout、DesignGridLayout。所以不,在实践中,除了简单的高度专业化环境外,你很少编写 LayoutManager。
如果 4 的答案是“是”,这不会导致 LayoutManager 类的激增而变得难以维护吗?
(4 的答案是否定的。
在我需要定义组件的子项之间的比例的情况下(例如,子项 1 应使用 10% 的空间,子项 2 应使用 40%,子项 3 应使用 50%),是否可以在不实现自定义 LayoutManager 的情况下实现这一点?
三巨头中的任何一个都可以,甚至不能 GridBag(从来没有费心真正掌握,太多的麻烦,太少的动力)。
评论
JEditorPane
一些启发式方法:
当您真正想要覆盖时,请不要使用,就像在创建自己的组件时所做的那样,如下所示。
set[Preferred|Maximum|Minimum]Size()
get[Preferred|Maximum|Minimum]Size()
当您可以依赖组件的小心覆盖时,请不要使用,如下所示。
set[Preferred|Maximum|Minimum]Size()
getPreferred|Maximum|Minimum]Size
请用于推导后几何,如下所示和此处。
set[Preferred|Maximum|Minimum]Size()
validate()
如果组件没有首选大小,例如 ,您可能必须在调用 之后调整容器的大小,但任何此类选择都是任意的。评论可能有助于澄清意图。
JDesktopPane
pack()
当您发现必须遍历多个组件才能获得派生大小时,请考虑使用备用布局或自定义布局,如这些注释中所述。
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());
}
}
}
}
评论
setPreferredSize()
我应该完全避免使用这些方法吗?
不可以,没有正式证据表明不允许调用或覆盖这些方法。事实上,甲骨文表示,这些方法用于给出大小提示:http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html#sizealignment。
在扩展 Swing 组件(而不是在自定义组件实例上调用该方法)时,它们也可能被重写(这是 Swing 的最佳实践)
最重要的是,无论您如何指定组件的大小,请确保组件的容器使用遵循请求的组件大小的布局管理器。
定义这些方法是有原因的。那么我应该什么时候使用它们呢? 在什么情况下?出于什么目的?
当您需要向容器布局管理器提供自定义大小提示时,以便组件布局良好
使用这些方法的负面后果究竟是什么?(一世 只能考虑增加不同系统之间的可移植性 屏幕分辨率)。
许多布局管理器不注意组件请求的最大大小。但是,确实如此。此外,它还能够显式设置最小、首选或最大尺寸,而无需接触组件。
BoxLayout
SpringLayout
GroupLayout
确保您确实需要设置组件的确切大小。每个 Swing 组件都有不同的首选大小,具体取决于它使用的字体和外观。因此,设置大小可能会在不同的系统上产生不同的 UI 外观
有时会遇到文本字段的问题,其中,如果容器的大小小于首选大小,则使用最小大小,这可能会导致文本字段大幅缩小。
GridBagLayout
JFrame
不强制覆盖,仅调用其作品getMinimumSize()
setMinimumSize(..)
我不认为任何 LayoutManager 可以完全满足所有所需的布局 需要。我真的需要为每个 LayoutManager 实现一个新的 LayoutManager 吗? 我的布局变化不大?
如果通过实现,你的意思是使用,那么是的。没有一个可以处理所有事情,每个都有其优点和缺点,因此每个都可以一起使用以产生最终的布局。LayoutManger
LayoutManager
参考:
评论
setXxxSize
getXxxSize
大多数人对这些方法知之甚少。您绝对不应该忽视这些方法。由布局管理器决定他们是否遵循这些方法。此页面有一个表格,显示哪些布局管理器支持以下哪些方法:
http://thebadprogrammer.com/swing-layout-manager-sizing/
我已经编写 Swing 代码 8+ 年了,JDK 中包含的布局管理器一直满足我的需求。我从来不需要第三方布局管理器来实现我的布局。
我要说的是,在你确定需要它们之前,你不应该尝试用这些方法给布局管理器提示。在不给出任何大小提示的情况下进行布局(即让布局管理器完成其工作),然后如果需要,您可以进行细微的更正。
评论
这里有很多很好的答案,但我想补充一点关于你通常应该避免这些的原因(这个问题刚刚在一个重复的主题中再次出现):
除了少数例外,如果您使用这些方法,您可能正在微调您的 GUI,使其在特定的外观和感觉上看起来不错(以及您的系统特定设置,例如您喜欢的桌面字体等)。这些方法本身并不是邪恶的,但使用它们的典型原因是邪恶的。一旦您开始调整布局中的像素位置和大小,您就有可能在其他平台上破坏 GUI(或至少看起来很糟糕)。
例如,尝试更改应用程序的默认外观。即使只是使用平台上可用的选项,您也可能会惊讶于结果的呈现效果有多差。
因此,为了让你的 GUI 在所有平台上都能正常运行和美观(请记住,Java 的主要好处之一是它的跨平台性),你应该依靠布局管理器等来自动调整组件的大小,以便它在特定开发环境之外正确呈现。
综上所述,您当然可以想象这些方法是合理的情况。同样,它们本质上并不是邪恶的,但它们的使用通常是一个大的危险信号,表明潜在的 GUI 问题。只要确保你意识到,如果你/当你使用它们时,很有可能出现并发症,并始终尝试思考是否有另一种独立于外观和感觉的解决方案来解决你的问题--通常你会发现这些方法不是必需的。
顺便说一句,如果你发现自己对标准的布局管理器感到沮丧,有很多很好的免费开源第三方管理器,例如JGoodies的FormLayout
或MigLayout
。一些 GUI 构建器甚至内置了对第三方布局管理器的支持——例如,Eclipse 的 WindowBuilder GUI 编辑器附带了对 和 的支持。FormLayout
MigLayout
评论
JTextField.setColumns
设置列计数,从而相应地调整首选大小。如果你这样做,那么你就是在硬编码一个大小,这将根据平台字体大小破坏你的布局。下面是网格包布局中的一些文本字段,其中适当调用了 setColumns
。对于按钮,请使用适当的布局/网格粗细来控制大小。setPreferredSize
我看到它与公认的答案不同。
1) 我应该完全避免使用这些方法吗?
永远不要回避!它们用于向布局管理器表达组件的大小限制。如果您不使用任何布局管理器,则可以避免使用它们,并尝试自行管理可视化布局。
不幸的是,Swing 没有合理的默认尺寸。但是,与其设置组件的尺寸,不如使用合理的默认值降低自己的组件。(在这种情况下,您可以在后代类中调用 setXXX。或者,您可以覆盖 getXXX 方法以获得相同的效果。
2)定义方法有原因。那么我应该什么时候使用它们呢?在什么情况下?出于什么目的?
总是。创建组件时,请根据该组件的使用情况设置其实际的最小/首选/最大大小。例如,如果您有一个用于输入国家/地区符号(如英国)的 JTextField,则其首选大小应与适合两个字符(使用当前字体等)一样宽,但让它变大可能毫无意义。毕竟,国家符号是两个字符。 相反,如果您有一个用于输入客户名称的 JTextField,它可以具有 20 个字符的首选大小,但如果调整布局大小,它可能会变得更大,因此将最大大小设置为更多。同时,拥有 0px 宽的 JTextField 是没有意义的,因此请设置一个实际的最小大小(我会说 2 个字符的像素大小)。
3)使用这些方法的负面后果究竟是什么?
(我只能考虑在具有不同屏幕分辨率的系统之间增加可移植性)。
没有负面后果。这些是布局管理器的提示。
4)我不认为任何布局管理器可以完全满足所有所需的布局需求。
我真的需要为布局上的每个小变化实现一个新的 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,它允许您以百分比、像素和其他单位设置此类约束。
评论
btnBar.setPreferredSize( dimTouchBtn );
GridBagLayout
BorderLayout
BoxLayout
我应该完全避免使用这些方法吗?我不会说“避免”它们。我想说的是,如果你认为你需要它们,你可能做错了什么。组件大小是在上下文中确定的。例如,文本组件大小由指定的行数和列数以及可能选择的字体决定。按钮和标签大小将是图形的大小(如果已设置)或显示所设置文本所需的空间。每个组件都有一个自然大小,布局管理器将使用这些组件来布置所有内容,而无需您指定大小。主要的例外是 JScrollPane,它的大小与它所包含的任何内容无关。对于这些,我有时会调用 ,并让该大小确定初始窗口大小,方法是调用 。通常,我会让窗口大小决定 JScrollPane 的大小。用户将确定窗口的大小。许多布局管理器无论如何都会忽略您设置的大小,因此它们通常不会做太多好事。setSize()
JFrame.pack()
定义这些方法是有原因的。那么我应该什么时候使用它们呢?在什么情况下?出于什么目的?我相信添加它们是为了向布局管理器提供提示。它们可能是出于历史原因而编写的,因为布局管理器是新来的,人们并不完全信任他们。我认识一些开发人员,他们避免使用布局管理器并手动放置所有内容,只是因为他们不想费心学习新的范式。这是一个可怕的主意。
使用这些方法的负面后果究竟是什么?(我只能考虑在具有不同屏幕分辨率的系统之间增加可移植性)。它们效率低下,并且会产生糟糕的布局,物体被挤压或拉伸到非自然大小。而且布局会很脆弱。更改窗口大小有时会破坏布局并将内容放在错误的位置。
我不认为任何 LayoutManager 可以完全满足所有所需的布局需求。我真的需要为布局上的每个小变化实现一个新的 LayoutManager 吗?不应“实现”新的 LayoutManager。您应该实例化现有的。我经常在一个窗口中使用多个布局管理器。每个 JPanel 都有自己的布局管理器。有些人对嵌套布局犹豫不决,因为它们很难维护。当我使用它们时,我给每个人自己的创建方法,以便更容易看到每个人的作用。但我从不“实现”布局管理器。我只是实例化它们。
如果 4 的答案是“是”,这不会导致 LayoutManager 类的激增而变得难以维护吗?如果要为布局中的细微变化实现新的布局管理器类,则使用它们是错误的。如果您只是实现新的布局管理器,那么您可能做错了什么。我唯一一次扩展 LayoutManager 类,是向 JScrollPane 添加缩放滑块。
在我需要定义组件的子项之间的比例的情况下(例如,child1 应使用 10% 的空间,child2 40%,child3 50%),是否可以在不实现自定义 LayoutManager 的情况下实现这一点?JSplitPane 有一种方法可以指定每个组件应获得的百分比。默认情况下,分隔线是可移动的,但您可以根据需要将其关闭。我不怎么使用这个功能。我通常有一些组件占用一定大小,其余空间由滚动窗格占用。滚动窗格大小将随窗口大小进行调整。如果有两个并排滚动窗格,则可以将它们放在 JSplitPane 中,并指定在用户展开和收缩窗口时为每个滚动窗格提供的新空间百分比。
评论