简体   繁体   中英

Java GUI builder - How to make an irregular shaped button

I have been working the the NetBeans GUI builder and all I want to do is have a picture (say a stick figure) and an action to only happen when i click the stick figure itself, not just its entire rectangle. I've googled for hours and I can't find anything, please help!

Edit: Mainly the question is how do I get it to ignore transparent pixels on the button?

package my.usefulness;

public class usefulness extends javax.swing.JFrame
{
    public usefulness()
    {
        initComponents();
    }
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jButton1 = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jButton1.setIcon(new javax.swing.ImageIcon("C:\\Users\\rando_000\\Desktop\\buttonImage.png")); // NOI18N
        jButton1.setBorderPainted(false);
        jButton1.setContentAreaFilled(false);
        jButton1.setFocusPainted(false);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(149, 149, 149)
                .addComponent(jButton1)
                .addContainerGap(200, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jButton1)
                .addGap(0, 237, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>                
}

I was a bit curious about this myself so I tested out creating a new component type. I took the approach then when you click on this component it should check the mouse event location with the image pixel data, if the pixel is transparent then the mouse event should not be registered. This implementation only processes mouse down events and class is far from complete but it should give you a good start! Checkout the TODO tags I put in there

public class TransparentImageButton extends JComponent implements MouseListener {

    private BufferedImage image = null;
    private File imageFile;
    private List<ActionListener> listeners;

    public TransparentImageButton(File imageFile) throws IOException {
        this.imageFile = imageFile;
        this.image = ImageIO.read(imageFile);
        this.addMouseListener(this);
        this.listeners = new ArrayList<ActionListener>();
    }

    public void addActionListener(ActionListener listener) {
        listeners.add(listener);
    }

    @Override
    protected void paintComponent(Graphics g) { 
        super.paintComponent(g);
        Rectangle r = getImageBounds();
        g.drawImage(image, r.x, r.y, r.width, r.height, this);
    }


    private Rectangle getImageBounds() {
        // TODO Add in proper handling if component size < image size.
        return new Rectangle((int)((getBounds().width-image.getWidth())/2), (int)((getBounds().height-image.getHeight())/2), image.getWidth(), image.getHeight());
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        // TODO Add more action events?
    }

    @Override
    public void mousePressed(MouseEvent e) {
        Rectangle bounds = getImageBounds();
        if(bounds.contains(e.getPoint())) {
            int ix = e.getX()-bounds.x;
            int iy = e.getY()-bounds.y;
            int [] arr = image.getData().getPixel(ix, iy, new int[4]);
            // get the alpha for the current pixel
            if(arr[3] != 0) {
                // not transparent
                ActionEvent newActionEvent = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, imageFile.getName(), e.getWhen(), e.getModifiers());
                for(ActionListener listener : listeners) {
                    listener.actionPerformed(newActionEvent);
                }
            }
        } else {
        }
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        // TODO Add more action events?
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        // TODO Add more action events?
    }

    @Override
    public void mouseExited(MouseEvent e) {
        // TODO Add more action events?
    }
} 

I tested it using this code:

public static void main(String[] args) throws FileNotFoundException, IOException {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(500, 400);
    frame.setLocationByPlatform(true);

    TransparentImageButton btn = new TransparentImageButton(new File("c:\\icon.png"));
    btn.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Clicky!!");
        }
    });
    frame.getContentPane().add(btn);
    frame.setVisible(true);
}

And used this image:

样本按钮图片

There is no such thing as "irregular shaped button " , not that i know of anyways... you would have to make that yourself .

The thing here is you may be confusing between an image and a java component. What i mean is there is no way for java to determine what is the point of interest in your image , thats why the image is considered a rectangle .If you worked with flash , photoshop or any advanced drawing tool : then its kinda like the difference between a vector and an image .

Anyways , to solve your problem , you can define a Poligon that has the same shape as your stick figure . To do that you define the Point s that make the Polygon , like so :

//a triangle for example 
int[] x = {100,50,150}; // the X postion of the points that form the polygon
int[] y = {100,200,200};// the y pos
Polygon pol = new Polygon(x,y,3) // 3 is number of points .

now inside the mouse event listener :

 public void mouseClicked(MouseEvent evt){
{
  if(pol.contains(evt.getPoint()){
    // do w.e your button should do 
   }
}

now this way , if the mouse click event is inside the Polygon pol (which should be your stick figure ) , you then do what your button is intended to do

Swing per se isn't capable of handling click events for non-rectangular shapes (as far as I know). You could try to catch the mouse position on the click event and calculate if this xy-coordinate is inside you shape or not.

I don't know what your image looks like, but it guess it is some sort of figure on a transparent or at least uniform background. If so, you could simply check if the pixel-color at that xy-coordinate is that specific background color or not.

That way you have a simple and fast algorithm... I wouldn't say it's the best, but maybe good enough :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM