I have been under the impression that a Canvas or a JPanel were necessary to put graphics on a JFrame, however I saw a video earlier in which the person was using paint(graphics g)while extending JFrame and hadn't made a panel or a canvas. If this is a case, why do people bother with making a Canvas or a JPanel?
JFrame extends from Frame, which extends from Window, which extends from Container, extends from Component which defines paint.
If this is a case, why do people bother with making a Canvas or a JPanel?
To answer that question, you need to have a better understanding of JFrame (and window based classes).
Complicated
JFrame is actually a composite component, that is, it's made of a number of other components which provide the core functionality of the window
What this means is, if you override paint and perform custom painting on the frame, it's very possible that you will paint over the child components or the child components will paint over it, and because of the way the paint subsystem works, will do so any time without the frame's paint method been called.
Decorations
Frame's include the window decorations within their available area. This means that the "viewable" area is actually smaller then the defined area of the frame.
This is also why it's recommend to use pack instead of setSize
This means that if you override paint you could actually paint under the window decorations (yes, this happens all the time, and we're tired of answering it)
Screen shot from How can I set in the midst?
The contentPane of JFrame takes care of this, as it's laid out within the viewable area.
Double buffering
Top level containers, like JFrame aren't double buffered, so, even if you overcome all of the above, you will get updates which flicker. Sure you could "devise" you're own double buffering algorithm, but in Swing (ie JPanel), it's free, so why bother
Locked in
As a general recommendation, we generally discourage extending from JFrame (or other top level containers) as it locks you into a single use case and prevents the possibility of re-use.
On the other hand, if you use a JPanel, you can add it to what ever container you want, when ever you want
Related
When using a JFrame, I noticed some people spent their time doing
setSize(new Dimension(400,400));
rather than simply
setSize(400,400);
Are there any particular advantages to using the prior rather than the latter if not storing the object in a variable?
Also, can I not draw directly in JFrame? do I need a canvas for it? is it simply best to extend Canvas? no JComponent alternative to Canvas?
When using a JFrame, I noticed some people spent their time doing
setSize(new Dimension(400,400));
rather than simply
setSize(400,400);
Are there any particular advantages to using the prior rather than the
latter if not storing the object in a variable?
No. Basically it's just a connivance so you don't need to do something like...
Dimension dim = new Dimension(400, 400);
setSize(dim.width, dim.height);
You could just do
setSize(dim);
Having said that, you shouldn't rely on it, as pack will produce better results, if you've build you base components properly...
Also, can I not draw directly in JFrame? do I need a canvas for it?
Generally, no, you shouldn't paint directly to the frame, there are a number of important reasons why, to start with, JFrame (and other top level containers) are not double buffered, so repaints will flicker, also, the frame decorations are painted WITHIN the frame, so if you paint directly to the frame, you run the risk of painting under the decorations...
For example...
How to get the EXACT middle of a screen, even when re-sized
Graphics rendering in title bar
How can I set in the midst?
And just because I can't be bothered typing it again...
Why is overriding paint in a top-level container so bad?
is it simply best to extend Canvas? no JComponent alternative to Canvas?
This depends on your needs. Canvas is a heavy weight component, so adding it to a JFrame can cause problems. It is also not double buffered, so either you need to implement a BufferStrategy or implement your own double buffering...
And no, there is no Swing alternative to Canvas, but remember, Swing components are double buffered already and (at least since Java 6...I think), support hard ware acceleration through either Direct3D or OpenGL where available...
So the intial setup for this issue is that there's a JDialog, and inside that I've placed a JPanel that would house the rest of the components (since painting the JDialog itself is apparently a bad idea). This JPanel has an overriden paintComponent(Graphics g) method that only paints the background and adds a faint border for aesthetic purposes.
Now inside that is a series of JPanels that categorize the contained form components, and each JPanel has an overridden paintComponent(Graphics g) as well, painting a semi-transparent background.
Inside each of those JPanels is where I start to have some issues, presumably with transparency. I have JTextFields, JCheckBoxes, JLabels, JSliders, etc inside these panels, and when you interact with one (hover, click, etc), the background goes from transparent to opaque, with an occassional ghosted image from another field (which appears slightly random sometimes). I'm using a custom LAF called Web, but I tested with other built-in LAFs and the same thing happens.
Is this a glitch with Java or did I mess something up? If so, how can I patch this up? I can paste code fragments later if necessary, but I've used several custom classes and nine-patch style image stitching which may make the code fragments hard to follow. Thanks in advance!
If you are painting components with a transparent background it is very important that the component is marked as transparent (setOpaue(false)) so that the repaint manager knows that it must paint the components below it.
It is also very important that when you are performing custom painting that you call super.paintComponent first.
This is especially important in the case of transparent components, as this prepares the Graphics context for painting.
Graphics is a shared resource. All the components painted in your window will share the same Graphics object, meaning that if you don't allow paintComponent to first prepare it, then you will see what was previously painted on it.
Since JPanel and Canvas are both same-level components, the solution would probably be some sort of a 'hack'. This question says that you won't be able to add the lightweight component to the heavyweight canvas (I want JPanel transparent).
If this isn't posible, then would putting a transparent Component work? Also, is it feasible to add Swing components to the Component (it just has to work, even if it's bad). And how would I go about actually putting it over the canvas (since they are both same-level components)?
Note: I would never do something like this in a real app, I just need it in this case
One possiblity is to add the JPanel to whatever container the Canvas is on, then setting the color of the JPanel to have an alpha of 0. This should add it over the old one, without blocking out the Canvas. Is this what you want?
EDIT: Thinking about it, the JPanel's default color is transparent... You should just be able to add the JPanel the Canvas's parent, and lay it over it
I've just started coding video games and I've heard that doing all your drawing on a JPanel and attaching that panel onto a JFrame is better than simply drawing onto the JFrame. I was just wondering why is this better?
It is better for various reasons, including:
In most Swing components, custom painting is achieved by overriding the paintComponent(Graphics) method. Top-level Swing containers (e.g. JFrame, JApplet, JWindow) have only paint(Graphics). As a result of the common method for painting, people who answer often forget about this difference between common and top-level components, and therefore suggest silly advice. ;)
A JComponent can be added to a JFrame, or a JApplet, or a JDialog, or a constraint of a layout in a JPanel, or a JInternalFrame, or a.. Just when you think your GUI is complete, the client says "Hey, wouldn't it be great to throw in a tool-bar & offer it as an applet as well?" The JComponent approach adapts easily to that.
As explained (complete with code!) by Richante, it is easier to calculate the co-ordinates required for painting the custom component, and if it has a preferred size, to size the JFrame to be 'exactly the right size' to contain the GUI (using pack()). That is especially the case when other components are added as well.
Now, two minor disagreements with the advice offered.
If the entire component is custom painted, it might be better to simply put a BufferedImage inside an ImageIcon/JLabel combo. Here is an example of painting to an image. When it comes time to update it:
Call getGraphics() for a Graphics or createGraphics() for a Graphics2D
Draw the custom painting to that graphics instance
Dispose of the graphics instance
Call repaint() on the label.
It is an easy way to do Double Buffering.
Let me elaborate a bit. In video games, to avoid the flicker caused by redrawing and display smoother graphics, people usually use double buffering. They create an offscreen surface, draw everything on that and then display the entire off-screen surface on the screen in one go.
In Java2D and Swing, the easiest way to do this, is to simply do your game sprite drawing on a JPanel, and then add the JPanel to a JFrame.
Secondly, by drawing things on a JPanel, you allow more GUI widgets and other graphical objects to be displayed on the JFrame without having to paint them manually in the game loop. For example buttons, menus, other panels, even other rendering JPanels.
Thirdly, it allows you to use automatically translated co-ordinates for your game. You can draw everything from the top-left to the bottom-right without having to worry about the window manager specific things like window border-widths, task panes, titles etc.!
Moreover, this is not just a convention only used by Game Programmers in Java. Creating a separate Game Board, Render Panel or Graphics Widget is quite popular when programming games using a library with an internal mainloop such as a GUI Toolkit. You can use a User Form in Windows Forms and a Drawing Board in GTK+.
The JFrame includes things like the menu, title bar and border. Therefore, when you refer to coordinates you have to account for these. You might also decide to add a menu bar, or some other components, to the frame. If your painting is all in a JPanel, then this won't change how you need to refer to coordinates.
For example, try:
public class Test extends JFrame {
public Test() {
super();
setVisible(true);
setBounds(100, 100, 200, 100);
}
#Override
public void paint(Graphics g) {
g.fillRect(0, 0, 50, 50);
}
public static void main(String[] args) {
new Test();
}
}
and you will see that the black square is not square! Because (0, 0) is the top left corner of the entire frame, not the corner of the visible area.
I have been playing about with some simple painting of Graphics2D and have some extremely good help from the community here.
I managed to get the flickering resolved from my "bouncy balls" by moving the code away from the main JFrame class and into a JPanel which I then added to the JFrame class, can anyone tell me why this would make such a difference?
When you draw in a JComponent's paintComponent method (such as a JPanel's), you use Swing which uses double-buffering when drawing by default. Drawing directly in a JFrame's paint method will only allow AWT type drawing since the JFrame directly inherits from Frame, a heavy weight container, and since AWT graphics does not use double buffering by default and this will lead to choppy animation.