Override getPreferredSize and PaintComponent method, why? - java

just beginning to learn java and was playing around with Jframes, Jpanels and Graphics and was wondering why is it that we must override the getPreferredSize and PaintComponent method to make a graphics object appear in the Jpanel that has been added to a Jframe?

You don't "need" to, but it sure will help.
A base component (like JPanel and JComponent) has a preferredSize of 0x0, which, when used with some layout managers, makes the component virtually invisible (in fact, the painting sub system won't even paint a component which has a size of 0x0).
In everyday use, they calculate their preferredSize through the layout manager that is applied to them, which uses the child components to calculate the result.
So, in the absence of child components, you need to provide appropriate sizing hints, so when you use something like JFrame#pack, you don't end up with a "flat packed" window or sit around for a few hours wondering why you awesome component won't show up

Related

Add more than one panel on a frame on top of each other

I'm working on a project where I am displaying a square and a circle.
The circle moves on its own, but the user moves the square via the arrow keys. Whenever the circle touches the square, it rebounds.
Square and circle are different classes (2 different panels). I want to add those two to a frame, one on top of the other such that both are visible.
Can someone tell me how to do so?
JFrame n = new JFrame();
n.setTitle("Background Color for JFrame");
n.setSize(1000,600);
n.setLocationRelativeTo(null);
n.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
n.setResizable(false);
n.add(new Ball());
n.add(new Team());
n.setVisible(true);
User interface components at the same level in a hierarchy are assumed to be non-overlapping by default. You can explicitly work around this by making your components transparent with setOpaque(false), assuming you are taking care to only draw whats needed in the components, e.g. in case of a JPanel ensure that its background is not drawn. Its still somewhat random (implementation dependent) which component has precendence over another when doing this.
There is a component explicitly designed for that: JLayeredPane (https://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html), which manages "layers" in which components can be put, giving you full control which overlays which.
Games often implement this by themselves, since the full features of JComponent are not needed to represent a simple graphical element. In that case a single component is used as a "canvas" to paint self-defined objects onto making use of an override of paintComponent (See: https://docs.oracle.com/javase/tutorial/uiswing/painting/)
If you want to do this in swing, as it sounds, I would really recommend making a new class that extends JPanel, and override it's paintComponent method. In this method you use the Graphics in the argument to paint to the canvas. Then you can add this custom panel instead of two separate components to your JFrame, and handle the rendering in there. This render panel can then keep track of all objects that needs to be rendered, preferable implementing some interface (Drawable?) with a draw(Graphics g) method. That way you can make your classes that needs to be rendered implement your Drawable interface, keep then as a list in your render panel and just iterate through them in your paintComponent method and call draw on your Drawables.

Does using setOpaque(false) impact painting performance on Swing JComponents?

My question is whether the use of setOpaque(false), which I've come to use with JPanel to layout UI in Swing, impacts painting performance more than keeping everything opaque (where isOpaque() returns true).
I'm not very familiar with how Swing renders UI but I would guess that if a JComponent is not opaque it would be harder to render what's behind the component then to simply paint an opaque background on the component.
Yes it does add extra work when painting but I doubt you would have to worry about it.
Basically, whenever you repaint a component that is transparent you need to go up the chain to find a parent that is not opaque and then paint that component first, before painting the child component.
So the bottom line is don't worry about it. If you have a reason to use transparency then use it. If you don't have a reason then you shouldn't be using it.
You may want to check out Background With Transparency. It goes into a little more detail on what the opaque property means and how it affects painting and the problem you will have if you do use a transparent background.

Using swing components with an applets paint method

I have a functioning Java applet which can be embedded into a web page and wish to now add some swing components for additional functions. However whenever I add a component like a JLabel, it simply does not appear on the viewport/canvas unless I remove my entire paint method. The latter option allows me to add swing components but naturally I then cannot render any shapes. It appears to resemble an eXclusive OR (XOR), either one but not the other.
Is there anyway in a native Java applet to add swing components and still maintain the paint(Graphics g) method. Please note that I am inheriting from Applet and not JApplet.
If you override paint method in applet then there is no simple way.
What you could do instead of overriding paint in applet.
Extend JComponent instead and do the custom drawing there.
create JPanel that contains all the needed swing components including the component from earlier step.
Add that panel to applet which is using default paint method.
I suggest that you add the components to a separate panel that doesn't override paint. By overridding paint, you are customizing the way the component is drawn, so the layout manager and the components it has to manage do not count, it's only the implementation of paint you wrote that dictates how stuff is rendered.
So, your applet would contain a panel with the components and a panel that does the custom painting.
At the end of your paint method, call super.paint(g).
I was able to visualise the components over the applet canvas by invoking
paintComponents(g);
Thus the components are thereafter painted AFTER the actual paint(Graphics g) method completes its epoch.

Swing component default gradient background

Is there a way to change the default background painting of all instances of a swing component (a JPanel for example) to paint with a gradient background? Or would I need to create an extension of JPanel that paints with a gradient and then use that instead of JPanel everywhere in my app?
IMHO, it would be easier to just subclass the Swing component and override its paintComponent method to do the gradient painting. And then, as you said, use this custom component throughout the application.
It could be tricky using the UI properties since they may not be consistent across all LaFs.
It is not entirely clear what scope you intend. Did you mean by class (so all JPanel instances follow the new painting scheme), or do you mean all components in a Container (e.g. everything in Frame)?
There are possibilities to do this depending on component class, the places where you can hook into are the Look and Feel, and on a by component instance base, either the paintComponent() method, or if you need to replace the standard look of an existing component where you can not overwrite the method because you have no control over it, by providing your own UI class (look at Component.setUI) after the component has been created.
Except for the overwrite paintComponent approach neither is simple to implement. For most applications the simple approach is the best :)

Is there a way to only mark a specific JPanel region as opaque?

Basically I'm drawing a lot of transparent JPanels; profiling shows that most time is spent in Component.paint(). It would be possible to optimize this quite radically since in most cases the real non-opaque area per JPanel is quite small, for example around the edges.
As it stands now, repainting() a component would trigger a repaint of all its parents, since the RepaintManager can't know the dirty parent area is in fact opaque and will go up the component hierarchy. I was thinking about calling markCompletelyClean() on all parents whenever a panel is invalidated and managing the dirty regions myself with addDirtyRegion().
However, is there a cleaner approach of marking only specific rectangles of a JPanel as opaque (or transparent, doesn't matter)?
Even if you do something fancy with markCompletelyClean() and addDirtyRegion(), I doubt you'll get much of a performance benefit. When swing goes for a repaint, it collates all of the dirty regions to paint and starts a repaint with the minimum bounding box of all of the dirty regions. Thus if you mark the perimeter of a JPanel as dirty, then the bounding box of the JPanel is the same as the entire JPanel, which means you'll be repainting the entire thing any way.
Consider using JLabel (non-opaque by default) instead of JPanel, then you are able (to the JLabel) you can add any JComponent same as to the JPanel, but you have to set LayoutManager, then you forgot care about Opacity/Transparency and for particulars Region(s)
Why can't you define your own method myRepaint() calling multiple
public void repaint(long tm, int x, int y, int width, int height)
for all the borders?
Also try to play with clipBounds of your Graphics to repaint only really necessary parts. You can set any custom Shape as clip.

Categories

Resources