【问题标题】:Looking for general method for GridBagLayout component creation寻找 GridBagLayout 组件创建的通用方法
【发布时间】:2014-01-27 10:44:43
【问题描述】:

我正在设计一个包含 20 个左右组件的 GUI:10 个标签、4 个文本字段、4 个按钮和 2 个文本区域。使用 GridBagLayout 似乎是个好主意。但是由于本书要求为每个组件执行所有实例变量(即不重用),添加组件的通用方法似乎是必须的。我真的认为这可行:

(注意:HORIZ 是 GridBagConstraints.HORIZONTAL 的缩写; CENTER 是 GridBagConstraints.CENTER 的缩写。)

public static void addComponent(Container f,      Component c, 
                                  int     x,        int   y, 
                                  int     w,        int   h, 
                                  int     ipadx,    int   ipady, 
                                  float   wtx,      float wty,
                                  int fill, int anchor, Insets insets){


  GridBagConstraints gbc = new GridBagConstraints();

  gbc.gridx = x;    gbc.gridy = y;      
  gbc.gridwidth = w;    gbc.gridheight = h;     
  gbc.fill = fill; 
  gbc.ipadx = ipadx;    gbc.ipady = ipady;  
  gbc.insets = insets;  gbc.anchor = anchor; 
  gbc.weightx = wtx;    gbc.weighty = wty;

  f.add(c,gbc);
}

我这样称呼它:

    Insets insets = new Insets(0,0,0,0);
    JFrame frame = new JFrame();
    label = new JLabel("Blablablah");   
    addComponent(frame, label, 0,0, 1,1, 0,0, 0.5f,0, HORIZ, CENTER, insets);

但我在f.add(c.gbc) 收到消息“无法添加到布局:约束必须是字符串(或 null)”。

我想我理解错误:frame 在调用addComponent 之前没有GridBagConstraintsgbc 在方法的第一行不属于参数f(或还有什么?)。

所以我稍微修改了方法签名,省略了Container

public static void addComponent(                  Component c, 
                                  int     x,        int   y, 
... (rest unchanged)

我修改了问题行如下:

frame.add(c, gbc);

所以我使用全局变量frame,而我宁愿将它作为参数传递。

两个问题:

(1) 有没有办法对我的代码进行最低限度的修改,以便将frame 传递给addComponent

(2) 有什么理由想要这样做吗?我想这相当于问,你会怎么做?


附:这是对修改后的addComponent 的调用,匆忙拼凑在一起以获得我想要的前几行的一些相似之处。间距现在很臭——我需要用插图、ipad、填充来玩弄——但它实际上是可用的。 (frame 的新名称是 GUI。)

private static void createAndShowGUI() {
  GUI = new JFrame();
  GUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  gbl = new GridBagLayout();
  GUI.setLayout(gbl);

  addComponent(lblRootNode, 0,0, 1,1, 0,0, 0.5f,0, HORIZONTAL, CENTER, new Insets(0,0,0,0));    
  addComponent(txtRootNode, 1,0, 5,1, 60,0, 0.5f,0, HORIZONTAL, CENTER, new Insets(0,0,0,0));    
  addComponent(btnBrowse,   6,0, 1,1, 0,0, 0.5f,0, HORIZONTAL, CENTER, new Insets(0,0,0,0));    
  addComponent(lblFilenamePat, 0,1, 2,1, 0,0, 0.5f,0, HORIZONTAL, EAST, new Insets(0,0,0,0));    
  addComponent(txtFilenamePat, 2,1, 4,1, 0,0, 0.5f,0, HORIZONTAL, LINE_END, new Insets(0,0,0,0));    
  addComponent(lblDates, 0,2, 2,1, 0,0, 0.5f,0, HORIZONTAL, CENTER, new Insets(0,0,0,0));    
  addComponent(lblSizes, 2,2, 2,1,   0,0, 0.5f,0, HORIZONTAL, CENTER, new Insets(0,0,0,0));    

...

【问题讨论】:

  • 如需尽快获得更好的帮助,请发帖 SSCCE。请注意,该文档正在this question 上进行审核和讨论,欢迎投稿。
  • 检查了几个相关的例子here

标签: java swing layout-manager gridbaglayout


【解决方案1】:

你说:

我这样称呼它:

Insets insets = new Insets(0,0,0,0);
JFrame frame = new JFrame();
label = new JLabel("Blablablah");   
addComponent(frame, label, 0,0, 1,1, 0,0, 0.5f,0, HORIZ, CENTER, insets);

你真的省略了frame.setLayout(new GridBagLayout())吗?
因为这是导致cannot add to layout: constraint must be a string (or null) 错误的原因...

编辑:
可能的重复:

【讨论】:

  • @ccjmne... 我再次发现自己是 DENSE AS ... iridium(谢谢,Google)。但是对我自己公平一点,因为我修改了 Java 教程的代码并且该代码没有缺少 setLayout (“缺失”,因为对教程来说是不必要的),我对 GridBagLayout 的缺乏了解一直存在我从看到绝对明显的遗漏。无论如何,我刚刚回到昨晚的 addComponent 冒险并添加了您的行。完美的世界。 (为什么我在问之前看不到这个............)
  • 啊哈哈哈!别担心。我很高兴我能派上用场!您是否还有其他问题/问题,请随时提问 :) 如果您害怕问一些对我们中的某些人来说显而易见的问题,您可以使用我的个人电子邮件 - 可在我的个人资料中找到。祝你好运;)
