I have trouble understanding a fundamental concept in Java 2D.
To give a specific example:
One can customize a swing component via implementing it's own version of the method paintComponent(Graphics g)
Graphics is available to the body of the method.
Question:
What is exactly this Graphics object, I mean how it is related to the object that has the method paintComponent? Ok, I understand that you can do something like:
g.setColor(Color.GRAY);
g.fillOval(0, 0, getWidth(), getHeight());
To get a gray oval painted. What I can not understand is how is the Graphics object related to the component and the canvas. How is this drawing actually done?
Another example:
public class MyComponent extends JComponent {
protected void paintComponent(Graphics g) {
System.out.println("Width:"+getWidth()+", Height:"+getHeight());
}
public static void main(String args[]) {
JFrame f = new JFrame("Some frame");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(200, 90);
MyComponent component = new MyComponent ();
f.add(component);
f.setVisible(true);
}
}
This prints
Width:184, Height:52
What does this size mean? I have not added anything to the frame of size(200,90).
UPDATE:
I understand that I must override paint to give in the Graphics g object the hints required to do the repaint and that I do not have to create a Graphics object as one will be given by platform.
What happens after that is what I can not understand.
E.g. does Graphics represent the screen and the object is painted accordingly on screen as soon as I start calling the various g.setXXX methods?
Does it get stored in a queue and there is a 1-1 association among g and each component? So the framework uses each g of each component to paint it one at a time?
How does this work?
Any help on this is highly welcome
Thanks
I understand your problem as I struggled with it for some time when I was learning Java graphics. It's not just Java 2D graphics - it is part of AWT.
When you create a JFrame or some other top-level object, it does a lot of work "behind the scenes" - part of which is creating a Graphics object. (There is not explicit notification of this, though if you stepped through the code with a debugger you may see classes which create the Graphics).
You then create components which you add, or register with, the top-level object. These all have to implement a call-back method, including
paint(Graphics g);
You will then #Override these methods so that when the component is rendered it uses YOUR paint method.
Do not try to save the Graphic or create a new one. Think of it as the framework taking the responsibility off you.
The size of components is often taken out of your hands. If you use a layout manager then it may decide to resize your component.
If you are coming from a procedural imperative background you may well have problems (I came from FORTRAN). My advice would be to try a number of tutorials and - at some stage - enlightenment will start to come.
The general documentation for Java graphics is poor. There are many concepts which are opaque (see How does Java Graphics.drawImage() work and what is the role of ImageObserver ). The early implementation was rushed through and had many bugs. Even now it is often unclear whether and in what order you should call methods such as setPack() and setVisible().
This doesn't mean you shouldn't use it! Just that the learning curve is a bit longer than IMO it should be.
MORE:
Also YOU don't decide when something is painted, the framework does. paint(g) really means "The framweork is repainting its components. What to you want this component to provide at this stage".
Maybe providePaintingInstructionsWhenRequiredForComponentGraphics(Graphics g) would be a useful name.
Similarly repaint() does not repaint at your orders, but when the system thinks it should. I haven't found it useful.
If you (say) resize a component interactively every slight change will normally trigger a paint(g). try putting a LOG.debug() in the paint code and seeing when it gets called.
What does this size mean? I have not added anything to the frame of size(200,90).
You added your component to the frame and set the size of the frame to be (200, 90). The default layout manager for the content pane of the frame is the BorderLayout, which means the component you added gets all the available space. The frame needs some space for the title bar and borders, so your component gets the remaining space.
The component does not create a static Graphics object association.
The graphics object is the wrapper for a platform handle giving access to a physical device, like the screen. It's valid for the time when "paint" is executed only, you can't store it and reuse it later. It is a resource managed by the "toolkit".
The component itself is an abstraction on top of the windowing system, that gets asociated shortly with this device for getting rendered.
EDIT
You can force such an association calling "getGraphics" if you feel the need to paint out of the "paint" callback. This should be a very rare case and you ALWAYS should dispose the Graphics afterwards.
Think of a Graphics like a piece of paper which you draw on to show how the Component looks like at that moment. After you've drawn it, the framework toolkit will trim off the edges and show what you've drawn to display the component. Moreover, the next time you draw the component, you'll be drawing on a different piece of paper, so don't keep the old Graphics around.
Related
I am creating a java 2D game and have come to a point where I am thinking of creating "pop-up graphics" i.e graphics that will populate the screen when a certain event occurs. Say, you pick up a certain item, I want this item to be displayed in the middle of the screen in a box containing information about said item.
Currently I have one big paintComponent that paints all of the graphics for the game (tiles, entities, players etc (which has been done efficiently I might add)). I know that I can probably have a boolean value which checks if an item has been picked up in that method, but it feels wrong.
What I am wondering is, is there a way for items to have their own paintComponent so that when it is called, it will show say a bo for a brief period WITHOUT having a boolean value in the big paintComponent method that I currently use for everything?
Small code example (won't execute)
public class popUpGraphics extends (JComponent or JPanel or whatever works best for this scenario)
{
public popUpGraphics(){
}
#Override
protected void paintComponent(Graphics g){
//g.Draw(stuff);
}
}
and then somewhere in an event I instantiate this or somesuch.
I do not want this to override the other paintcomponent, I just want to add to it
(as if it was another layer) to the paintComponent
In short I want to know:
1. Is is possible to have brief graphics shown without including in the huge paintComponent method
2. What Swing library should be extended (JComponent, JPanel etc)
Thanks in advance!
i' m little bit confused about few things:
Example code,that shows my problem,this isn't compilable
// image
private BufferedImage image;
private Graphics2D graphic;
private changeImage;
. . .
//thread loop
while (running) {
. . .
render();
Graphics showGraphic = getGraphics();
showGraphic.drawImage(image, 0, 0, null);
showGraphic.dispose();
}
public void render(){
if(changeImage == 1)
graphic.drawImage(ImageLoader.TREE, 0, 0, null);
else if(changeImage == 2){
graphic.drawImage(ImageLoader.HOUSE, 0, 0, null);
. . .
graphic.fillRect(50,60,30,40);
}
}
I create an global object Graphic2D and i draw things in render(), I do not call repaint() inside the game loop, is it good practice to do this?
Should I use repaint() inside my loop , and the paintComponent() function ?
one other thing, how graphic.dispose() works correctly? , because trying to remove this line of code, nothing happens.
I understand how works dispose() according to java docs, but I have not noticed any differences with dispose() or without.
my program runs very well, but i have this dubts.
is it good practice to do this
No, this is actually incredibly horrible and error prone. This assumes that this is the Component#getGraphics method. The problem is Swing uses a passive rendering algorithm, that is, Swing decides when and what should be repainted and does this for optimisation reasons.
This means updates are not regular, which really helps when doing animation, and can happen at any time for any number reasons, many of which you simply don't and can't control.
For these reasons, painting should be done within one of the paint methods, preferably, paintComponent of a JComponent based class. This way, when Swing decides to do a repaint, one, you know about it and can update the output accordingly, and two, it won't clear what you have previously painted to it using getGraphics which could cause flickering...
You are also running into potential threading issues, as you thread is trying to paint to the Graphics context, so might the Event Dispatching Thread and that isn't going to end pretty...All painting should be done from within the context of the EDT (for component based painting)
You could try using a BufferedStrategy, which would give you direct control over the painting process, but this limits you to the AWT library. Don't know if this is good or bad in your case.
one other thing, how graphic.dispose() works correctly? , because
trying to remove this line of code, nothing happens.
dispose basically releases any native resources that the Graphics context might be holding. General rule of thumb, if you didn't create, you don't dispose of it.
So if you use Graphics#create or BufferedImage#createGraphics to obtain a Graphics context, then you should call dispose when your done with it.
On some systems calling dispose on the Grpahics context used to perform painting of components (such as that passed to paintComponent or obtained from getGraphcis) can prevent further content from been painted.
Equally, not disposing of Graphics contexts that you have created can lead to memory leaks as they won't get garbage collected...
There are many questions about "good" or "correct" painting in Swing, so this question could be considered as a duplicate, and I won't try to repeat here what should be read in context, for example, in the Lesson about Performing Custom Painting.
However, to summarize the most important information short and concisely:
You should never call getGraphics on a Component. It may return null or a Graphics object that is in any other way "invalid". It will fail in the one form or the other, sooner or later
You should do all your painting operations in the paintComponent method (or in methods that are called from paintComponent), using the Graphics object that you receive there as an argument
Whether or not you call the Graphics#dispose method may not make a difference that is "immediately" visible. You should not call it on the one that you received in the paintComponent. You should only call it on a Graphics object that you either obtained from a BufferedImage, or one that you created by calling Graphics#create(). When you do not call it, this might cause a memory leak that will only be noticed after the application has been running for a while.
Right now I have a main game loop that constantly redraws the screen. Since I need to slow this thread down but continue to draw other items at a faster rate I need to make a new thread. The problem is I am not sure how to go about making a new thread that also draws to the screen I know how to make a new thread, I am just stuck on how to implement the Graphics2D drawing in the new thread. For example I have the code below which is the typical starting point and then there is the draw method defined in the other class that directs what and when to draw. If I wanted to branch off and have another thread drawing and doing its own thing how do I do that?
Do I have to make a new class that creates an entirely new PaintComponent()? Or would I simply create a new Graphics2D object so I can use different font colors and such? I guess what confuses me most is that I can't just call a different draw method because I still need to pass g2d as the argument. So it appears to me that I need to make the thread from within another method that already has the g2d object.
If this is confusing I do apologize as I am still a beginner to JAVA. If you need more information just let me know. Thank you in advance.
public abstract void draw(Graphics2D g2d);
#Override
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
super.paintComponent(g2d);
draw(g2d);
}
In the first place, Swing is inherently single-threaded. This was once summarized in the "single thread rule"
Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.
(Unfortunately, the respective site did not survive the migration of Java from Sun to Oracle, but some information can be found here http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html or when doing a websearch for "swing single thread rule")
In general, this applies also to painting : The paintComponent may only safely be called by the Event Dispatch Thread (EDT). And it will be called "automatically". That is why this technique is called "passive rendering": You overwrite the paintComponent method, expecting that it will be called by the EDT.
However, particularly for game development, you can use a technique called "active rendering". In this case, painting is slightly more complicated and involves setting up an own BufferStrategy. The potential advantage is that any thread may perform rendering operations in this case, because you can obtain a Graphics object by calling BufferStrategy#getDrawGraphics.
Information can be found at http://docs.oracle.com/javase/tutorial/extra/fullscreen/rendering.html (while this refers to fullscreen rendering, similar concepts can be applied to active rendering in a window, but I'd recommend to consult further tutorials/resources that can be found with keywords like "swing active rendering").
I am new to java and programming in general,and i am trying to write a little shooting game with a spaceship and aliens, but having a lot of trouble with the graphics. It seems that I am mixing a lot of different kind of components.
How should I do it right?
- Should I use Swing JFrame and then add it a Graphics object?
- Should I make a panel first and add the graphics on to it instead?
- Or maybe should I use a canvas instead of a JPanel?
There are a lot of options, and searching the net for answers makes me very confused. Some advise to use the paint() method while others demonstrates code while using the paintComponent()...
What is the preferred way in the right order for the graphics to be laid and what classes should I use?
For serious gaming, don't use Swing but rather other more game-specific GUI libraries such as the LWJGL. For simple Games, Swing is OK, but be sure to read the graphics tutorials first as your assumptions on how to draw may need to be changed (I know mine were). For instance you would not use a Graphics class field but would usually draw passively in a JComponent's paintComponent method.
For simple games, Swing is fine and you can rely on the followings:
Use Swing and forget about AWT. You will get far better results with a lot less code.
Use Swing Timer to pace your game
For each of your components, extends JComponent or JPanel and override paintComponent. In paintComponent perform only drawing operations (do not update your model or modify the state of your component).
Whenever when you want to draw something, modify your components state and then invoke repaint()
To handle layers, you can always use JLayeredPane
I can see the sense of using a game API as suggested by #Hovercraft, but feel that many simple games can do without it.
I agree with the first 2 points of #Guillaume, but would tend to go in a different direction after that.
Let us assume the game:
Is a fixed size (i.e. non-resizable).
Has no components appearing over it.
In that case, I would tend to do the rendering in a BufferedImage that is displayed in a label.
Answers to specific questions
Should I use Swing JFrame and than add it a Graphics object?
That would not compile. So ..no.
Should I make a panel first and add the graphics on to it instead?
That is basically what Guillaume is suggesting, though I prefer using an image as the canvas on which to paint.
Or maybe should I use a canvas instead of a JPanel?
If by 'canvas' you mean java.awt.Canvas then no.
If you mean a java.awt.image.BufferedImage then yes.
Some advise to use the paint() method while others demonstrates code while using the paintComponent()...
This is a common confusion since there is so much old and bad code out there in the World Wild Web.
The only components that overriding paint(Graphics) would work with are components of the AWT & Swing top-level containers such as JFrame, JApplet or JWindow.
AWT is last millennium's GUI component toolkit. We should use Swing this millennium.
It is unusual for an entire GUI to be custom painted. Instead it might be added as the major component amongst others that report player health, lives remaining, score etc. For that (and other) reasons, it is better to render to a 'non top-level container' which would leave us looking at doing custom painting in a JComponent or JPanel.
For custom painting in an extended panel or component, override paintComponent(Graphics). That is the correct way to do custom painting for those components.
Other tips
Each object of the game (e.g. Ship, Enemy, Missile) should know how to draw itself. Keep a reference to each of the game elements in the painting routine, and simply call gameElement.paint(Graphics) (or Graphics2D) on the instance of graphics it is painting.
If the game elements are drawn from Java-2D based Shape instances, or if a Shape can be defined from the existing sprite images, collision detection becomes simple. For details see:
This answer to Get mouse detection with a dynamic shape
This answer to Collision detection with complex shapes
I had to implement a lost-vikings like game for a course project and not having a single idea how to do it, had the exact same questions in mind.
I ended up writing methods that are responsible for drawing different elements(i.e., one to draw enemies, one for missiles, etc...), and then called them in paint() method. I called repaint() method in my main loop to redraw everything.
I am not sure if this is a good practice, but it worked. You can check the code here, though, I must warn you that it is a "damn, i must get it done" project. You should specifically have a look at GameFrame.java.
I got two Graphics objects, is it possible to combine them into one?
The answer of Hovercraft Full of Eels is useful, but not what I meant. Say I have two Graphics objects, private Graphics gr1 and private Graphics gr2. Now, how should I merge them in the paintComponent(Graphics) method to draw them to my (for example JPanel)?
The question is not at all about images, but about mere Graphics objects. Sorry, the first paragraph was misleading.
Edit:
In response to Hovercraft Full of Eels:
If you draw in the paintComponent() method, I find it annoying everything is gone if you redraw your screen, and if you want to move something you have to save the coordinates and dimensions of everything, adjust those and get them someway in your Graphics object.
The question I asked myself was accely: What object is best suited or saving a Graphics object. A Graphics object, I thought. but the problem is that if you have (for example) two rectangles, and one moves to the left and one moves to the right you can't if you have 1 Graphics object.
My solution was multiple graphics objects, which can be transformed to simulate movement and then drawn to the screen.
#Hovercraft Full of Eels I think you (and most SO'ers) think this is not a good solution, looking at your answer.
But, an answer saying You're doing it all wrong, you'd better stop programming doesn't help me at all, so please give me an alternative.
You can draw any image into a BufferedImage by getting its Graphics context, drawing in it, and then disposing of the Graphics object. For example
// get a Graphics object from BufferedImage, img1
Graphics g = img1.getGraphics();
// use that g to draw another BufferedImage starting at x, y of 0, 0
g.drawImage(img2, 0, 0, null);
// clean up resources so as not to run out.
g.dispose();
Now img1 shows old img1 with img2 overlying it. If you want the images to be transparent, you'll need to read up on alpha composite and how to use it
Edit
Regarding your edited question:
I got two Graphics objects, is it possible to combine them into one?
The answer of Hovercraft Full of Eels is useful, but not what I meant. Say I have two Graphics objects, private Graphics gr1 and private Graphics gr2. Now, how should I merge them in the paintComponent(Graphics) method to draw it to my (for example JPanel)?
The question is not at all about images, but about mere Graphics objects. Sorry, the first paragraph was misleading.
This request is somewhat confusing to me since I think of Graphics objects as pens or paintbrushes that are used to draw something to an image or the screen. So I don't think that you draw a Graphics object to anything, but rather that you use a Graphics object as a tool to draw to something else that can display or store the graphics. You are going to have to put a lot more effort into asking your question, supplying us with enough details so that we don't have to keep guessing what it is you are trying to do. Please tell us the whole story.
Edit 2
In response to your latest edit to your question:
If you draw in the paintComponent() method, I find it annoying everything is gone if you redraw your screen, and if you want to move something you have to save the coordinates and dimensions of everything, adjust those and get them someway in your Graphics object.
The question I asked myself was accely: What object is best suited or saving a Graphics object. A Graphics object, I thought. but the problem is that if you have (for example) two rectangles, and one moves to the left and one moves to the right you can't if you have 1 Graphics object.
No, a better solution I think is to use a BufferedImage to save that which should persist. When ever you wanted to add an image to the background BufferedImage, you'd obtain the BufferedImage's Graphics object by calling getGraphics() on it, then you'd draw your image to it by using the Graphics object's drawImage(...) method, then you'd call dispose() on the BufferedImage's Graphics object so as not to lose resources.
To draw the background BufferedImage in the background, you'd call it at the top of the paintComponent(...) method, usually just after calling the super.paintComponent(...) method.
My solution was multiple graphics objects, which can be transformed to simulate movement and then drawn to the screen.
#Hovercraft Full of Eels I think you (and most SO'ers) think this is not a good solution, looking at your answer.
Yep, you guess correctly -- I believe that there are better solutions than the one you suggest.
An example of just what I'm getting at can be found in my code post in my answer to this question: changing-jpanel-graphics-g-color-drawing-line.
If you run my code, you'd see this:
To impose images, use an appropriate layout, as shown here.
To compose images, use an appropriate composite rule, as shown here.
Addendum: #HFOE is correct. An instance of Graphics or Graphics2D, sometimes called a graphics context, is a transitory means to apply rendering methods; the target device or off-screen image must exist independently. Classes implementing the Shape interface are good candidates for encapsulating information about what to render in a given context. AffineTest and GraphPanel are examples.