0

enter image description here I am trying to get a basic GUI program. It doesnt have to do much but the buttons at the bottom have to work. I am having trouble placing the components (The text, combobox, etc) in the proper spot. Using the GridBag I am able to change the location with the c.gridx and c.gridy but in a very weird way. If I put the gridy values 0-7 with x being 0 everything is on top of eachother. I try putting the combo box next to the text by changing the gridx value and everything gets messed up. The alignment is off on the components after the one I was trying to move over. How do I fix this? I tried the BorderLayout.DIRECTION with no luck. The actual change doesn't take effect at all (moving then to the bottom). How do I fix this? Thanks

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package javaredesign;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;

/**
 *
 * @author 
 */
public class Window extends JFrame {

    //Default global variables
    private JButton submit;
    private JButton cancel;
    private JButton reset;
    private JPanel panel = new JPanel(new GridBagLayout());
    private String searchOutput = "";


    //Default constructor
    public Window() {

        //Title of the window
        super("LIBSYS: Search");

        //Creating the flow layout to which we can work on and adding panel to frame
        setLayout(new FlowLayout());
        add(panel, BorderLayout.SOUTH);

        //Creating the grid to location of the parts
        GridBagConstraints c = new GridBagConstraints();

        //Initializing the buttons
        submit = new JButton("Submit");
        c.insets = new Insets(10, 10, 10, 5);
        c.gridx = 1;
        c.gridy = 20;
        panel.add(submit, c);
        reset = new JButton("Reset");
        c.gridx = 2;
        c.gridy = 20;
        panel.add(reset, c);
        cancel = new JButton("Cancel");
        c.gridx = 3;
        c.gridy = 20;
        panel.add(cancel, c);

        //Handler constructor to do the actionlistening
        Handler handler = new Handler();
        submit.addActionListener(handler);
        reset.addActionListener(handler);
        cancel.addActionListener(handler);

        //Creating the two dropdowns with the words next to them
        JLabel chooseCollection = new JLabel("Choose Collection");
        String[] ccString = {"All", "Mostly", "Some", "Few"};
        JComboBox ccComboBox = new JComboBox(ccString);
        JLabel searchUsing = new JLabel("Search Using");
        String[] suString = {"Title", "Artist", "Arthor", "Type"};
        JComboBox suComboBox = new JComboBox(suString);

        //Adding all the text and dropdown menus to the panel
        c.gridx = 0;
        c.gridy = 24;
        panel.add(chooseCollection, c);
        c.gridx = 0;
        c.gridy = 25;
        panel.add(ccComboBox, c);
        c.gridx = 1;
        c.gridy = 25;
        panel.add(searchUsing, c);
        c.gridx = 0;
        c.gridy = 27;
        panel.add(suComboBox, c);
        c.gridx = 1;
        c.gridy = 27;

        //Setting up the text inputbox
        JLabel keyword = new JLabel("Keyword or phrase");
        JTextField textField = new JTextField(20);

        //Adding lable and text box to the panel
        panel.add(keyword);
        panel.add(textField);
    }

}
user1881401
  • 85
  • 3
  • 15
  • Provide ASCII art, or a simple drawing, of the required layout of the GUI. – Andrew Thompson Nov 07 '14 at 07:39
  • 1
    And provide an [MCVE](http://stackoverflow.com/help/mcve) without things like `public class Handler implements ActionListener {..` I understand that buttons with no functionality are useless in a real app., but they are unnecessary to demonstrate a layout problem. – Andrew Thompson Nov 07 '14 at 07:41
  • 2
    Don't try and do this in a single component, use multiple `JPanel`s to maintain individual elements of the UI and focus on their layout requirements, build each layer, using different layout managers if you need to until you have the result your need – MadProgrammer Nov 07 '14 at 07:44
  • There is _no way_ you should have things like `c.gridy = 25` etc. I don't understand what you are trying to do with something like that. Do you have 27 rows? It seems highly unlikely. Is your goal to get what your picture looks like? Also, at the end you simply add `keyword` and `textField` without a `GridBagConstraints` object. – Jared Nov 07 '14 at 07:52
  • I just used 25 as a test value to see if anything is changing. I just kept upping the values to see if it will go off the screen but yah. – user1881401 Nov 07 '14 at 07:57
  • If I make a JPanel for every new component I am going to add how do I set the size of the JPanel container? I would have to stack all the containers in one window. – user1881401 Nov 07 '14 at 08:04
  • @MadProgrammer My above comment – user1881401 Nov 07 '14 at 08:12
  • @user1881401 FYI, in the future, you should _not_ extend `JFrame`. Create another class that extends `JPanel` (or `JComponent`--I have yet to hear a good reason why you should extend one over the other--but that is a whole other conversation). Set up your panel in this class, then simply use `JFrame.setContentPane(...)` to set a `JFrame` to your panel. Even if you go with multiple panels, this paradigm still holds true. You can create multiple panels and then set the layout with some "master" panel and _that_ is what you should set the content pane of your `JFrame` to. – Jared Nov 07 '14 at 08:52
  • @user1881401 No, you miss the point, focus on the individual relationships, buttons in one panel, some fields in another, maybe others in another. Focus on the individual relationships in isolation and the begin to pull them together into groups. – MadProgrammer Nov 07 '14 at 08:58

4 Answers4

4

I would not try and force the buttons into the same layout as the other components. They are independant and should be free floating evenly spaced.

The button panel should be in a panel set with flowlayout. Then this panel should be added to the frame with borderlayout at SOUTH.

The rest of the components should go into a panel with gridbaglayout and added to the frame at CENTER.

The gridbaglayout panel then should have all its components set at the coordinates listed in the picture. If the component covers more than two cells (ie combo) then set the gridWidth property to 2.

enter image description here

Oliver Watkins
  • 12,575
  • 33
  • 119
  • 225
2

The problem is that you need to understand GridBagLayout. You need to layout your components onto a proper grid:

GridBagLayout

So you should have 5 rows and 12 columns to layout the way you described in your picture. Don't mind the padding (I tried to add that to make it more illustrative). Your first three rows should each have elements of gridwidth = 6 and the first should be at gridx = 0 and the second at gridx = 6. Then your "yes" and "no" buttons should each have gridwidth = 3 at gridx = 6 and gridx = 9, respectively. Finally your last row, the buttons, should each have gridwidth = 2 and gridx = 1, gridx = 5, and gridx = 9, respectively. Instead of using padding, I would just use gbc.anchor = GridBagConstraints.CENTER (for all of the components) so that the components are centered inside of their grid.

I would suggest using gbc.fill = GridBagConstraints.HORIZONTAL for the text field and combo boxes so that they line up "nice and pretty" but don't forget to set that back to gbc.fill = GridBagConstraints.NONE for the radio buttons and regular buttons (unless you want them to stretch as well).

I very haphazardly changed your code (which appeared to be in no logical order):

// Creating the grid to location of the parts
GridBagConstraints c = new GridBagConstraints();

c.anchor = GridBagConstraints.CENTER;

// Initializing the buttons
submit = new JButton("Submit");
c.insets = new Insets(10, 10, 10, 5);
c.gridx = 1;
c.gridy = 5;
c.gridwidth = 2;
panel.add(submit, c);
reset = new JButton("Reset");
c.gridx = 5;
// c.gridy = 20;
panel.add(reset, c);
cancel = new JButton("Cancel");
c.gridx = 9;
// c.gridy = 20;
panel.add(cancel, c);

// Handler constructor to do the actionlistening
Handler handler = new Handler();
submit.addActionListener(handler);
reset.addActionListener(handler);
cancel.addActionListener(handler);

// Creating the two dropdowns with the words next to them
JLabel chooseCollection = new JLabel("Choose Collection");
String[] ccString = { "All", "Mostly", "Some", "Few" };
JComboBox ccComboBox = new JComboBox(ccString);
JLabel searchUsing = new JLabel("Search Using");
String[] suString = { "Title", "Artist", "Arthor", "Type" };
JComboBox suComboBox = new JComboBox(suString);
// Adding all the text and dropdown menus to the panel
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 6;
c.anchor = GridBagConstraints.WEST;
panel.add(chooseCollection, c);
c.gridx = 6;
// c.gridy = 25;

c.fill = GridBagConstraints.HORIZONTAL;
panel.add(ccComboBox, c);
c.gridx = 0;
c.gridy = 2;
c.fill = GridBagConstraints.NONE;
panel.add(searchUsing, c);
c.gridx = 6;
// c.gridy = 27;
c.fill = GridBagConstraints.HORIZONTAL;
panel.add(suComboBox, c);

// Setting up the text inputbox
JLabel keyword = new JLabel("Keyword or phrase");
JTextField textField = new JTextField(20);
// Adding lable and text box to the panel
c.gridx = 0;
c.gridy = 1;
c.fill = GridBagConstraints.NONE;
panel.add(keyword, c);
c.gridx = 6;

panel.add(textField, c);

Which produced the following layout:

enter image description here

Jared
  • 940
  • 5
  • 9
  • 2
    While certainly doable and great demonstration of `GridBagLayout`s power, I'd prefer to break the UI down in it's relational containers (fields and buttons separately for example), this way you could add more components into the containers without adversely affecting the layouts of other components...just saying ;) – MadProgrammer Nov 07 '14 at 09:00
  • @MadProgrammer Yeah, I am no GUI wizard and, in fact, would like to avoid GUI layouts like the plague. For me, I figured out that `GridBagLayout` was general enough that I could do most anything with it without having to understand other layouts and so that is what I have proceeded with. Truth be told, from what I understand, swing is dead and even JavaFX isn't really widely used. It seems to me that CSS has won the day and that layouts should be done via web technologies and Java should be left where it belongs--in the backend. – Jared Nov 07 '14 at 09:56
