user2938543
user2938543

Reputation: 327

Multiple 2D Graphics Drawing Functions in Java

The program I am working on includes a class named GameForm that extends JFrame. This form is going to contain a map (just a series of rectangles), as well as certain objects on the map.

However, I would not be able to draw all of these objects with a single paintComponent(Graphics g) function, since not all objects in the game always have to be drawn at the same time. For example, the drawMap() function would only be called when the form first loads, whereas all other drawing functions would be called after each turn.

However, from what I have read (and please correct me if I am wrong), only one paintComponent function is allowed in the class, and other functions cannot make use of its Graphics2D object.

Are there any ideas as to how this can be implemented?

Upvotes: 1

Views: 923

Answers (2)

user719662
user719662

Reputation:

Generally speaking, what you wish to achieve can be done in a couple of ways. It's strictly related to so-called sprites (http://en.wikipedia.org/wiki/Sprite_%28computer_graphics%29) and image buffering (http://en.wikipedia.org/wiki/Multiple_buffering). Simplest approaches would be:

a) in paintComponent() of a JPanel added to your JFrame generate the resulting image by processing all the input data/user events/machine state,

b) you can prepare & store the overlay as e.g. BufferedImage, updating it as needed, and then paint it over your JFrame during a single call - the state of JFrame will be updated only on paint events (paint(), paintComponents() etc, so you must force invalidation by hand if the map changes without direct JFrame interaction (resizing the window, covering it with other frames etc), e.g. by calling repaint() etc.

c) you can get the drawing context by calling getGraphics() (http://docs.oracle.com/javase/7/docs/api/javax/swing/JFrame.html#getGraphics%28%29), and then using the returned object (probably casted to Graphics2D) as your canvas whenever the need arises. Note that this is actually the worst solution in terms of efficiency.

They ain't the only ones possible - I, for once, use OpenGL/JOGL for most of my 2D rendering needs, since it allows insane rendering speed with all the profits of 3D graphics [interpolation, scaling, rotations, alpha-blending, perspective, geometry morphing, shading etc] with only minimal functional overhead.

Also, note it is usually advisable to draw on a dedicated canvas component (e.g. JPanel) instead of global JFrame - it's connected to so-called lightweight vs heavyweight component difference and other OOP/Swing/AWT/EDT concerns; it also allows to hide the map and reuse the JFrame for something else with one simple JPanel#setVisible(false) call.

See java what is heavier: Canvas or paintComponent()? for more information.

Upvotes: 0

J4v4
J4v4

Reputation: 810

People who are new to Swing / GUI programming often imagine JFrames to be like a draw surface or paper. However, you will have to get used to the fact that this is not the case.

First of all, a GUI program has some kind of EDT (Event Dispatch Thread). This thread is where all the GUI action happens. Updating the GUI and responding to user input happens here. This is necessary because user interaction and programmatic changes to the GUI need to be synchronized well.

Back to the topic, a JFrame is basically just a rectangle that is registered to the System to be your "draw surface". Rather than just painting on top of it, you are asked to paint it.

That's what paintComponent(Graphics) is good for. Obviously, you don't want to paint all the time. It just works like:

  1. user opens your window
  2. system tells your app: "hey, you wanted this surface, please paint it"
  3. the Graphics from paintComponent() is used to repaint your frame (quickly)
  4. your application remains inactive until the user makes the next input

If you want to animate your frame, you have to work like this:

  1. tell the system: "hey, I'd like to repaint my surface" (calling repaint())
  2. system calls paintComponent() and you repaint your stuff
  3. the next call must be delayed
  4. start over, paint the next image

Note that the delay is important because all of this happens on the holy EDT. The EDT handles everything and needs to "breathe" so the user can do stuff while you're doing your animation.

All in all, what you've learned is:

  1. Save all the state you need for painting in variables.
  2. When paintComponent() is called, draw onto the surface
  3. If you want to animate, call repaint() -> paintComponent() will be called
  4. never block the EDT

Last thing to consider: don't use JFrame to paint directly to it.
Rather than that, add a JPanel to the frame and override its paintComponent() method.

Upvotes: 1

Related Questions