Java GridBagLayout automated construction

2019-02-18 11:03发布

问题:

I designed a GUI using GridBagLayout and GridBagConstraints. It contains a variable number of rows, each with one of a few possible column layouts. To test the code, I comprised the GUI of panels with different colors that illustrate the location and resizing behavior of all the cells in each row and column. This test GUI works fine, but now I need to automate its construction. Specifically, I need the rows to be one of three different types. If you run the code below and look at the resulting GUI, you can see that row1 is one type, rows 2,6,and7 are another type, and rows 3,4,and5 are yet a third type. I need each of these three types of rows to be encapsulated in its own class. What's more, I need my code to be able to create a variable number of rows of the third type (illustrated by rows 3,4,and5 in the example). (This is for data analysis software. The panels will load data views and tools for manipulating the data views.)

When I try to encapsulate the rows into their own classes, the GUI stops looking like it should look. The test code below generates a GUI layout the way that it should look. Can anyone show me how to alter this code so that it has the functionality described in the first paragraph above?

You can just paste my test code below into your IDE to get it working right away. The test code is in two separate files, as follows:

The code for GridBagLayoutDemo.java is:

import java.awt.*;
import javax.swing.JFrame;

public class GridBagLayoutDemo {
    final static boolean RIGHT_TO_LEFT = false;

    public static void addComponentsToPane(Container pane) {
        if (RIGHT_TO_LEFT) {pane.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);}
        pane.setLayout(new GridBagLayout());

// top row (fill, gridx, gridy, gridwidth 1, weightx 0, weighty 0, ipady 0)
        TestPanel panelr1c1 = new TestPanel(Color.black);
        GridBagConstraints constraint_r1c1 = getGridBagConstraints(GridBagConstraints.NONE,0,0,1,0,0,0);
        pane.add(panelr1c1,constraint_r1c1);

        TestPanel panelr1c2 = new TestPanel(Color.blue);
        GridBagConstraints constraint_r1c2 = getGridBagConstraints(GridBagConstraints.HORIZONTAL,1,0,1,0.8,0,0);
        pane.add(panelr1c2,constraint_r1c2);

        TestPanel panelr1c2a = new TestPanel(Color.green);
        GridBagConstraints constraint_r1c2a = getGridBagConstraints(GridBagConstraints.HORIZONTAL,2,0,1,0.8,0,0);
        pane.add(panelr1c2a,constraint_r1c2a);

        TestPanel panelr1c3 = new TestPanel(Color.red);
        GridBagConstraints constraint_r1c3 = getGridBagConstraints(GridBagConstraints.NONE,3,0,1,0,0,0);
        pane.add(panelr1c3,constraint_r1c3);

        TestPanel panelr1c4 = new TestPanel(Color.blue);
        GridBagConstraints constraint_r1c4 = getGridBagConstraints(GridBagConstraints.NONE,4,0,1,0,0,0);
        pane.add(panelr1c4,constraint_r1c4);

// second row (fill, gridx, gridy, gridwidth 1, weightx 0, weighty 0, ipady 0)
        TestPanel panelr2c1 = new TestPanel(Color.magenta);
        GridBagConstraints constraint_r2c1 = getGridBagConstraints(GridBagConstraints.NONE,0,1,1,0,0,0);
        pane.add(panelr2c1,constraint_r2c1);

        TestPanel panelr2c2 = new TestPanel(Color.pink);
        GridBagConstraints constraint_r2c2 = getGridBagConstraints(GridBagConstraints.HORIZONTAL,1,1,2,1.0,0,0);
        pane.add(panelr2c2,constraint_r2c2);

        TestPanel panelr2c3 = new TestPanel(Color.black);
        GridBagConstraints constraint_r2c3 = getGridBagConstraints(GridBagConstraints.NONE,3,1,1,0,0,0);
        pane.add(panelr2c3,constraint_r2c3);

        TestPanel panelr2c4 = new TestPanel(Color.pink);
        GridBagConstraints constraint_r2c4 = getGridBagConstraints(GridBagConstraints.NONE,4,1,1,0,0,0);
        pane.add(panelr2c4,constraint_r2c4);

// third row (fill, gridx, gridy, gridwidth 1, weightx 0, weighty 0, ipady 0)
        TestPanel panelr3c1 = new TestPanel(Color.gray);
        GridBagConstraints constraint_r3c1 = getGridBagConstraints(GridBagConstraints.VERTICAL,0,2,1,0,0.5,40);
        pane.add(panelr3c1,constraint_r3c1);

        TestPanel panelr3c2 = new TestPanel(Color.orange);
        GridBagConstraints constraint_r3c2 = getGridBagConstraints(GridBagConstraints.BOTH,1,2,2,1.0,0.5,40);
        pane.add(panelr3c2,constraint_r3c2);

