Home > other >  Display interactive circular overlay on JLabel
Display interactive circular overlay on JLabel

Time:09-16

I have a JFrame containing a JLabel displaying a BufferedImage. When I hover my mouse on this image, I need to display a circular overlay where the mouse which contains another image.

My code so far:

JFrame frame = new JFrame();
GridBagLayout layout = new GridBagLayout();
frame.getContentPane().setLayout(gLayout);
JLabel myLabel= new JLabel(new ImageIcon(baseImage));
GridBagConstraints constraints = new GridBagConstraints();
...
frame.getContentPane().add(myLabel, constraints);

Now I need to display a circular overlay at the location of the mouse displaying another BufferedImage.

So I need something like this:

myLabel.onMouseHover(event -> {
    Pane p = new Pane();
    x = event.x;
    y = event.y;
    p.setImage(newImage);
    // draw this pane on the label but with an offset for it to be at the center
    myLabel.draw(pane, x - offset, y - offset);
})

CodePudding user response:

If I understand correctly, then you kind of want a "see through" style effect. Event if you don't and you want to display a static image at the point of the mouse, the "basic" idea will work, you'll just have to reposition the overlaid image to the correct position.

This example takes two images which are the same size and as you move the mouse around, it will make it appear as if you're "seeing through" the main image. It's illusion, but most effects like this are.

Simple

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
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;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Point mousePoint;

        private BufferedImage background;
        private BufferedImage seeThrough;

        public TestPane() {

            try {
                background = ImageIO.read(BYO your own image));
                seeThrough = ImageIO.read(BYO your own image));
            } catch (IOException ex) {
                ex.printStackTrace();;
            }

            MouseAdapter ma = new MouseAdapter() {
                @Override
                public void mouseMoved(MouseEvent e) {
                    mousePoint = e.getPoint();
                    repaint();
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    mousePoint = null;
                    repaint();
                }

            };

            addMouseMotionListener(ma);
            addMouseListener(ma);
        }

        @Override
        public Dimension getPreferredSize() {
            if (background != null) {
                return new Dimension(background.getWidth(), background.getHeight());
            }

            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
            g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));

            if (background != null) {
                g2d.drawImage(background, 0, 0, this);
            }

            if (seeThrough != null && mousePoint != null) {
                double radius = 45;
                Ellipse2D.Double clip = new Ellipse2D.Double(mousePoint.x - radius, mousePoint.y - radius, radius * 2, radius * 2);
                g2d.setClip(clip);
                g2d.drawImage(seeThrough, 0, 0, this);
            }

            g2d.dispose();
        }

    }
}

But this doesn't use a JLabel

No, it doesn't. JLabel is a pain in the ... code. There's no way to ascertain the location of the image, assuming that's important to you, but in most cases, I'd prefer to have control. You could do something similar with a JLabel, conceptually it's the same idea.

Another solution might be to use a type of "overlay" panel directly on top of the target component, for example

Pointy

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new BorderLayout());
            JLabel label = new JLabel();
            OverlayPane overlayPane = new OverlayPane(label);
            try {
                label.setIcon(new ImageIcon(ImageIO.read(BYO your own image))));
            } catch (IOException ex) {
                ex.printStackTrace();;
            }

            add(overlayPane);
        }

    }

    public class OverlayPane extends JPanel {

        public OverlayPane(JComponent child) {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.fill = GridBagConstraints.BOTH;

            add(new GlassPane(), gbc);
            add(child, gbc);
        }

        protected class GlassPane extends JPanel {

            private Point mousePoint;
            private BufferedImage pointer;

            public GlassPane() {
                try {
                    pointer = ImageIO.read(BYO your own image));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }

                MouseAdapter ma = new MouseAdapter() {
                    @Override
                    public void mouseMoved(MouseEvent e) {
                        mousePoint = e.getPoint();
                        repaint();
                    }

                    @Override
                    public void mouseExited(MouseEvent e) {
                        mousePoint = null;
                        repaint();
                    }

                };

                addMouseMotionListener(ma);
                addMouseListener(ma);

                setOpaque(false);
            }

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();

                g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
                g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));

                if (pointer != null && mousePoint != null) {
                    double radius = Math.max(pointer.getWidth()   10, pointer.getHeight()   10) / 2;
                    g2d.setColor(Color.WHITE);
                    Ellipse2D.Double clip = new Ellipse2D.Double(mousePoint.x - radius, mousePoint.y - radius, radius * 2, radius * 2);
                    g2d.fill(clip);
                    int x = (int) (mousePoint.x - radius)   5;
                    int y = (int) (mousePoint.y - radius)   5;
                    g2d.drawImage(pointer, x, y, this);
                }

                g2d.dispose();
            }
        }

    }
}

Alternatively, you could just use the frame's glassPane directly, but needs drives musts

Annnd JLayer style concept - sure, it allows you to draw dots, but it should give you what you need to make an image move over the top

  • Related