Hei*_*bug 471 java swing layout-manager
有几次我因为建议使用以下方法而受到批评:
在Swing
组件上.当我想在显示的组件之间定义比例时,我没有看到任何替代它们的用法.我被告知这个:
对于布局,答案总是相同的:使用合适的LayoutManager
我在网上搜索了一下,但我没有找到任何关于这个主题的综合分析.所以我有以下问题:
kle*_*tra 236
我应该完全避免使用这些方法吗?
是应用程序代码.
已经定义了这些方法的原因.那我什么时候应该使用它们?在哪种情况下?出于什么目的?
我不知道,我个人认为它是一个API设计事故.稍微强迫复合组件对孩子大小有特殊想法."稍微",因为他们应该使用自定义LayoutManager实现他们的需求.
使用这些方法的负面后果究竟是什么?(我只能考虑在具有不同屏幕分辨率的系统之间添加可移植性.)
一些(不完整的,不幸的是由于SwingLabs迁移到java.net而导致链接断开)技术原因例如在规则(hehe)中或在他/她对我的回答的评论中找到的@bendicott 链接中提到.在社交方面,为你不幸的家伙提供大量的工作,他必须维护代码并且必须追踪破碎的布局.
我认为任何LayoutManager都不能完全满足所有需要的布局需求.我是否真的需要为布局上的每个小变化实现一个新的LayoutManager?
是的,有一些强大的LayoutManagers可以满足"所有布局需求"的非常好的近似.三巨头是JGoodies FormLayout,MigLayout,DesignGridLayout.所以不,在实践中,除了简单的高度专业化的环境外,你很少编写LayoutManagers.
如果4的答案是"是",那么这是否会导致LayoutManager类的扩散变得难以维护?
(4的答案是"不".)
在我需要定义组件的子项之间的比例的情况下(例如,子项1应该使用10%的空间,子项2 40%,子项3 50%),是否可以在不实现自定义LayoutManager的情况下实现该比例?
任何一个三巨头都可以,甚至不能GridBag(从不打扰真正掌握,太少的力量太麻烦).
tra*_*god 99
一些启发式:
不要使用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
,您可能必须调整容器的大小,但任何此类选择都是任意的.评论可能有助于澄清意图.
当您发现必须遍历许多组件以获取派生大小时,请考虑备用或自定义布局,如这些注释中所述.
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());
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Dav*_*amp 46
我应该完全避免使用这些方法吗?
不,没有正式证据表明不允许调用或覆盖这些方法.事实上,Oracle表示这些方法用于提供大小提示:http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html#sizealignment.
在扩展 Swing组件时(而不是在自定义组件实例上调用方法),它们也可能被覆盖(这是Swing的最佳实践)
最重要的是,无论您如何指定组件的大小,请确保组件的容器使用的布局管理器遵循所请求的组件大小.
已经定义了这些方法的原因.那我什么时候应该使用它们?在哪种情况下?出于什么目的?
当您需要为容器布局管理器提供自定义大小提示时,以便组件布局良好
使用这些方法的负面后果究竟是什么?(我只能考虑在具有不同屏幕分辨率的系统之间添加可移植性).
许多布局管理器不关注组件所请求的最大大小.然而,BoxLayout
并SpringLayout
做.此外,GroupLayout
还提供了明确设置最小,首选或最大尺寸的功能,而无需触摸组件.
确保您确实需要设置组件的确切大小.每个Swing组件都有不同的首选大小,具体取决于它使用的字体和外观.因此,设置大小可能会在不同的系统上产生不同的UI 外观
有时可能遇到问题GridBagLayout
和文本字段,其中如果容器的大小小于首选大小,则使用最小大小,这可能导致文本字段显着缩小.
JFrame
不强制重写getMinimumSize()
仅调用setMinimumSize(..)
其工作
我认为任何LayoutManager都不能完全满足所有需要的布局需求.我是否真的需要为布局上的每个小变化实现一个新的LayoutManager?
如果通过实施你的意思是使用然后是的.没有人LayoutManger
可以处理所有事情,每个LayoutManager
都有其优点和缺点,因此每个可以一起使用来产生最终的布局.
参考:
Jas*_*n C 25
有很多在这里很好的答案,但我想补充一点关于原因,为什么你通常应该避免这些(问题只是在重复的主题又上来):
除了少数例外情况,如果您使用这些方法,您可能会对GUI进行微调,使其在特定的外观(以及您的系统特定设置,例如您首选的桌面字体等)上看起来很好.这些方法本身并不是邪恶的,但使用它们的典型原因是.一旦开始调整布局中的像素位置和大小,就会在其他平台上面临GUI破坏(或者至少看起来很糟糕)的风险.
作为示例,请尝试更改应用程序的默认外观.即使只是平台上提供的选项,您可能会惊讶于结果的呈现效果不佳.
因此,在所有平台上保持GUI功能和漂亮的名称(请记住,Java的主要优点之一是它的跨平台性),您应该依赖布局管理器等来自动调整大小.您的组件,以便它在您的特定开发环境之外正确呈现.
总而言之,你当然可以设想这些方法合理的情况.同样,它们本身并不是邪恶的,但它们的使用通常是一个大红旗,表明潜在的GUI问题.只要确保你在使用它们的时候意识到并发症的可能性很大,并且总是试着想一想你的问题是否有另一种看起来独立的解决方案 - 你经常会发现这些方法是没有必要的.
顺便说一句,如果您发现自己对标准布局管理器感到沮丧,那么有许多优秀的免费开源第三方管理器,例如JGoodiesFormLayout
,或者MigLayout
.一些GUI构建器甚至内置了对第三方布局管理器的支持 - 例如,Eclipse的WindowBuilder GUI编辑器附带支持FormLayout
和MigLayout
.
Mic*_*ael 16
大多数人都很难理解这些方法.你绝对不应该忽略这些方法.如果他们遵守这些方法,则由布局管理员决定.此页面有一个表格,显示哪些布局管理员会尊重以下哪些方法:
http://thebadprogrammer.com/swing-layout-manager-sizing/
我已经写了8年以上的Swing代码,JDK中包含的布局管理器一直满足我的需求.我从来没有需要第三方布局管理器来实现我的布局.
我会说你不应该尝试用这些方法给出布局管理器提示,直到你确定需要它们为止.在没有给出任何大小提示的情况下进行布局(即让布局管理器完成其工作),然后如果需要,可以进行小的修正.
Tho*_*mas 15
在我需要定义组件的子项之间的比例(子项1应该使用10%的空间,child2 40%,child3 50%)的情况下,是否可以在不实现自定义布局管理器的情况下实现这一点?
也许GridBagLayout
会满足你的需求.除此之外,网上还有大量的布局管理人员,我打赌有一个适合您的要求.
我看到它与接受的答案不同.
永远不要避免 他们在那里向布局管理器表达组件的大小限制.如果您没有使用任何布局管理器并尝试自行管理可视布局,则可以避免使用它们.
不幸的是,Swing没有合理的默认尺寸.但是,不是设置组件的尺寸,而是以合理的默认值下降自己的组件是更好的OOP.(在这种情况下,您在后代类中调用setXXX.)或者,您可以覆盖getXXX方法以获得相同的效果.
总是.创建组件时,请根据该组件的使用设置其实际的最小/首选/最大大小.例如,如果您有一个JTextField用于输入国家/地区符号(例如UK),则其首选大小应该宽到适合两个字符(使用当前字体等),但可能让它变得更大没有意义.毕竟,国家符号是两个字符.相反,如果你有一个JTextField用于输入例如客户名称,它可以有一个首选大小,如20个字符的像素大小,但如果调整布局可以增大,所以将最大大小设置为更多.同时,拥有0px宽的JTextField是没有意义的,所以设置一个真实的最小尺寸(我会说像素大小为2个字符).
(我只能考虑在具有不同屏幕分辨率的系统之间添加可移植性).
没有负面后果.这些是布局管理器的提示.
我是否真的需要为布局上的每个小变化实现一个新的LayoutManager?
不,绝对不是.通常的方法是级联不同的基本布局管理器,例如水平和垂直布局.
例如,下面的布局:
<pre>
+--------------+--------+
| ###JTABLE### | [Add] |
| ...data... |[Remove]|
| ...data... | |
| ...data... | |
+--------------+--------+
</pre>
Run Code Online (Sandbox Code Playgroud)
有两个部分.左右部分是水平布局.右边部分是添加到水平布局的JPanel,这个JPanel有一个垂直布局,垂直布置按钮.
当然,这可能会因现实生活布局而变得棘手.因此,如果您要开发任何严肃的东西,基于网格的布局管理器(如MigLayout)要好得多.
不,你肯定不会开发布局管理器,除非你需要非常特别的东西.
组件的子项之间(例如,child1应该使用10%的空间,child2 40%,child3 50%),是否可以在不实现自定义LayoutManager的情况下实现这一点?
基本上,一旦首选尺寸设置正确,您可能不希望以百分比做任何事情.简单地说,因为百分比是没有意义的(例如,JTextField占窗口大小的10%是没有意义的 - 因为可以缩小窗口以使JTextField变为0px宽,或者可以扩展窗口以使JTextField跨越两个显示器多显示器设置).
但是,有时您可以使用百分比来控制gui(例如面板)的更大构建块的大小.
您可以使用JSplitPane,您可以在其中预先设置双方的比例.或者,您可以使用MigLayout,它允许您以百分比,像素和其他单位设置此类约束.