        TestPanel panelr3c3 = new TestPanel(Color.red);
        GridBagConstraints constraint_r3c3 = getGridBagConstraints(GridBagConstraints.VERTICAL,3,2,1,0,0.5,40);
        pane.add(panelr3c3,constraint_r3c3);

        TestPanel panelr3c4 = new TestPanel(Color.orange);
        GridBagConstraints constraint_r3c4 = getGridBagConstraints(GridBagConstraints.VERTICAL,4,2,1,0,0.5,40);
        pane.add(panelr3c4,constraint_r3c4);

// fourth row (fill, gridx, gridy, gridwidth 1, weightx 0, weighty 0, ipady 0)
        TestPanel panelr4c1 = new TestPanel(Color.black);
        GridBagConstraints constraint_r4c1 = getGridBagConstraints(GridBagConstraints.VERTICAL,0,3,1,0,0.5,40);
        pane.add(panelr4c1,constraint_r4c1);

        TestPanel panelr4c2 = new TestPanel(Color.white);
        GridBagConstraints constraint_r4c2 = getGridBagConstraints(GridBagConstraints.BOTH,1,3,2,1.0,0.5,40);
        pane.add(panelr4c2,constraint_r4c2);

        TestPanel panelr4c3 = new TestPanel(Color.green);
        GridBagConstraints constraint_r4c3 = getGridBagConstraints(GridBagConstraints.VERTICAL,3,3,1,0,0.5,40);
        pane.add(panelr4c3,constraint_r4c3);

        TestPanel panelr4c4 = new TestPanel(Color.blue);
        GridBagConstraints constraint_r4c4 = getGridBagConstraints(GridBagConstraints.VERTICAL,4,3,1,0,0.5,40);
        pane.add(panelr4c4,constraint_r4c4);

// fifth row (fill, gridx, gridy, gridwidth 1, weightx 0, weighty 0, ipady 0)
        TestPanel panelr5c1 = new TestPanel(Color.darkGray);
        GridBagConstraints constraint_r5c1 = getGridBagConstraints(GridBagConstraints.VERTICAL,0,4,1,0,0.5,40);
        pane.add(panelr5c1,constraint_r5c1);

        TestPanel panelr5c2 = new TestPanel(Color.yellow);
        GridBagConstraints constraint_r5c2 = getGridBagConstraints(GridBagConstraints.BOTH,1,4,2,1.0,0.5,40);
        pane.add(panelr5c2,constraint_r5c2);

        TestPanel panelr5c3 = new TestPanel(Color.white);
        GridBagConstraints constraint_r5c3 = getGridBagConstraints(GridBagConstraints.VERTICAL,3,4,1,0,0.5,40);
        pane.add(panelr5c3,constraint_r5c3);

        TestPanel panelr5c4 = new TestPanel(Color.orange);
        GridBagConstraints constraint_r5c4 = getGridBagConstraints(GridBagConstraints.VERTICAL,4,4,1,0,0.5,40);
        pane.add(panelr5c4,constraint_r5c4);

// sixth row (fill, gridx, gridy, gridwidth 1, weightx 0, weighty 0, ipady 0)
        TestPanel panelr6c1 = new TestPanel(Color.green);
        GridBagConstraints constraint_r6c1 = getGridBagConstraints(GridBagConstraints.NONE,0,5,1,0,0,0);
        pane.add(panelr6c1,constraint_r6c1);

        TestPanel panelr6c2 = new TestPanel(Color.blue);
        GridBagConstraints constraint_r6c2 = getGridBagConstraints(GridBagConstraints.HORIZONTAL,1,5,2,1.0,0,0);
        pane.add(panelr6c2,constraint_r6c2);

        TestPanel panelr6c3 = new TestPanel(Color.red);
        GridBagConstraints constraint_r6c3 = getGridBagConstraints(GridBagConstraints.NONE,3,5,1,0,0,0);
        pane.add(panelr6c3,constraint_r6c3);

        TestPanel panelr6c4 = new TestPanel(Color.black);
        GridBagConstraints constraint_r6c4 = getGridBagConstraints(GridBagConstraints.NONE,4,5,1,0,0,0);
        pane.add(panelr6c4,constraint_r6c4);

// seventh row (fill, gridx, gridy, gridwidth 1, weightx 0, weighty 0, ipady 0)
        TestPanel panelr7c1 = new TestPanel(Color.darkGray);
        GridBagConstraints constraint_r7c1 = getGridBagConstraints(GridBagConstraints.NONE,0,6,1,0,0,0);
        pane.add(panelr7c1,constraint_r7c1);

        TestPanel panelr7c2 = new TestPanel(Color.white);
        GridBagConstraints constraint_r7c2 = getGridBagConstraints(GridBagConstraints.HORIZONTAL,1,6,2,1.0,0,0);
        pane.add(panelr7c2,constraint_r7c2);

