javaswingcolorsrepaintjcheckbox

Faulty repaint() of swing components when using custom background colors


When I use JCheckboxes or JScrollPane (applied to the main component that holds all others in order to generate a scrollable window) together with components that use

component.setBackground(new Color(R, G, B, A));

to define their background color, I am getting some obnoxious repaint() issues. Hovering over JCheckboxes activates MouseListener and the background of the JCheckbox will suddenly display a random other part of the window. This remains even when taking the mouse off the JCheckbox.

The issue disappears when using

JCheckbox.setRollOverEnabled(false);

BUT will still occur when selecting the checkbox!

The scrollpane will also not properly repaint. ONLY the parts that are outside of the visible frame will be painted several times in a row in direction of scrolling when they come back into the frame. It looks similar to that error on Windows OS when a program crashes and you can "draw" with the window on the screen because it "generates" a new window every time you move it (https://i.sstatic.net/L5G5Q.png).

The most interesting part is that the issue completely disappears when I use

Color.grey (or any other pre-generated color)

It also disappears when not selecting a custom background color at all.

So is there an issue with revalidate() and repaint() hidden anywhere in this? Is the use of RGBA a problem, specifically the A (= opacity) part since Color.AnyColor works?


Solution

  • Is the use of RGBA a problem, specifically the A (= opacity) part

    Yes, Swing does not support transparent backgrounds.

    Swing expects a component to be either:

    1. opaque - which implies the component will repaint the entire background with an opaque color first before doing custom painting, or
    2. fully transparent - in which case Swing will first paint the background of the first opaque parent component before doing custom painting.

    The setOpaque(...) method is used to control the opaque property of a component.

    In either case this makes sure any painting artifacts are removed and custom painting can be done properly.

    If you want to use tranparency, then you need to do custom painting yourself to make sure the background is cleared.

    The custom painting for the panel would be:

    JPanel panel = new JPanel()
    {
        protected void paintComponent(Graphics g)
        {
            g.setColor( getBackground() );
            g.fillRect(0, 0, getWidth(), getHeight());
            super.paintComponent(g);
        }
    };
    panel.setOpaque(false); // background of parent will be painted first
    

    Similar code would be required for every component that uses transparency.

    Or, you can check out Background With Transparency for custom class that can be used on any component that will do the above work for you.