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

I have a JButton that is written using the user delegate of the user interface (CustomButtonUI extends BasicButtonUI). The customButtonUI paint () method draws a button with rounded “smooth” corners to make apperance as smooth as possible.

Somehow the “smoothed” edges of the button disappear every time I drag the mouse over the button. This makes the edges of the buttons look “pixelated”. However, as soon as I add a line of code to recolor the parent of the button, smoothing will hit even when I click the mouse button above the button.

Now, my question is, is this a good idea? I will still repaint the parent component from the child component. I wonder if this led to the appearance of a loop? If a parent is trying to redraw his children, and the children are trying to repaint their parent, then I guess we are talking about a loop.

I linked my code as a link. Any comments are very welcome!

public class JCustomButtonUI extends BasicButtonUI { @Override public void installUI(JComponent c) { super.installUI(c); AbstractButton b = (AbstractButton) c; b.setBorderPainted(false); } @Override public void paint(Graphics g, JComponent c) { //Cast the Graphics instance to a Graphics2D instance. Graphics2D g2d = (Graphics2D) g; JButton b = (JButton) c; //Repaint parent component to make sure that we get "antialiased" //edges. b.getParent().repaint(); //Get the component height and width. int w = (int) g.getClipBounds().getWidth(); int h = ((int) g.getClipBounds().getHeight()); //Make sure the button is drawn with "antialiased" edges. g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setColor(Color.GRAY); g2d.fillRoundRect(0, 0, w, h, w, h); } } 

Update 1

To illustrate the alias and the smooth border, please take a look at the two photos below. When I (from the ButtonUI paint () method) manually call the parent JPanel repaint method, all borders are completely smoothed all the time. However, when I do not manually call the parent JPanel repaint method, the borders no longer smooth out as soon as I click the mouse button above the button.

AlasedAntialiased

Update 2

I have separated the entire “component”, which consists of JPanel, JSlider and a pair of JButtons on Snipt. Please get it from http://snipt.org/wnllg .

Update 3

I think I managed to get it to work. Instead of painting the JPanel background in my paintComponent () method, I created a JCustomPanelUI that I installed on JPanel. I don't think this was the solution itself, but instead of using the width and height from the Graphics instance, I tried using widht and height from the JPanel itself. I'm not quite sure why something is wrong when I use the width and height from the Graphics instance. I thought that the width and height from the Graphics instance were already “prepared” relative to the dimensions from the JPanel component. You can look at the last component here: http://snipt.org/wnlli ,

+4
source share
5 answers

I gave an example only to smoothing, and I can not reproduce the problem. It is platform independent. I'm not sure why you are using getClipBounds() .

Application:

The JPanel background (gradient) should be drawn through ...

I am updating an example using a gradient background behind a transparent button; I set the smoothed (left) and pseudonymous (right) examples side by side. I do not see 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(); } }); } } 
+4
source

For smoothing to work consistently, your component must return false for isOpaque . Otherwise, the RepaintManager may skip drawing the area behind your component.

I suspect that if you use a magnifier to look at the “undefined” edges, you will find that they are really smooth. But this was done against a black, unpainted background, not a parent background.

+2
source

The Swing experts who know what they are talking about will be here soon. At the same time, let me comment on this:

Now my question is. Is this a good idea? I still recolor the parent component from the child component. I wonder if this will lead to a repainting loop? If a parent is trying to repaint his children and the children are trying to repaint his parent - then I assume that we are talking about a cycle.

Once you try this and see that this is not a problem on your computer, there is a chance that this will be true on all the JVMs you are trying. That is, the evidence is in the pudding, or accidental uncertainty generally leads to positive results in Swing. Recursive loops have a way to make the program stop pretty quickly in Java, so the answer would be ... if it were completely wrong , which you already know about. Alternatively, you can put sysouts there to see if this happens (this is clearly not the case).

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

+1
source

AFAIK, repaint () simply tells the system to recolor this component in the next drawing cycle, i.e. if you call repaint () in the paint () method, you can redraw in the next loop.

As a rule, Swing starts on its own thread, so repeated redrawing should not stop the application logic. However, it can use computing power if the system always repaints ui, even if there are no changes.

You can try to write a log message to find out when and how often the button will be drawn. If this happens continuously, even if nothing happens, which will lead to an ui upgrade, you may have to find a solution. If not, you should be fine.

Edit: There is a class called RepaintManager that may seem interesting to you.

0
source

Performing a redraw there is definitely a bad idea. Try adding b.setOpaque(false) to installUI() . Your button no longer draws all borders due to anti-aliasing. To fill in the blanks you need to skip the background.

0
source

Source: https://habr.com/ru/post/1342005/


All Articles