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

后端 未结 5 793
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-11 21:17

I\'ve got a JButton which is painted using a custom UI delegate (CustomButtonUI extends BasicButtonUI). The CustomButtonUI\'s paint() method draws the button with rounded \"

相关标签:
5条回答
  • 2020-12-11 22:02

    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.

    0 讨论(0)
  • 2020-12-11 22:03

    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.

    0 讨论(0)
  • 2020-12-11 22:09

    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.

    0 讨论(0)
  • 2020-12-11 22:13

    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.

    ButtonUITest.png

    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();
                }
            });
        }
    }
    
    0 讨论(0)
  • 2020-12-11 22:18

    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.

    0 讨论(0)
提交回复
热议问题