10

So in a recent answer, someone commented this (in regards to painting):

"This is probably some kind of illness of 90% of Swing Programmers: When they make their own component, they always extend JPanel instead of JComponent. Why?"

I'm still fairly new to programming, so I think it's too early to call myself a Swing programmer, as I have yet to find my niche. But overriding JPanel is just the way I was taught. So I set out to find the answer to the "Why?" question of the commenter. These are some of the answers I found.


Background painting is main difference. The JComponent class doesn't paint its background, so you have to paint the background in the overridden paintComponent method. In contrast, JPanel has an opaque background which can be painted by calling its paintComponennt method.


Instead of extending JComponent, some programmers prefer to extend the JPanel class. A JPanel is intended to be a container that can contain other components, but it is also possible to paint on it. There is just one difference. A panel is opaque, which means that it is responsible for painting all pixels within its bounds. The easiest way to achieve that is to paint the panel with the background color, by calling super.paintComponent in the paintComponent method of each panel subclass:


If the opaque property is set to true ... then the Swing painting system does not have to waste time trying to paint behind the component, hence improves performance.


I think the last quote really explains it best. But besides the opacity, are there other beneficial reasons "90% of Swing programmers have this illness" of extending JPanel rather than JComponent?

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • 2
    The `opaque` argument has nothing to do with this choice since it's a `JComponent`-declared method (and I assume you already know where in the class hierarchy is `JPanel`). – Marko Topolnik Dec 26 '13 at 16:25
  • 1
    @MarkOpolnik from what I've come to understand is that a `JPanel` is opaque whereas a `JComonent` isn't. – Paul Samsotha Dec 26 '13 at 16:28
  • If that was true, what business would the method [`setOpaque`](http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#setOpaque(boolean)) have in either of them? – Marko Topolnik Dec 26 '13 at 16:31
  • @peeskillet, that doesn't in fact make a valid reason for preference of overriding one over the other. They have different opaque property so use them according the opaqueness. One might be lazy like me for setting the LayoutManager as JPanel comes with a default layout setting, the `FlowLayout`, but that doesn't make anyone affect to be ill. – Sage Dec 26 '13 at 16:31
  • @Sage I _do_ know where you're getting at. But my question is, why does everyone choose to override JPanel rather than JComponent? Is it just habit or is there some other defining factor that I'm unaware of? – Paul Samsotha Dec 26 '13 at 16:35
  • @MarkoTopolnik if opacity _"has nothing to do with this choice"_ then why _is_ `JPanel` most often the one being subclassed? That's my question. I'm trying to get down to the bottom of why I'm doing what I've been doing. – Paul Samsotha Dec 26 '13 at 16:40
  • 5
    @peeskillet, One other reason would be the UI delegation: Panel will be decorated by the appropriate look and feel UI delegate. There are default settings available for Panel in UIManger's default setting to provide some default design which can live through your application life cycle. On the contrary UIManger doesn't have such for `JComponent` – Sage Dec 26 '13 at 16:43
  • FWIW, having the background painted for you may be conducive to performance issues. And from what I can see, `JPanel` participates in the Pluggable Look and Feel mechanism, which is usually not applicable to custom components. – Marko Topolnik Dec 26 '13 at 16:43
  • 2
    I was also going to mention the main difference is the UI delegate, which is responsible for painting the background of the panel. Even if you use component.setOpaque(true) and component.setBackground(Color.RED), the background will not be painted because there is no UI delegate to do the painting. If you want your component to respect the background/opaque properties, then you must add code to the paintComponent() method to do the painting. – camickr Dec 26 '13 at 16:47
  • @camickr But painting your background is a *privilege* to have because it wastes performance to paint the complete rectangle, only to paint the actual content over it. You can paint only the parts which will remain visible in the end. – Marko Topolnik Dec 26 '13 at 16:48
  • @camickr that _does_ make those quotes above more clear. – Paul Samsotha Dec 26 '13 at 16:48
  • 2
    @MarkoTopolnik, all Swing components are responsible for painting the entire background. That is what the opaque property means. If you don't paint the background you can be left with painting artifacts. For example see [Backgrounds With Transparency](http://tips4java.wordpress.com/2009/05/31/backgrounds-with-transparency/) which tries to explain how the opaque property works for a component. – camickr Dec 26 '13 at 16:57
  • @camickr You meant "all *opaque* Swing components" because that is what opaque means---a non-opaque component (quoting your source) "makes no guarantees about painting all the pixels within its rectangular bounds". – Marko Topolnik Dec 26 '13 at 17:08
  • @camickr Anyway, that's slightly beside the point. My point is that you want *your* code in your *opaque* component to be in charge of background painting instead of the complete background being wastefully painted in advance, whether or not it will be visible. – Marko Topolnik Dec 26 '13 at 17:09
  • @MarkoTopolnik, Swing components use the fillRect(...) method to paint the entire background. Then the custom painting code for the component is invoked to do the painting on top of the background. Even if you are painting an image over the entire area of the component you should still paint the entire background because the image may use transparency which has the potential to cause problems. – camickr Dec 26 '13 at 17:35
  • @camickr Clearly, that is not a general argument because 1) who said I want to paint an image into my custom component and 2) even if I did, I could know for a fact that the image does not have any transparent pixels. There is *no need* for the blanket `fillRect` method call in the general case. – Marko Topolnik Dec 26 '13 at 17:49
  • @MarkoTopolnik, there is no need for premature optimization. Every Swing component does a general painting of the background first. Invoking a single fillRect(...) to paint a background is not an issue in the general case. It is not a waste and will not affect performance and will make sure you don't have painting artifacts. – camickr Dec 26 '13 at 19:16
  • @camickr In the general case, it *may* be an issue and in particular, it *was* an issue for me in a component which presented a live histogram at 60 FPS. Painting artifacts are had by those who do not quite know what their code is doing, but I for one don't like my design to be advised by the inadequacies of the common denominator of Swing programmer. – Marko Topolnik Dec 26 '13 at 20:27
  • @MarkoTopolnik, you have a very specialized highly animated example that you are talking about. In the "general case", think all the existing Swing components, it is not an issue and should not be treated as an issue until you have a problem. Also, think of the majority of question asked in the forum, you are more likely to have a problem when you don't paint the background first as oppose to having a performance problem. – camickr Dec 26 '13 at 20:37
  • @camickr I think we may actually agree here: you do acknowledge that it would be wrong design if the `paintComponent` method gave you no option but to have your background pre-painted. Of course it is OK to prepaint whenever it doesn't cause performance issues or flickering, but it would *not* be OK if prepainting was outside of your code's control---which is all I am arguing for the whole time. – Marko Topolnik Dec 26 '13 at 20:41
  • This [answer](http://stackoverflow.com/a/11338636/230513) illustrates an unusual `JPanel` UI delegate in an older version of Mac OS X. – trashgod Dec 27 '13 at 02:53

2 Answers2

5

Difference in opacity handling is not the only factor.

Looking at JPanel source code helps because it is only ~100 lines.

All constructors eventually call this constructor. Opacity and double buffering default to true. The default LayoutManager is FlowLayout which you may or may not want.

public JPanel(LayoutManager layout, boolean isDoubleBuffered) {
        setLayout(layout);
        setDoubleBuffered(isDoubleBuffered);
        setUIProperty("opaque", Boolean.TRUE);
        updateUI();
}

Loy et al in O'Reilly's Java Swing 2nd edition recommend extending JComponent for truly custom components (p.1333) but also mention the need to consider a UI delegate. JPanel handles it's own concrete AccessibleContext whereas a class extending JComponent returns null.

For a read-only visual component I usually extend JComponent but I'd probably think twice for an interactive component because of the extra considerations for accessibility.

Cheers,

reggoodwin
  • 1,514
  • 13
  • 14
4

This is proper mental. If you check the source of JPanel it doesn't touch opaque. However, it happens that most versions of most PL&Fs do set the opaque property. They could set other properties randomly.

Early versions of the GTK PL&F didn't set the opaqueness for JPanel. It was changed, apparently for performance though perhaps shoddy Swing programmers using JPanel inappropriately may have been a factor.

There are very few valid reasons for subclassing JPanel. Don't do it.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • 2
    +1 but is the argument then that `JComponent` is more general and the proper target for subclassing, whereas `JPanel` is too specific and subclassing it would just be a hack? Do you concur that it doesn't usually make sense for your custom component to participate in the PLAF framework? – Marko Topolnik Dec 26 '13 at 17:54