Java ImageIcons and actioin listeners
Asked Answered
H

1

1

I am creating a simple game where a person clicks on an image the score increases by one. It seems simple enough, right? Here's the catch-- the images will be hidden partially behind other images!

Currently, I'm using several imageIcons to set up my scene. For instance, my foreground has an image "foreground.png," my background is "background.png", and my image that is hiding between the two is "hiding.png".

My first thought was to simply get the coordinates of the imageIcon hiding, add the height() and width() to them, and create a mouse listener that would only work in that specified region. However, that would give me a rectangle for the user to click which would defeat the purpose of hiding the object (someone could click the rigid boundary of the graphic behind the foreground).

Do you have any suggestions on how to make a mouse action listener work only on the VISIBLE pixels of an imageIcon? Yes, I understand that action listeners can only be applied to components (such as buttons) but "the button" just doesn't do what I want for this project.

Halden answered 8/4, 2013 at 4:52 Comment(4)
It depends on how you are rendering the images. If you use JLabel, you could attach a mouse listener to the JLabel, based on the Z-Order of the component Layout, you'd only be able to click the visible area of the label (as the one above it would consume it)Betroth
Could I paint JLabels behind images (I overrode the paintComponent() to draw the foreground and background)?Halden
Do you care about transparency?Betroth
Yes, but if the "non transparent" method could point me in the right direction I'll take a look at it :)Halden
B
5

Example 1

This basically uses a series of JLabels on a JLayeredPane. Each label has it's own mouse listener and when you mouse over it, will turn red. But, if there is a label above it, it won't respond to mouse events...