【解决方案2】:

我经常使用 GridBagLyout,但像我之前的许多其他人一样,我很快发现它可能非常冗长。网络上有很多关于用户如何编写实用方法和/或类来帮助他们生成 GBL 代码的示例。我会告诉你我的工作。

1) 首先,我创建了 2 个枚举,它们是 anchorfill GridBagConstraints 字段的包装器。我更喜欢枚举与整数的类型检查,它还允许我编写更简洁的代码(稍后您将看到)。是的,我仍然对 Anchor 使用旧的“定向”值。我永远无法完全习惯 PAGE_START 之类的首选值。使用你喜欢的任何东西。

Anchor.java:

包gbl; 导入 java.awt.*; /** * 方便的枚举,为 * GridBagConstraints anchor 属性。 */ 公共枚举锚 { 北(GridBagConstraints.NORTH), 南(GridBagConstraints.SOUTH), 东(GridBagConstraints.EAST), 西(GridBagConstraints.WEST), 东北(GridBagConstraints.NORTHEAST), 西北(GridBagConstraints.NORTHWEST), 东南(GridBagConstraints.SOUTHEAST), 西南(GridBagConstraints.SOUTHWEST), 中心(GridBagConstraints.CENTER); 私有 int 约束; 私人锚(int gbConstraint) { 约束 = gbConstraint; } 公共 int getConstraint() { 返回约束; } }

Fill.java:

包gbl; 导入 java.awt.*; /** * 方便的枚举,为 * GridBagConstraints fill 属性。 */ 公共枚举填写 { 无(GridBagConstraints.NONE), 水平(GridBagConstraints.HORIZONTAL), 垂直(GridBagConstraints.VERTICAL), 两者(GridBagConstraints.BOTH); 私有 int 约束; 私人填充(int gbConstraint) { 约束 = gbConstraint; } 公共 int getConstraint() { 返回约束; } }

2) 然后,我创建了一个 GridBagConstraints 的子类,它允许我仅在需要时“链接”属性,同时使用通用默认值:

GBConstraints.java:

