Search code examples
javaswingjpaneljbuttonlayout-manager

Positioning of Swing Components Java - JPanel, JButton, JFrame


I'm currently studying Java GUI implementations and for my next assignment we are to create a program to simulate the control of a house. We have been given pretty much free reign to build it however we like (as long as its in Java). I've drawn up this mockup: http://i.imgur.com/9RtWL7b.jpg (shown below) and thats what I'm looking to produce.

Program Mockup: Program Mockup

Below is the code I have at the moment and it produces this http://i.imgur.com/XZLiwWx.jpg (below)

Example Code Below: Program Example

My question is; how can I get the three buttons to be in the to left of the screen? Am I going about this the wrong way? I feel like the code I have below is quite clunky and un-organised, should I be taking a different approach?

Let me know if any more information is needed.

import java.awt.*;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;

public class ImageTest {

    public static void main(String[] args) {
        ImagePanel panel            = new ImagePanel("program/assets/main_bg.jpg");

        TopTabButton buttonHome     = new TopTabButton("home");
        TopTabButton buttonSettings = new TopTabButton("settings");
        TopTabButton buttonHelp     = new TopTabButton("help");

        panel.add(buttonHome);
        panel.add(buttonSettings);
        panel.add(buttonHelp);


        panel.setPreferredSize(new Dimension(1000, 760));

        JFrame frame = new JFrame();
        frame.setBackground(new Color(53, 56, 64));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.getContentPane().add(panel);

        frame.pack();

        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setVisible(true);
    }

}

class TopTabButton extends JButton {

    public TopTabButton(String buttonCode) {
        setIcon(new ImageIcon("program/assets/top_tab_button_bg_" + buttonCode + "_inactive.png"));
        setRolloverIcon(new ImageIcon("program/assets/top_tab_button_bg_" + buttonCode + "_active.png"));
        setBorderPainted(false);
        setFocusPainted(false);
        setContentAreaFilled(true);
        setSize(new Dimension(126, 75));
     }

}

class ImagePanel extends JPanel {

    private Image img;

    public ImagePanel(String img) {
        this(Toolkit.getDefaultToolkit().createImage(img));
    }

    public ImagePanel(Image img) {
        this.img = img;
        Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
        setPreferredSize(size);
        setMinimumSize(size);
        setMaximumSize(size);
        setSize(size);
        setLayout(new GridBagLayout());
    }

    public void paintComponent(Graphics g) {
        g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
    }
}

class ButtonPanelTest extends JPanel {

    private Image img;

    public ButtonPanelTest(String name) {
        Button Button1 = new Button(name);
        Dimension size = new Dimension(100, 50);
        setSize(size);
        setLayout(new GridBagLayout());
    }

}

Solution

  • Let's start with the problem at hand...

    As JFrame uses a BorderLayout by default, doing frame.getContentPane().add(panel); is going to add it to the center position of the window. Instead, try frame.getContentPane().add(panel, BorderLayout.NORTH); instead

    The setSize call in TapTabButton is irrelevant. It's size will be established by the layout manager of the container it is added to...

    The calls to setPreferredSize(size), setMinimumSize(size) and setMaximumSize(size) are not recommended, instead, you should override the corresponding getXxxSize methods and return the size you want. This will prevent people from overriding them later ;)

    Also, the call to setSize is irrelevant...

    I'd be wary of ImageIcon. It uses a delegation model to load the image (passes it off to another Thread), meaning that getWidth and getHeight might actually return 0 as the there might not be enough data loaded to ascertain the size of the image.

    Instead, use ImageIO, this will read the image in before returning...

    You MUST call super.paintComponent in you paintComponent method! This is very important, as one of the jobs of paintComponent is to prepare the Graphics context for painting

    setSize in ButtonPanelTest...irrelevant

    Working Example

    Okay, took a little playing with, but basically, all the setXxxSize calls was messing about with things...

    enter image description here

    import java.awt.BorderLayout;
    import java.awt.Button;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.GridBagLayout;
    import java.awt.Image;
    import java.awt.Toolkit;
    import javax.swing.ImageIcon;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class ImageTest {
    
        public static void main(String[] args) {
            ImagePanel panel = new ImagePanel("program/assets/main_bg.jpg");
    
            TopTabButton buttonHome = new TopTabButton("home");
            TopTabButton buttonSettings = new TopTabButton("settings");
            TopTabButton buttonHelp = new TopTabButton("help");
    
            panel.add(buttonHome);
            panel.add(buttonSettings);
            panel.add(buttonHelp);
    
    
    //        panel.setPreferredSize(new Dimension(1000, 760));
    
            JFrame frame = new JFrame();
            frame.setBackground(new Color(53, 56, 64));
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            frame.add(panel, BorderLayout.NORTH);
    
            frame.pack();
    
            frame.setLocationRelativeTo(null);
    //        frame.setResizable(false);
            frame.setVisible(true);
        }
    
        class TopTabButton extends JButton {
    
            public TopTabButton(String buttonCode) {
                setText(buttonCode);
    //        setIcon(new ImageIcon("program/assets/top_tab_button_bg_" + buttonCode + "_inactive.png"));
    //        setRolloverIcon(new ImageIcon("program/assets/top_tab_button_bg_" + buttonCode + "_active.png"));
    //        setBorderPainted(false);
    //        setFocusPainted(false);
    //        setContentAreaFilled(true);
    //        setSize(new Dimension(126, 75));
            }
        }
    
        class ImagePanel extends JPanel {
    
            private Image img;
    
            public ImagePanel(String img) {
                this(Toolkit.getDefaultToolkit().createImage(img));
            }
    
            public ImagePanel(Image img) {
                this.img = img;
                Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
    //        setPreferredSize(size);
    //        setMinimumSize(size);
    //        setMaximumSize(size);
    //        setSize(size);
                setLayout(new GridBagLayout());
            }
    
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
            }
        }
    //
    //class ButtonPanelTest extends JPanel {
    //
    //    private Image img;
    //
    //    public ButtonPanelTest(String name) {
    //        Button Button1 = new Button(name);
    //        Dimension size = new Dimension(100, 50);
    //        setSize(size);
    //        setLayout(new GridBagLayout());
    //    }
    //}
    }
    

    Yes, I would probably build each element of the UI as separate panels, rather the trying to get it all to work in pane.

    This isolates control and responsibility as well as reduce layout congestion and confusion...