2

I have a class extending JButton that I am trying to apply a .png image to. The image is irregular in shape, and is surrounded by transparent pixels. I have overridden the paintComponent() method in the JButton to apply my buffered image to the button. Right now, the image is the only thing being drawn, which is what I want.

However, the button is still detecting events in the rectangle around it. Is there a way to limit detection to only the area containing opaque pixels (or rather to not detect events on the transparent pixels)?

Code for button class is below.

public class DrawButton extends JButton{

    private BufferedImage bi;

    public DrawButton(BufferedImage bi){
        setPreferredSize(new Dimension(bi.getWidth(), bi.getHeight()));
        this.bi = bi;
    }

    @Override
    protected void paintComponent(Graphics g){
        g.drawImage(bi, 0, 0, null);
        g.dispose();
    }
}
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
user18
  • 119
  • 7
  • @david Looked at your answer -- do I need to call `super.paintComponent()` first? If I do, it preserves the original button background, which I don't want. – user18 Dec 11 '12 at 17:21

1 Answers1

8

Well I would suggest using a MouseAdapter, and override mouseClicked(..). In mouseClicked check if pixel is alpha at point of click if it is do nothing, if not do something.

  • Always call super.paintComponent(..) as first call in overriden paintComponent method, but because, especially with buttons, this will redraw the JButton background call setContentAreaFilled(false) on JButton instance to stop this. You may also want setBorderPainted(false) too.

Here is a small example I made (adapted from here):

enter image description here

if click on smiley:

enter image description here

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

public class TransparentButton {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                final JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                MyButton button = null;

                try {
                    button = new MyButton(scaleImage(100, 100, ImageIO.read(new URL("http://2.bp.blogspot.com/-eRryNji1gQU/UCIPw0tY5bI/AAAAAAAASt0/qAvERbom5N4/s1600/original_smiley_face.png"))));
                } catch (Exception ex) {
                    ex.printStackTrace();
                }

                button.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent me) {
                        super.mouseClicked(me);
                        MyButton mb = ((MyButton) me.getSource());
                        if (!isAlpha(mb.getIconImage(), me.getX(), me.getY())) {
                            JOptionPane.showMessageDialog(frame, "You clicked the smiley");
                        }
                    }

                    private boolean isAlpha(BufferedImage bufImg, int posX, int posY) {
                        int alpha = (bufImg.getRGB(posX, posY) >> 24) & 0xFF;
                        return alpha == 0;
                    }
                });

                frame.add(button);

                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    public static BufferedImage scaleImage(int w, int h, Image img) throws Exception {
        BufferedImage bi;
        bi = new BufferedImage(w, h, BufferedImage.TRANSLUCENT);
        Graphics2D g2d = (Graphics2D) bi.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
        g2d.drawImage(img, 0, 0, w, h, null);
        return bi;
    }
}

class MyButton extends JButton {

    BufferedImage icon;

    MyButton(BufferedImage bi) {
        this.icon = ((BufferedImage) bi);
        setContentAreaFilled(false);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(icon.getWidth(), icon.getHeight());
    }

    public BufferedImage getIconImage() {
        return icon;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(icon, 0, 0, this);
    }
}
Community
  • 1
  • 1
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
  • Thanks so much! This works perfectly (at least it seems to so far)! Looks like you used code from the same other question I looked at ([link](http://stackoverflow.com/questions/6735891/creating-custom-jbutton-from-images-containing-transparent-pixels)), but I couldn't make heads or tails of it. This more complete example makes a lot of sense. – user18 Dec 11 '12 at 17:55
  • @user18 yup, just adapted the example to follow best practice and implement into working SSCCE fro your specific to your problem. – David Kroukamp Dec 11 '12 at 18:00
  • [use ButtonModel instead of MouseListener](http://stackoverflow.com/questions/5751311/creating-a-custom-button-in-java-with-jbutton/5755124#5755124) – mKorbel Jan 07 '13 at 08:02
  • 1
    Instead of using `return alpha == 0 ? true : false;` simply write `return alpha == 0`. ;-) – Guillaume Polet Jan 16 '13 at 22:56
  • @GuillaumePolet +1 lol very true talk about over complicating a simple line :) thanks – David Kroukamp Jan 16 '13 at 22:59