包gbl; 导入 java.awt.*; /** * 方便类来简化 GridBagConstraints 对象的创建。 */ 公共类 GBConstraints 扩展了 GridBagConstraints { 公共GBConstraints(int gridX,int gridY) { 极好的(); this.gridx = gridX; this.gridy = gridY; this.gridwidth = 1; this.gridheight = 1; this.weightx = 0; this.weighty = 0; this.anchor = 西北; // 旧的默认值是 CENTER this.fill = NONE; this.insets = new Insets(1,2,1,2); // 旧的默认值是 (0,0,0,0) 这个.ipadx = 1; // 旧的默认值为 0 这个.ipady = 1; // 旧的默认值为 0 } 公共 GBConstraints 锚点(Anchor 锚点) { this.anchor = anchor.getConstraint(); 返回这个; } public GBConstraints fill(填充填充) { this.fill = fill.getConstraint(); /* * 为方便起见,适当设置权重,因为这些值是 * 几乎总是与给定的填充一起使用。来电者总是可以 * 如果不需要这些默认值,请稍后调用 weight(...) 方法。 */ 开关(填充) { 案例水平: this.weightx = 1; this.weighty = 0; 休息; 垂直案例: this.weightx = 0; this.weighty = 1; 休息; 两种情况: this.weightx = 1; this.weighty = 1; 休息; 默认: this.weightx = 0; this.weighty = 0; 休息; } 返回这个; } 公共 GBConstraints 插图(int 长度) { 返回插图(长度,长度,长度,长度); } 公共GBConstraints insets(int top,int left,int bottom,int right) { this.insets = new Insets(top, left, bottom, right); 返回这个; } 公共GBConstraints ipad(int ipadX,int ipadY) { 这个.ipadx = ipadX; 这个.ipady = ipadY; 返回这个; } 公共GBConstraints跨度(int gridWidth,int gridHeight) { this.gridwidth = gridWidth; this.gridheight = gridHeight; 返回这个; } 公共 GBConstraints spanX(int gridWidth) { this.gridwidth = gridWidth; 返回这个; } 公共 GBConstraints spanY(int gridHeight) { this.gridheight = gridHeight; 返回这个; } 公共 GBConstraints 权重(双倍权重X,双倍权重Y) { this.weightx = weightX; this.weighty = weightY; 返回这个; } public GBConstraints weightX(double weightX) { this.weightx = weightX; 返回这个; } public GBConstraints weightY(double weightY) { this.weighty = weightY; 返回这个; } }

3) 综上所述,这是一个演示如何使用上述类的演示。这大大简化了使用 GridBagLayout,恕我直言。旁注:我通常远离静态导入,但我喜欢这种情况。

演示:

包gbl; 导入静态 gbl.Anchor.*; 导入静态 gbl.Fill.*; 导入 java.awt.*; 导入 javax.swing.*; 公共类 GridBagDemo 实现 Runnable { 公共静态无效主要(字符串 [] 参数) { SwingUtilities.invokeLater(new GridBagDemo()); } 公共无效运行() { JLabel lblFirst = new JLabel("名字"); JLabel lblLast = new JLabel("姓氏"); JLabel lblStreet = new JLabel("Street"); JLabel lblCity = new JLabel("城市"); JLabel lblState = new JLabel("State"); JLabel lblZip = new JLabel("ZIP"); JLabel lblNotes = new JLabel("Notes"); JTextField txfFirst = new JTextField(15); JTextField txfLast = new JTextField(20); JTextField txfStreet = new JTextField(40); JTextField txfCity = new JTextField(15); JTextField txfState = new JTextField(5); JTextField txfZip = new JTextField(10); JTextArea txaNotes = new JTextArea(5, 50); JScrollPane scrNotes = new JScrollPane(txaNotes); scrNotes.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_​​ALWAYS); 组件 spacer1 = Box.createHorizo​​ntalStrut(5); 组件 spacer2 = Box.createHorizo​​ntalStrut(5); JPanel 面板 = new JPanel(new GridBagLayout()); panel.add(spacer1, new GBConstraints(0,0)); panel.add(lblFirst, 新的 GBConstraints(0,1)); panel.add(txfFirst, 新的 GBConstraints(1,1)); panel.add(lblLast, 新 GBConstraints(2,1)); panel.add(txfLast, new GBConstraints(3,1).spanX(3).fill(HORIZONTAL)); panel.add(lblStreet, 新的 GBConstraints(0,2)); panel.add(txfStreet, new GBConstraints(1,2).spanX(5).fill(HORIZONTAL)); panel.add(lblCity, 新的 GBConstraints(0,3)); panel.add(txfCity, 新的 GBConstraints(1,3)); panel.add(lblState, new GBConstraints(2,3).anchor(EAST)); panel.add(txfState, 新的 GBConstraints(3,3)); panel.add(lblZip, new GBConstraints(4,3)); panel.add(txfZip, new GBConstraints(5,3).fill(HORIZONTAL)); panel.add(lblNotes, new GBConstraints(0,4)); panel.add(scrNotes, new GBConstraints(1,4).spanX(5).fill(BOTH)); panel.add(spacer2, new GBConstraints(0,5)); JFrame frame = new JFrame("网格包演示"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new JScrollPane(panel), BorderLayout.CENTER); 框架.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }

同样,这只是您在网上可以找到的众多方式之一。希望这会有所帮助。

【讨论】:

  • 嗯...将我上一条评论的结尾更改为“NEAR the end of a thread”。谢谢,@splungebob。我一定会尝试消化你所有的辛勤工作!
  • @DSlomer64 没问题。如果您对此代码有任何疑问或问题,请告诉我。
  • 现在我已经阅读了@splungebob 的帖子,我很高兴地说(a)我尝试做他所做的事情并且(b)他的方式好多了,所以我赢了! (这是 Patrick Star 的参考。他在很多不恰当的地方说“我赢了!”。)(但我的“我赢了”是合适的,因为我要直接实施 splungebob 的东西。
  • 我只想说,当我开始使用 GBL 编写 GUI 时,我也开始使用与您发布的方法类似的方法,因为这就是我的书所建议的。它工作得很好,但代码对我来说仍然很笨重。我发布的代码只是几年来多次调整的结果。随心所欲地使用,或根据自己的喜好对其进行更多调整。
  • 如果我能实现这些东西,我欠@splungebob 很多时间!非常慷慨。从某种意义上说,这就是 SO 应该做的事情——分享。我自己也想大方一点,但我不知道蹲!不是真的...我知道 SQUAT,但仅此而已。这几天真是太棒了!!
【解决方案3】:

正如我在上面评论的那样,@ccjmme 的帮助使我添加了两行并修改了签名以获得没有全局变量的工作版本。我发布的版本比那个工作版本好得多。我随后的评论解释了原因。

package test;
import java.awt.*;
import javax.swing.*;

public class Test{

  public static void addComponent(Component c, Container f,      GridBagConstraints gbc, 
                                    int     x,        int   y, 
                                    int     w,        int   h, 
                                    int     ipadx,    int   ipady, 
                                    float   wtx,      float wty,
                                    int fill, int anchor, Insets insets){
    if(fill   <= 0)     fill    = GridBagConstraints.NONE;
    if(anchor <= 0)     anchor  = GridBagConstraints.CENTER;
    if(insets == null)  insets  = new Insets(0,0,0,0);
    gbc.gridx = x;        gbc.gridy = y;      
    gbc.gridwidth = w;    gbc.gridheight = h;     
    gbc.fill = fill; 
    gbc.ipadx = ipadx;    gbc.ipady = ipady;  
    gbc.insets = insets;  gbc.anchor = anchor; 
    gbc.weightx = wtx;    gbc.weighty = wty;

    f.add(c,gbc);
  }

  public static void addComponent(String s, Container f, GridBagConstraints gbc, 
                                  int     x,        int   y, 
                                  int     w,        int   h, 
                                  int     ipadx,    int   ipady, 
                                  float   wtx,      float wty,
                                  int fill, int anchor, Insets insets){
    addComponent(new JLabel(s),f,gbc,x,y,w,h,ipadx,ipady,wtx,wty,fill,anchor,insets);
  }  

  public static void addComponent(Component c, Container f, GridBagConstraints gbc, 
                                  int     x,        int   y){
    addComponent(c, f, gbc, x, y, 1,1, 0,0, 0.5f,0.5f, 
                  GridBagConstraints.NONE, GridBagConstraints.CENTER,
                  new Insets(0,0,0,0));
  }


  public static void main(String[] args) {
    Insets insets = new Insets(0,0,0,0);
    JFrame frame = new JFrame();
    frame.setLayout(new GridBagLayout());
    JLabel label = new JLabel("Blablablah");
    JTextField text = new JTextField("text");
    JTextField next = new JTextField("a bit longer");
    GridBagConstraints gbc = new GridBagConstraints();

    addComponent("On the fly", frame, gbc, 0,0, 1,1, 0,0, 0.5f,0, 0, 0, insets);
    addComponent(label,        frame, gbc, 0,1);

    addComponent(next, frame, gbc, 1,1, 10,1, 0,0, 5.0f,0.5f, GridBagConstraints.EAST, 0, null);        
    addComponent(text, frame, gbc, 1,0);
    frame.pack();
    frame.setVisible(true);
  }
}

改进:

(1) addComponent 的两个附加签名,以便 (i) 可以在不声明和初始化变量的情况下即时创建标签(如果代码需要引用它则不好,但我的很少这样做)和( ii) 只需指定 (x,y) 并接受默认值即可添加任何组件。

(2) “默认 0”选项以避免在您通常需要这些填充和锚点值时键入“GridBagConstraints.CENTERand...NONE”。

【讨论】:

  • 我看待它的方式(以及对我自己问题的“回答”的原因)是(a)Netbeans GUIbuilder 不是制作 GUI 的方法和(b)GridBagLayout,一次mastered(这里还在等待精通),是很费时间的,但是如果有足够的方法支持,它可能不会那么糟糕。所以我希望得到建设性的批评,并为寻求帮助的人们提供一种工作模式,以考虑和改进(也许可以从中学习)。还有什么地方比在 SO 线程结束时更能找到帮助呢?
【解决方案4】:

这是我使用@Splungebob 的枚举和方法(以及他作为模型的“演示”),在我的现有程序中毫无痛苦地快速设计和实现一个非常漂亮的 UI,该程序的 UI 很糟糕。

public class UI extends JFrame {
...
JPanel panel = new JPanel(new GridBagLayout());
panel.setBackground(Color.LIGHT_GRAY);
panel.add(lblCenterX,     new GBConstraints(0,0));
panel.add(lblCenterY,     new GBConstraints(0,1));
panel.add(lblRadius,      new GBConstraints(0,2).anchor(EAST));
panel.add(lblIterations,  new GBConstraints(0,3).anchor(EAST));
panel.add(spnCenterX,     new GBConstraints(1,0).fill(HORIZONTAL));
panel.add(spnCenterY,     new GBConstraints(1,1).ipad(90,10));
panel.add(spnRadius,      new GBConstraints(1,2));
panel.add(spnIterations,  new GBConstraints(1,3));
panel.add(btnPaint,       new GBConstraints(0,4).spanX(2).spanY(2).ipad(30,20).anchor(CENTER));
panel.add(btnDefaults,    new GBConstraints(0,6));
panel.add(btnExit,        new GBConstraints(1,6).anchor(EAST));
this.add(panel, BorderLayout.CENTER);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);

我发布这篇文章只是希望有人像我一样需要关于 GBL 的帮助,然后再回头查看 Splunge 的资料。 btnPaint 的线路多么酷,我在那里制作了一个巨大的按钮!

【讨论】:

    猜你喜欢
    • 2019-02-18
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-29
    • 1970-01-01
    • 1970-01-01
    • 2015-08-21
    相关资源
    最近更新 更多