Search code examples
javaswingjbuttonjcomponent

mouseover removes transparent background of round JButton in java


Once I hover over any of the round buttons a square background appears behind it and never disappears again. The button is still clickable while the square background isn't. I haven't implemented any MouseEvents so I have no idea why the background appears

https://i.sstatic.net/I1MIq.png left: after it's been clicked, center: after mouseover, right: original state

Code Sample

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class GUI implements ActionListener {
    private JLabel textLabel;
    private JButton buttonL, buttonConfirm, buttonR;
    
    public GUI() {
        JPanel mainDisplay = new JPanel();
        mainDisplay.setBackground(new Color(172, 181, 176));
        mainDisplay.setPreferredSize(new Dimension(150, 160));
        mainDisplay.setLayout(new BorderLayout());
        textLabel = new JLabel("1");
        textLabel.setHorizontalAlignment(SwingConstants.CENTER);
        mainDisplay.add(textLabel, BorderLayout.CENTER);
        
        JPanel container = new JPanel();
        container.setLayout(new GridBagLayout());
        GridBagConstraints gc = new GridBagConstraints();
        gc.gridwidth = GridBagConstraints.REMAINDER;
        gc.insets = new Insets(75, 0, 0, 0);
        container.add(mainDisplay, gc);
        
        Dimension buttonDimension = new Dimension(18, 18);
        Color buttonColor = new Color(242, 240, 241);
        buttonL = new RoundButton();
        buttonL.setBackground(buttonColor);
        buttonL.setPreferredSize(buttonDimension);
        buttonL.addActionListener(this);
        gc.gridwidth = GridBagConstraints.RELATIVE;
        gc.insets = new Insets(10, 20, 0, 0);
        container.add(buttonL, gc);
        
        buttonConfirm = new RoundButton();
        buttonConfirm.setBackground(buttonColor);
        buttonConfirm.setPreferredSize(buttonDimension);
        buttonConfirm.addActionListener(this);
        gc.insets = new Insets(40, -35, 0, 0);
        container.add(buttonConfirm, gc);
        
        buttonR = new RoundButton();
        buttonR.setBackground(buttonColor);
        buttonR.setPreferredSize(buttonDimension);
        buttonR.addActionListener(this);
        gc.insets = new Insets(10, -50, 0, 0);
        container.add(buttonR, gc);
        
        JFrame frame = new JFrame();
        frame.add(container);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == buttonL) {
            textLabel.setText("L");;
        } else if(e.getSource() == buttonConfirm) {
            textLabel.setText("");
        } else if(e.getSource() == buttonR) {
            textLabel.setText("R");
        }
    }
    
    public static void main(String[] args) {        
            new GUI();
    }
}

Round JButton

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import javax.swing.JButton;

public class RoundButton extends JButton {
    Shape shape;
    
    @Override
    protected void paintComponent(Graphics g) {
        g.setColor(getBackground());
        g.fillOval(0, 0, getSize().width - 1, getSize().height - 1);
    }

    @Override
    protected void paintBorder(Graphics g) {
        g.setColor(Color.darkGray);
        g.drawOval(0, 0, getSize().width - 1, getSize().height - 1);
    }

    @Override
    public boolean contains(int x, int y) {
        if (shape == null || !shape.getBounds().equals(getBounds())) {
            shape = new Ellipse2D.Float(0, 0, getWidth(), getHeight());
        }
        return shape.contains(x, y);
    }
}

Solution

  • When doing custom painting the basic code should be:

    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
    
        // add custom painting here
    }
    

    This will make sure the background is cleared so there are no painting artifacts.

    However, in your case you want the background of the parent panel to be painted before the button is painted so you need to use:

    button.setOpaque(false);
    

    Also, when you click on a button the rectangle will still appear indicating the button is in a pressed state. To remove this painting you need to use:

    button.setContentAreaFilled( false );
    

    Check out: Change JButton focus area for another implementation of a round button.