        TestPanel panelr7c3 = new TestPanel(Color.yellow);
        GridBagConstraints constraint_r7c3 = getGridBagConstraints(GridBagConstraints.NONE,3,6,1,0,0,0);
        pane.add(panelr7c3,constraint_r7c3);

        TestPanel panelr7c4 = new TestPanel(Color.green);
        GridBagConstraints constraint_r7c4 = getGridBagConstraints(GridBagConstraints.NONE,4,6,1,0,0,0);
        pane.add(panelr7c4,constraint_r7c4);
    }

    // Create the GUI and show it.  For thread safety, this method should be invoked from the event-dispatching thread.
    private static void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("GridBagConstraint Practice");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //Set up the content pane.
        addComponentsToPane(frame.getContentPane());
        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread: creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {public void run() {createAndShowGUI();}});
    }
    private static GridBagConstraints getGridBagConstraints(int fill, int gridx, int gridy, int gridwidth, double weightx, double weighty, int ipady){
        GridBagConstraints myGridBagConstraints = new GridBagConstraints();
          myGridBagConstraints.fill=fill;
        myGridBagConstraints.gridx=gridx;
        myGridBagConstraints.gridy=gridy;
        myGridBagConstraints.gridwidth=gridwidth;
        myGridBagConstraints.weightx=weightx;
        myGridBagConstraints.weighty=weighty;
        myGridBagConstraints.ipady=ipady;
        return myGridBagConstraints;
    }
}

The code for TestPanel.java is:

import java.awt.Color;
import javax.swing.JPanel;

public class TestPanel extends JPanel {
    public TestPanel (Color myColor){this.setBackground(myColor);}
}

回答1:

Rule number 1 for Swing layouts: Whatever you do, do not use GridBagLayout. GridBagLayout was fine in 1998. Its design is problematic, it is buggy, and it has not evolved. Code is extremely verbose, hard to write, hard to understand and hard to maintain.

I recommend MigLayout, it's the most versatile and intuitive layout manager I have seen. Look at the quick start guide on the MigLayout site, it's a lot less difficult than GridBagLayout and much more powerful. Here is your example in in MigLayout and I've shown you how to refactor your row types:

import net.miginfocom.swing.MigLayout;
import java.awt.*;
import javax.swing.*;

public class GridBagLayoutDemo {
    final static boolean RIGHT_TO_LEFT = false;

    public static void addComponentsToPane(Container pane) {
        if (RIGHT_TO_LEFT) {
            pane.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        }
        pane.setLayout(new MigLayout("insets 0, gap 0, wrap", "[][fill, grow][fill, grow][][]", "[fill]"));

        addType1(pane, Color.BLACK, Color.BLUE, Color.GREEN, Color.RED, Color.BLUE);
        addType2(pane, Color.MAGENTA, Color.PINK, Color.BLACK, Color.PINK);
        addType3(pane, Color.GRAY, Color.ORANGE, Color.RED, Color.ORANGE);
        addType3(pane, Color.BLACK, Color.WHITE, Color.GREEN, Color.BLUE);
        addType3(pane, Color.DARK_GRAY, Color.YELLOW, Color.WHITE, Color.ORANGE);
        addType2(pane, Color.GREEN, Color.BLUE, Color.RED, Color.BLACK);
        addType2(pane, Color.DARK_GRAY, Color.WHITE, Color.YELLOW, Color.GREEN);
    }

    private static void addType1(Container pane, Color c1, Color c2, Color c3, Color c4, Color c5) {
        pane.add(new TestPanel(c1));
        pane.add(new TestPanel(c2));
        pane.add(new TestPanel(c3));
        pane.add(new TestPanel(c4));
        pane.add(new TestPanel(c5));
    }

    private static void addType2(Container pane, Color c1, Color c2, Color c3, Color c4) {
        pane.add(new TestPanel(c1));
        pane.add(new TestPanel(c2), "spanx 2");
        pane.add(new TestPanel(c3));
        pane.add(new TestPanel(c4));
    }

    private static void addType3(Container pane, Color c1, Color c2, Color c3, Color c4) {
        pane.add(new TestPanel(c1));
        pane.add(new TestPanel(c2), "spanx 2, pushy, hmin pref+40");
        pane.add(new TestPanel(c3));
        pane.add(new TestPanel(c4));
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("MigLayout Practice");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        addComponentsToPane(frame.getContentPane());
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

class TestPanel extends JPanel {
    public TestPanel(Color myColor) {
        this.setBackground(myColor);
    }
}

This yields the exact same layout as your example. Probably you want hmin 40 instead of hmin pref+40, the latter produces the same result as setting ipady=40 in GridBagConstraints.

And please use the upper case constants in the Color class, the lower-case constants should really be deprecated.

For anyone who wonders what this layout looks like, here it is: