To fine-tune a layout, you will be forced to work with GridBagLayout, a highly configurable but more complex layout manager. GridBagLayout is similar to GridLayout in that it lets you organize components in a grid of rectangles. However, GridBagLayout gives you far more control. First, each rectangle is not fixed in sizeit is generally sized according to the default, or "preferred" size of the component it contains. Components can span multiple rows or columns. The modified createFieldsPanel code shows how to lay out labels and fields using a GridBagLayout. (The code in the method presumes that you have statically imported java.awt.GridBagConstraints.*.) JPanel createFieldsPanel() { GridBagLayout layout = new GridBagLayout(); JPanel panel = new JPanel(layout); int columns = 20; JLabel departmentLabel = createLabel(DEPARTMENT_LABEL_NAME, DEPARTMENT_LABEL_TEXT); JTextField departmentField = createField(DEPARTMENT_FIELD_NAME, columns); JLabel numberLabel = createLabel(NUMBER_LABEL_NAME, NUMBER_LABEL_TEXT); JTextField numberField = createField(NUMBER_FIELD_NAME, columns); layout.setConstraints(departmentLabel, new GridBagConstraints( 0, 0, // x, y 1, 1, // gridwidth, gridheight 40, 1, // weightx, weighty LINE_END, //anchor NONE, // fill new Insets(3, 3, 3, 3), // top-left-bottom-right 0, 0)); // padx, ipady layout.setConstraints(departmentField, new GridBagConstraints(1, 0, 2, 1, 60, 1, CENTER, HORIZONTAL, new Insets(3, 3, 3, 3), 0, 0)); layout.setConstraints(numberLabel, new GridBagConstraints(0, 1, 1, 1, 40, 1, LINE_END, NONE, new Insets(3, 3, 3, 3), 0, 0)); layout.setConstraints(numberField, new GridBagConstraints(1, 1, 2, 1, 60, 1, CENTER, HORIZONTAL, new Insets(3, 3, 3, 3), 0, 0)); panel.add(departmentLabel); panel.add(departmentField); panel.add(numberLabel); panel.add(numberField); return panel; } After creating a GridBagLayout and setting it into the panel, you call the method setConstraints on the layout for each widget to add. The setConstraints method takes two parameters: a Component object and a GridBagConstraints object. A GridBagConstraints object contains several constraints for the Component object. This table very briefly summarizes the constraints (see the API documentation for complete details):
Each of the fields in GridBagConstraints is public. You can construct a GridBagConstraints object with no parameters, then set individual fields as necessary. Or, as in the listing for createFieldsPanel, you can specify every possible constraint using the alternate GridBagConstraints constructor. The best tactic is to sketch, on paper or whiteboard, a grid representing how you want the output to look. Use the gridx/gridy and gridwidth/gridheight constraints to determine the relative sizes and positions of the components. Then concentrate on the anchor and fill aspects of each component. Code the layout to these sketched specifications and modify if necessary. You can then experiment with the insets and weightx/weighty constraints (and occasionally the ipadx/ipadx constraints) to tweak the spacing between components. Obviously there is a lot of redundancy in createFieldsPanel. The following code is modestly refactored. JPanel createFieldsPanel() { GridBagLayout layout = new GridBagLayout(); JPanel panel = new JPanel(layout); int columns = 20; addField(panel, layout, 0, DEPARTMENT_LABEL_NAME, DEPARTMENT_LABEL_TEXT, DEPARTMENT_FIELD_NAME, columns); addField(panel, layout, 1, NUMBER_LABEL_NAME, NUMBER_LABEL_TEXT, NUMBER_FIELD_NAME, columns); return panel; } private void addField( JPanel panel, GridBagLayout layout, int row, String labelName, String labelText, String fieldName, int fieldColumns) { JLabel label = createLabel(labelName, labelText); JTextField field = createField(fieldName, fieldColumns); Insets insets = new Insets(3, 3, 3, 3); // top-left-bottom-right layout.setConstraints(label, new GridBagConstraints( 0, row, // x, y 1, 1, // gridwidth, gridheight 40, 1, // weightx, weighty LINE_END, //anchor NONE, // fill insets, 0, 0)); // padx, ipady layout.setConstraints(field, new GridBagConstraints(1, row, 2, 1, 60, 1, CENTER, HORIZONTAL, insets, 0, 0)); panel.add(label); panel.add(field); } The onerous nature of such code should drive you in the direction of extreme refactoring. Consider replacing repetition in the construction of GridBagConstraints objects by using a simplified utility constructor. If you need to represent more than a couple of fields and associated labels, consider representing each pair using a data class. You can then represent the entire set of fields in a table, iterating through it to create the layout. Figure 9 shows a layout that is getting close to being acceptable. Resize it to see how the field components fill to their display area. Figure 9. Using GridBagLayoutAs of Java 1.4, Sun introduced the SpringLayout class. This layout manager is primarily designed for the use of GUI composition tools. The basic concept of SpringLayout is that you define a layout by tying the edges of components together using constraints known as springs. In CoursesPanel, you might create a spring to attach the west (left) edge of the department text field to the east (right) edge of the department label. The spring object attaching the two components is a fixed five pixels in size. Another spring might attach the east edge of the department text field to the east side of the panel itself, also separated by a spring of fixed length. As the panel grows in width, the department text field would grow accordingly. Creating a SpringLayout by hand is fairly easy for a panel with only a few components. It can also be incredibly frustrating and difficult for a more complex layout. In most cases, you will want to leave the job to a layout tool. |