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.
Related
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
So I have a JPanel called displayPane and I want to make it a drawing area for a graph (I am making a graphing calculator). I am using WindowBuilder and this is the generated code for the JPanel:
JPanel displayPane = new JPanel();
displayPane.setBackground(Color.WHITE);
displayPane.setBounds(173, 33, 455, 432);
frame.getContentPane().add(displayPane);
After that I want to draw the axis of the graph but I have no idea how.
I've searched everywhere about it but everyone constructs a member class or something in the main class and adds the paintComponent(Graphics g) but that confuses me. What is that trying to accomplish ? Or just give me your way of doing it I don't really care as long as I understand it.
Any help is appreciated :)
Since this is homework, I'm going to give you general guidance without code, but first and foremost, please read this link on performing custom painting with Swing. Next you should put the Window Builder software to the side and work on creating your own code from scratch, at least do this til you're comfortable coding with Swing.
Next suggestions:
Have your DrawingPanel extend JPanel
Override paintComponent(Graphics g)
Call the super's method in your override, super.paintComponent(g) as this will refresh the graphics and erase old "dirty" pixels.
Play with drawing lines using g.drawLine(...)`
keep doing this and you'll get the idea of what you'll need.
Custom painting is achieved by overriding the paintComponent method of a JComponent based class (like JPanel).
This gives you access to the drawing surface onto which content is drawing and eventually shown on screen
See Custom painting and Painting in AWT and Swing for more details.
The Graphics API (or more specifically, the Graphics2D API) is a power abstract toolkit which provides with the means to actually paint stuff to the screen.
At the basic level, this provides you with the ability to specify colors and draw basic shapes and text. At a more complex level, you can define you own shapes, perform more complex coloring effects, including gradient fills and transformations of the basic context
See the 2D Graphics Trail for more details.
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...
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'm creating a simple 2D game in java. I've only done this in C++ with the Windows API so far. In java, I'm getting a Graphics object by extending JFrame and calling this.getContentPane().getGraphics(). With this object, I'm drawing everything to the screen, every frame. This raises a few questions:
Backbuffer: I'm not getting any flickering effects, while I'm not using a backbuffer (I'm drawing directly on the Graphics object). Why is this? Does java has a built-in backbuffer or something?
Animations: I'm used to put all animation parts in a single sprite sheet, like in this example:
http://www.envygames.com/share/sample_animation.jpg
Now, someone has told me that you can just draw animated .gif's and java will draw these independent of the game loop. I've tried this out and it doesn't seem to work. Is this true or am I also supposed to use these sprite sheets in java?
Thanks
Yes, Java has a double buffer rendering strategy that can be switched on and off...
Java: how to do double-buffering in Swing?
About the animated gifs, I think it is right, but you may have to put them in the appropriate container (maybe as the icon of a JLabel?).
getting a Graphics object by extending JFrame and
calling this.getContentPane().getGraphics().
don't painting directly to the JFrame for Custom Painting you have to look for JLabel that allows painting everything, another choise will be extending JCompoments, or JPanel for that
for painting in the Swing you have to look for paintComponent(Graphics g), not paint(Graphics g), because this method are lots of time used in examples and ditributed on some of Java ExamplesDepots, that's wrong method with possible lacks