When does a JPanel paint (or repaint) its child components?

被刻印的时光 ゝ 提交于 2019-11-28 13:51:35

I've reduced the example to just the anti-aliasing, and I am unable to reproduce the problem. It doesn't appear to be platform dependent. I'm not sure why you are using getClipBounds().

Addendum:

The JPanel background (a gradient) needs to shine through…

I've update the example to use a gradient background behind a transparent button; I've put anti-aliased (left) and aliased (right) examples side-by-side. I see no unexpected behavior.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.plaf.basic.BasicButtonUI;

/** @see http://stackoverflow.com/questions/5169647 */
public class ButtonUITest extends JPanel {

    public ButtonUITest() {
        this.setLayout(new GridLayout(1, 0));
        this.setPreferredSize(new Dimension(640, 480));
        this.add(new CustomButton(true));
        this.add(new CustomButton(false));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        int w = this.getWidth();
        int h = this.getHeight();
        Graphics2D g2d = (Graphics2D) g;
        g2d.setPaint(new GradientPaint(0, 0, Color.blue, w, h, Color.red));
        g2d.fillRect(0, 0, w, h);
    }

    private void display() {
        JFrame f = new JFrame("ButtonUITest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static class CustomButton extends JButton {

        public CustomButton(boolean antialiased) {
            this.setOpaque(false);
            this.setUI(new CustomButtonUI(antialiased));
        }
    }

    private static class CustomButtonUI extends BasicButtonUI {

        private boolean antialiased;

        public CustomButtonUI(boolean antialiased) {
            this.antialiased = antialiased;
        }

        @Override
        public void paint(Graphics g, JComponent c) {
            int w = c.getWidth();
            int h = c.getHeight();
            Graphics2D g2d = (Graphics2D) g;
            if (antialiased) {
                g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            }
            g2d.setColor(Color.LIGHT_GRAY);
            g2d.fillOval(0, 0, w, 2 * h);
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ButtonUITest().display();
            }
        });
    }
}

For antialiasing to work consistently, your component needs to return false for isOpaque. Otherwise, the RepaintManager is free to skip painting the region behind your component.

I suspect that if you use a screen magnifier to look at the "unaliased" edges, you will find they really are antialiased. But it was done against against a black, unpainted background, not the parent's background.

The Swing experts who know what they're talking about will be here shortly. In the meantime, let me comment on this:

Now, my question relates to wether this is a good idea? I do after all repaint the parent component from a child component. I wonder if this lead to a loop of repaints? If the parent tries to repaint its children and the children tries to repaint its parent - then i assume we're talking about a loop.

Once you try it out and see that it's not a problem on your machine, chances are that it will be true on all JVMs you try. That is to say, the proof is in the pudding, or random bumbling does generally lead to positive results in Swing. Recursive loops have a way of causing the program to halt pretty quickly in Java, so the answer is... if this were totally wrong you'd already know. Plus you can put sysouts in there to see if this is happening (it's obviously not).

That said, there may be a better way to deal with your problem, but if your answer works, stick with it for now.

AFAIK, repaint() just tells the system to repaint that component in the next paint cycle, i.e. if you call repaint() in a paint() method the repaint might be done in the next cycle.

Generally, Swing runs in its own thread, so repeated repaint should not stop the application logic. However, it might be using processing power, if the system always repaints the ui even if there are no changes.

You could try and write a log message to see when and how often the button is painted. If that happens continously even if nothing happens that would cause a ui update, you might have to find a solution. If not, you should be fine.

Edit: there is a class called RepaintManager that you might find interesting.

Doing a repaint in there is definitely a bad idea. Try adding b.setOpaque(false) in installUI(). Your button is no longer painting its entire bounds because of the antialiasing. You need to let the background show through to fill the gaps.

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