Search code examples
javaimageswingjbutton

Java: Overlay an Image with Transparent JButton


So I'm creating a GUI that represents a vending machine. I'm just running into some problems trying to get the layout to work the way I want it. My thought was to insert an image into a JLabel, and then overlay that image with transparent JButtons in specific locations so that when you click on the image in certain locations, it will trigger the JButton. I haven't gotten to the transparency yet as I'm currently stuck on how to get the JButtons precisely where they need to be.

I've tried setLocation and setBounds with no luck. Any help on how to exactly position the jbuttons over the possible vending machine choices would be great.

import javax.swing.*;
import javax.imageio.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;

public class vendMachine extends JFrame
{
//Frame Dimensions 
private static final int FRAME_HEIGHT = 800;
private static final int FRAME_WIDTH = 800;


private JPanel totalGUI, imagePanel, coinPanel;

public vendMachine()
{
    createComponents();
    setSize(FRAME_WIDTH, FRAME_HEIGHT);
    setTitle("Vending Machine");
}

private void createComponents()
{
    try
    {
        BufferedImage machineImg = ImageIO.read(new File("images/pepsivend.jpg"));
        JLabel machineImgLabel = new JLabel(new ImageIcon(machineImg));
        machineImgLabel.setLayout(new FlowLayout());
        JButton test = new JButton("TEST BUTTON");
        machineImgLabel.add(test);
        //test.setBounds(0,0,0,0);


        ImageIcon pennyIcon = new ImageIcon("images/coins/penny.jpg");
        JButton pennyButton = new JButton(pennyIcon);            
        ImageIcon nickelIcon = new ImageIcon("images/coins/nickel.jpg");
        JButton nickelButton = new JButton(nickelIcon);
        ImageIcon dimeIcon = new ImageIcon("images/coins/dime.jpg");
        JButton dimeButton = new JButton(dimeIcon);
        ImageIcon quarterIcon = new ImageIcon("images/coins/quarter.jpg");
        JButton quarterButton = new JButton(quarterIcon);

        coinPanel = new JPanel();
        coinPanel.setLayout(new GridLayout(4,1));
        coinPanel.add(pennyButton);
        coinPanel.add(nickelButton);
        coinPanel.add(dimeButton);
        coinPanel.add(quarterButton);


        totalGUI = new JPanel();
        totalGUI.setLayout(new BorderLayout());
        totalGUI.add(machineImgLabel, BorderLayout.CENTER);
        totalGUI.add(coinPanel, BorderLayout.EAST);
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

    add(totalGUI);
}

}

Button needs to go here

In the above Image I would like some help on how to get the test button, to overlay on top of the pepsi selection. From there I can go about making it transparent and removing the borders and text.

Edited to add: none of the buttons do anything yet. Simply trying to get the layout going before adding in anything else


Solution

  • It's unclear as to what your actually problem, however, I'll start with the layout...

    No single layout will ever do everything you want, some times, you'll need to use multiple layouts and compound them. This example uses a BorderLayout and a GridBagLayout to set up the basic layout...

    Example

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.Insets;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new VendingMachinePane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class VendingMachinePane extends JPanel {
    
            public VendingMachinePane() {
                setLayout(new BorderLayout());
                JLabel label = new JLabel("Cover");
                // Demonstration purpose only
                label.setPreferredSize(new Dimension(200, 400));
                label.setOpaque(true);
                label.setBackground(Color.BLUE);
    
                add(label);
    
                JPanel optionsPane = new JPanel(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.weightx = 1;
                gbc.gridwidth = GridBagConstraints.REMAINDER;
                gbc.anchor = GridBagConstraints.NORTH;
    
                optionsPane.setBackground(Color.DARK_GRAY);
                optionsPane.add(new JLabel("Coin Slot"), gbc);
                optionsPane.add(makeButton("Pepsi"), gbc);
                optionsPane.add(makeButton("Diet Pepsi"), gbc);
                optionsPane.add(makeButton("Slice"), gbc);
                optionsPane.add(makeButton("Dr Pepper"), gbc);
                optionsPane.add(makeButton("Lipton"), gbc);
                optionsPane.add(makeButton("Mountain Dew"), gbc);
                optionsPane.add(makeButton("Schweppes"), gbc);
                gbc.weighty = 1;
                optionsPane.add(makeButton("Pepsi"), gbc);
    
                add(optionsPane, BorderLayout.LINE_END);
            }
    
            protected JButton makeButton(String text) {
                JButton btn = new JButton(text);
                btn.setBorderPainted(false);
                btn.setContentAreaFilled(false);
                btn.setMargin(new Insets(4, 4, 4, 4));
                btn.setOpaque(false);
                return btn;
            }
    
        }
    
    }
    

    As to your "overlay buttons" issue, to me, that doesn't make sense, since a JButton has a icon property, why not just use a JButton to start with?

    You make buttons transparent simply by changing their borderPainted contentAreaFilled and opaque properties

    // You can pass a `Icon` instead of a `String` to the constructor
    JButton btn = new JButton(text);
    btn.setBorderPainted(false);
    btn.setContentAreaFilled(false);
    btn.setMargin(new Insets(4, 4, 4, 4));
    btn.setOpaque(false);
    

    Don't forget to setup an ActionListener ;)

    Updated, based on the updated requirements...

    You Could...

    Break the image down in segments, making each element it's own image and simply applying those to the buttons, using a similar approach above

    You Could...

    Map hot spots on the image, and using a MouseListener monitor where the mouseClicked events occur - you do lose the advantage of keyboard input though

    You Could...

    Map out the hot spots of the image and using a GridBagLayout or custom layout manager, map the buttons onto the image.