Swing transparent background not being repainted

落花浮王杯 提交于 2020-07-22 21:36:24

问题


I have a problem using transparent backgrounds in Swing. There are a lot of artefacts produced as swing is not repainting changed regions.

buggy

As far as I can tell there are 2 out-of-the-box ways to use transparent backgrounds:

  1. an opaque component with transparent color set as background (left txt field)

Problem: the transparent part of the background is never refreshed -> Artefacts.

  1. an non-opaque component with transparent color set as background (right txt field)

Problem: background is not being drawn at all.

What I do not want to do:

  • to use timers to auto repaint the frame (super awful)
  • to override paintComponent method (which actually works, but is really really awful)

I am running on Win7 x64

Aaaand here is my SSCCEEE:

Update 1: init with invokeLater (still won't work)

public class OpacityBug {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new OpacityBug();
            }
        });
    }

    static final Color transparentBlue = new Color(0f, 1f, 0f, 0.5f); 

    JFrame frame;
    JPanel content;

    JTextField txt1;
    JTextField txt2;

    public OpacityBug() {
        initFrame();
        initContent();
    }

    void initFrame() {
        frame = new JFrame();
        frame.setSize(300, 80);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    void initContent() {
        content = new JPanel();
        content.setDoubleBuffered(true);
        content.setBackground(Color.red);
        frame.getContentPane().add(content);

        txt1 = new JTextField() {
            @Override
            public void setBorder(Border border) {
                super.setBorder(null); //nope border
            }
        };
        txt1.setText("Hi! I am Buggy!");
        txt1.setOpaque(true);
        txt1.setBackground(transparentBlue);
        content.add(txt1);

        txt2 = new JTextField() {
            @Override
            public void setBorder(Border border) {
                super.setBorder(null); //nope border
            }
        };
        txt2.setText("And I have no BG!");
        txt2.setOpaque(false);
        txt2.setBackground(transparentBlue);
        content.add(txt2);

        content.revalidate();
        content.repaint();
    }
}

Update 2

As some of you noticed, it seems Swing that swing is unable to paint transparent backgrounds. But it is not (yet) clear to me why, I searched for the piece of code responsible for drawing the background of the component and have found the following code in ComponentUI.java :

public void update(Graphics g, JComponent c) {
if (c.isOpaque()) {
    g.setColor(c.getBackground());
    g.fillRect(0, 0, c.getWidth(),c.getHeight());
}
paint(g, c);
}

As you can see, it assumes that if a component is not opaque the background doesn't need to be repainted. I say that this is a very vague assumption.

I would propose following implementation:

public void update(Graphics g, JComponent c) {
if(c.isOpaque() || (!c.isOpaque() && c.isBackgroundSet())) {
    g.setColor(c.getBackground());
    g.fillRect(0, 0, c.getWidth(), c.getHeight());
}
paint(g, c);
}

I simply check if the background was set at all when the component is not opaque. This simple addition would allow us to use transparent backgrounds in swing. At least I do not know any reasons why it should not be done that way.


回答1:


By using a transparent background you are breaking Swings painting rules. Basically when the component is opaque you promise to paint the background of the component. But because the background is transparent there is nothing to paint.

Check out Backgrounds With Transparency for more information and a couple of simple solutions.




回答2:


You should respect Swing's threading policy and have the GUI initialized on the GUI thread:

SwingUtilities.invokeLater(() -> new OpacityBug());

I can't tell whether this is all it will take to correct the behavior on your side, but it has done just that on mine (OS X).




回答3:


I cannot stress this enough, I see a ton of people not doing this while it is vital to ensure correct behavior from Swing; ALL GUI instances must run on the EDT(Event Dispatch Thread)

Please read the below article and adjust your code and report back with the effects.

https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html




回答4:


You should use setOpaque(false) on the component that has a problem, and do the same with all the parents !

For example, if you have a JList 'jList' inside a JScrollPane 'scrollPane', the whole thing being inside a JPanel 'jPanel', you should use :

jList.setOpaque(false);
scrollPane.setOpaque(false);
scrollPane.getViewport().setOpaque(false);
jPanel.setOpaque(false);

And yes, if you have a JScrollPane, you should set its viewport's opacity to false as well.

This will prevent painting problems on your components with transparent backgrounds.



来源:https://stackoverflow.com/questions/27399910/swing-transparent-background-not-being-repainted

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!