enter image description here

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ClickMyImages {

    public static void main(String[] args) {
        new ClickMyImages();
    }

    public ClickMyImages() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JLayeredPane {

        public TestPane() {
            try {
                BufferedImage img1 = ImageIO.read("/Image1");
                BufferedImage img2 = ImageIO.read("/Image2");
                BufferedImage img3 = ImageIO.read("/Image3");
                BufferedImage img4 = ImageIO.read("/Image4");
                BufferedImage img5 = ImageIO.read("/Image5");

                JLabel label1 = new ClickableLabel(new ImageIcon(img1));
                JLabel label2 = new ClickableLabel(new ImageIcon(img2));
                JLabel label3 = new ClickableLabel(new ImageIcon(img3));
                JLabel label4 = new ClickableLabel(new ImageIcon(img4));
                JLabel label5 = new ClickableLabel(new ImageIcon(img5));

                Dimension masterSize = getPreferredSize();

                Dimension size = label1.getPreferredSize();
                label1.setBounds((masterSize.width - size.width) / 2, (masterSize.height - size.height) / 2, size.width, size.height);
                Point masterPoint = label1.getLocation();
                size = label2.getPreferredSize();
                label2.setBounds(
                        masterPoint.x - (size.width / 2), 
                        masterPoint.y - (size.height  / 2), 
                        size.width, size.height);
                size = label3.getPreferredSize();
                label3.setBounds(
                        masterPoint.x + (size.width / 2), 
                        masterPoint.y - (size.height  / 2), 
                        size.width, size.height);
                size = label4.getPreferredSize();
                label4.setBounds(
                        masterPoint.x - (size.width / 2), 
                        masterPoint.y + (size.height  / 2), 
                        size.width, size.height);
                size = label5.getPreferredSize();
                label5.setBounds(
                        masterPoint.x + (size.width / 2), 
                        masterPoint.y + (size.height  / 2), 
                        size.width, size.height);

                add(label1);
                add(label2);
                add(label3);
                add(label4);
                add(label5);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(800, 800);
        }
    }

    // This is for demonstration purposes only!
    public class ClickableLabel extends JLabel {

        private boolean isIn = false;

        public ClickableLabel(Icon image) {
            super(image);
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseEntered(MouseEvent e) {
                    isIn = true;
                    repaint();
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    isIn = false;
                    repaint();
                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (isIn) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
                g2d.setColor(Color.RED);
                g2d.fillRect(0, 0, getWidth(), getHeight());
                g2d.dispose();
            }
        }
    }
}

Example 2

This examples uses the paintComponent method to renderer the images. It checks the pixel alpha of the image at the mouse point to determine if the mouse event should fall through or not.

I've been a little strict using an alpha value of 255, but you could soften it a little based on your needs (something like 225 instead for example)...

I've hard coded the layers so that the tree is always above the squirrel, but it wouldn't be hard to add all the images to List in the order you want them to appear and simple run down the list till you get a hit.

enter image description here

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ClickMyDrawnImages {

    public static void main(String[] args) {
        new ClickMyDrawnImages();
    }

    public ClickMyDrawnImages() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private BufferedImage tree;
        private BufferedImage squirrel;
        private BufferedImage mouseOver;

        public TestPane() {
            try {
                tree = ImageIO.read(new File("Tree.png"));
                squirrel = ImageIO.read(new File("Squirrel.png"));
            } catch (IOException exp) {
                exp.printStackTrace();
            }
            addMouseMotionListener(new MouseAdapter() {
                @Override
                public void mouseMoved(MouseEvent e) {
                    if (withinTree(e.getPoint())) {
                        mouseOver = tree;
                    } else if (withinSquirrel(e.getPoint())) {
                        mouseOver = squirrel;
                    } else {
                        mouseOver = null;
                    }
                    repaint();
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        protected boolean withinTree(Point p) {
            return withinBounds(p, getTreeBounds(), tree);
        }

        protected boolean withinSquirrel(Point p) {
            return !withinBounds(p, getTreeBounds(), tree) && withinBounds(p, getSquirrelBounds(), squirrel);
        }

        protected Rectangle getTreeBounds() {
            int width = getWidth();
            int height = getHeight();

            int x = (width - tree.getWidth()) / 2;
            int y = (height - tree.getHeight()) / 2;

            return new Rectangle(x, y, tree.getWidth(), tree.getHeight());
        }

        protected Rectangle getSquirrelBounds() {
            Rectangle bounds = getTreeBounds();

            return new Rectangle(
                    bounds.x - (squirrel.getWidth() / 4),
                    (getHeight() - squirrel.getHeight()) / 2,
                    squirrel.getWidth(), squirrel.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            int width = getWidth();
            int height = getHeight();

            int x = (width - tree.getWidth()) / 2;
            int y = (height - tree.getHeight()) / 2;

            g.drawImage(highlight(squirrel), x - (squirrel.getWidth() / 4), (height - squirrel.getHeight()) / 2, this);
            g2d.drawImage(highlight(tree), x, y, this);

            g2d.dispose();
        }

        protected BufferedImage highlight(BufferedImage img) {
            BufferedImage highlight = img;
            if (img.equals(mouseOver)) {
                highlight = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = highlight.createGraphics();
                g2d.setColor(Color.RED);
                g2d.drawImage(img, 0, 0, this);

                g2d.setComposite(AlphaComposite.SrcAtop.derive(0.5f));
                g2d.fillRect(0, 0, highlight.getWidth(), highlight.getHeight());
                g2d.dispose();
            }
            return highlight;
        }

        protected boolean withinBounds(Point p, Rectangle bounds, BufferedImage image) {
            boolean withinBounds = false;
            if (bounds.contains(p)) {
                int x = p.x - bounds.x;
                int y = p.y - bounds.y;
                int pixel = image.getRGB(x, y);
                int a = (pixel >> 24) & 0xFF;
                // could use a little weighting, so translucent pixels can be effected
                if (a == 255) {
                    withinBounds = true;
                }
            }
            return withinBounds;
        }
    }
}
Betroth answered 8/4, 2013 at 5:18 Comment(2)
Thank you so much! this is exactly what wanted to doHalden
@Halden I've updated to include a custom paint example as well.Betroth

© 2022 - 2024 — McMap. All rights reserved.