2

I'd do it by having an outer BorderLayout. In the CENTER would be a GroupLayout for the label/control pairs. In the PAGE_END would be a FlowLayout for the buttons. LibSysSearch control

This uses a TitledBorder instead of the JLabel to show LIBSYS Search. If it really needs a label, put it in the PAGE_START of the border layout.

import java.awt.*;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
import javax.swing.border.*;

public class LibSysSearch {

    private JComponent ui = null;

    LibSysSearch() {
        initUI();
    }

    public void initUI() {
        if (ui != null) {
            return;
        }
        ui = new JPanel(new BorderLayout(4, 4));
        ui.setBorder(new EmptyBorder(4, 4, 4, 4));

        // Here is our control.  This puts a titled border around it, 
        // instead of using a label in the PAGE_START
        JPanel libSysSearchControl = new JPanel(new BorderLayout());
        ui.add(libSysSearchControl);

        JPanel actionPanel = new JPanel(
                new FlowLayout(FlowLayout.CENTER, 15, 15));
        libSysSearchControl.add(actionPanel, BorderLayout.PAGE_END);

        String[] actionNames = {"Search", "Reset", "Cancel"};
        for (String name : actionNames) {
            actionPanel.add(new JButton(name));
        }

        // Use GroupLayout for the label/cotrnl combos.
        // make the arrays for the factory method
        String[] labels = {
            "Choose Collection", "Search Using", 
            "Keyword or phrase", "Adjacent words"
        };
        String[] ccString = {"All", "Mostly", "Some", "Few"};
        String[] suString = {"Title", "Artist", "Arthor", "Type"};

        JPanel confirmAdjacent = new JPanel(new FlowLayout(FlowLayout.LEADING,5,0));
        confirmAdjacent.add(new JRadioButton("Yes", true));
        confirmAdjacent.add(new JRadioButton("No"));

        JComponent[] controls = {
            new JComboBox(ccString),
            new JTextField(20),
            new JComboBox(suString),
            confirmAdjacent
        };

        libSysSearchControl.add(getTwoColumnLayout(labels, controls));

        // throw in a few borders for white space
        Border border = new CompoundBorder(
                new EmptyBorder(10, 10, 10, 10),
                new TitledBorder("LIBSYS Search"));
        border = new CompoundBorder(
                border,
                new EmptyBorder(10, 10, 10, 10));
        libSysSearchControl.setBorder(border);
    }

    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception useDefault) {
                }
                LibSysSearch o = new LibSysSearch();

                JFrame f = new JFrame("Library System Search");
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                f.setLocationByPlatform(true);

                f.setContentPane(o.getUI());
                f.pack();
                f.setMinimumSize(f.getSize());

                f.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(r);
    }

    /**
     * Provides a JPanel with two columns (labels & fields) laid out using
     * GroupLayout. The arrays must be of equal size.
     *
     * Typical fields would be single line textual/input components such as
     * JTextField, JPasswordField, JFormattedTextField, JSpinner, JComboBox,
     * JCheckBox.. & the multi-line components wrapped in a JScrollPane -
     * JTextArea or (at a stretch) JList or JTable.
     *
     * @param labels The first column contains labels.
     * @param fields The last column contains fields.
     * @param addMnemonics Add mnemonic by next available letter in label text.
     * @return JComponent A JPanel with two columns of the components provided.
     */
    public static JComponent getTwoColumnLayout(
            JLabel[] labels,
            JComponent[] fields,
            boolean addMnemonics) {
        if (labels.length != fields.length) {
            String s = labels.length + " labels supplied for "
                    + fields.length + " fields!";
            throw new IllegalArgumentException(s);
        }
        JComponent panel = new JPanel();
        GroupLayout layout = new GroupLayout(panel);
        panel.setLayout(layout);
        // Turn on automatically adding gaps between components
        layout.setAutoCreateGaps(true);
        // Create a sequential group for the horizontal axis.
        GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
        GroupLayout.Group yLabelGroup = layout.createParallelGroup(GroupLayout.Alignment.TRAILING);
        hGroup.addGroup(yLabelGroup);
        GroupLayout.Group yFieldGroup = layout.createParallelGroup();
        hGroup.addGroup(yFieldGroup);
        layout.setHorizontalGroup(hGroup);
        // Create a sequential group for the vertical axis.
        GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
        layout.setVerticalGroup(vGroup);

        int p = GroupLayout.PREFERRED_SIZE;
        // add the components to the groups
        for (JLabel label : labels) {
            yLabelGroup.addComponent(label);
        }
        for (Component field : fields) {
            yFieldGroup.addComponent(field, p, p, p);
        }
        for (int ii = 0; ii < labels.length; ii++) {
            vGroup.addGroup(layout.createParallelGroup().
                    addComponent(labels[ii]).
                    addComponent(fields[ii], p, p, p));
        }

        if (addMnemonics) {
            addMnemonics(labels, fields);
        }

        return panel;
    }

    private final static void addMnemonics(
            JLabel[] labels,
            JComponent[] fields) {
        Map<Character, Object> m = new HashMap<Character, Object>();
        for (int ii = 0; ii < labels.length; ii++) {
            labels[ii].setLabelFor(fields[ii]);
            String lwr = labels[ii].getText().toLowerCase();
            for (int jj = 0; jj < lwr.length(); jj++) {
                char ch = lwr.charAt(jj);
                if (m.get(ch) == null && Character.isLetterOrDigit(ch)) {
                    m.put(ch, ch);
                    labels[ii].setDisplayedMnemonic(ch);
                    break;
                }
            }
        }
    }

    /**
     * Provides a JPanel with two columns (labels & fields) laid out using
     * GroupLayout. The arrays must be of equal size.
     *
     * @param labelStrings Strings that will be used for labels.
     * @param fields The corresponding fields.
     * @return JComponent A JPanel with two columns of the components provided.
     */
    public static JComponent getTwoColumnLayout(
            String[] labelStrings,
            JComponent[] fields) {
        JLabel[] labels = new JLabel[labelStrings.length];
        for (int ii = 0; ii < labels.length; ii++) {
            labels[ii] = new JLabel(labelStrings[ii]);
        }
        return getTwoColumnLayout(labels, fields);
    }

    /**
     * Provides a JPanel with two columns (labels & fields) laid out using
     * GroupLayout. The arrays must be of equal size.
     *
     * @param labels The first column contains labels.
     * @param fields The last column contains fields.
     * @return JComponent A JPanel with two columns of the components provided.
     */
    public static JComponent getTwoColumnLayout(
            JLabel[] labels,
            JComponent[] fields) {
        return getTwoColumnLayout(labels, fields, true);
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
1

i would probably do it like this:

public class Test {

    public Test() {

        JFrame frame = new JFrame();
        JPanel mainPanel = new JPanel(new GridLayout(0, 1));
        JPanel chooseCollectionPanel = new JPanel(new BorderLayout());
        JPanel keywordPanel = new JPanel(new BorderLayout());
        JPanel searchCategoryPanel = new JPanel(new BorderLayout());
        // ...
        mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        chooseCollectionPanel.setBorder(BorderFactory.createEmptyBorder(5, 0,
                5, 0));
        keywordPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
        searchCategoryPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5,
                0));

        JLabel chooseCollectionLabel = new JLabel("Choose Collection: ");
        JComboBox<String> chooseCollectionCB = new JComboBox<String>();
        chooseCollectionCB.addItem("All");
        chooseCollectionPanel.add(chooseCollectionLabel, BorderLayout.WEST);
        chooseCollectionPanel.add(chooseCollectionCB, BorderLayout.CENTER);

        JLabel chooseCollectionkeywordLabel = new JLabel("Choose Collection: ");
        JTextField keywordCB = new JTextField(10);
        keywordPanel.add(chooseCollectionkeywordLabel, BorderLayout.WEST);
        keywordPanel.add(keywordCB, BorderLayout.CENTER);
        // ...

        mainPanel.add(chooseCollectionPanel);
        mainPanel.add(keywordPanel);
        mainPanel.add(searchCategoryPanel);
        // ...
        frame.add(mainPanel);
        frame.setSize(400, 400);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        new Test();
    }
}
huidube
  • 390
  • 3
  • 15