0

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

Cratylus
  • 52,998
  • 69
  • 209
  • 339

4 Answers4

2

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.

Community
  • 1
  • 1
peter.murray.rust
  • 37,407
  • 44
  • 153
  • 217
  • @peter.murray.rust:When you say `notification` you mean explanation of what Graphics actually is? Yes the part about overriding `paint` I get. I just do not understand what is the relation of `g` passed as argument with the object that override's paint and how exactly `g` does its job – Cratylus Jan 02 '11 at 10:49
  • @user384706 no, I mean it creates one without explicitly telling you it has done it or requiring any input from you. – peter.murray.rust Jan 02 '11 at 10:52
  • @peter.murray.rust:I understand that `g` is already set up to be used.Does the `g` represent the screen? Is it a new `g` passed to each component? Also concerning size, in the post I use the `getWidth` and `getHeight` inherited from JComponent. Why is there a size? JComponent has somekind of default size? – Cratylus Jan 02 '11 at 10:55
  • Each component has its Graphics (if appropriate). The default size of a component is (I think) 0,0 or the sum of the size of the components that are in it. I find difficulty in using setMinimumSize(), etc. this doesn't actually set the onscreen size (i think) but allows the renderer to obey it when it needs to. – peter.murray.rust Jan 02 '11 at 10:57
  • @peter.murray.rust: So how should I interpret the `Width:184, Height:52`? – Cratylus Jan 02 '11 at 11:18
  • @peter.murray.rust:I have made an update to my question. Thank you very much – Cratylus Jan 02 '11 at 11:23
  • That should be the actual size rendered on the screen. Suggest you add something like `g.drawRect(0,0,getWidth(),getHeight())` to your paint method and re-run, changing the size interactively – peter.murray.rust Jan 02 '11 at 11:23
  • @peter.murray.rust:Actual size rendered on screen of what?My extended JComponent has nothing. If I do `g.drawRect(0,0,getWidth(),getHeight())` a rect will be drawn ok. But still same problem with the inherited methods of width and height – Cratylus Jan 02 '11 at 11:26
  • Actual size of component. getWidth() is a method of JComponent. Think of Graphics as an entity which carries out set of drawing and other instructions provided by the components. – peter.murray.rust Jan 02 '11 at 11:32
2

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.

camickr
  • 321,443
  • 19
  • 166
  • 288
1

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.

mtraut
  • 4,720
  • 3
  • 24
  • 33
  • So when `paintComponent` is eventually called, when the various methods of `Graphics` object are called they are performed directly on the screen? – Cratylus Jan 02 '11 at 11:56
  • They are performed on the device behind (see Component.print, calling paint, which should use a printer device). This more or less maps directly on the graphics primitives of the platform. – mtraut Jan 02 '11 at 11:59
  • Still more precise: it can be an offline device (Bitmap handle), too - as it is in case of double buffering. – mtraut Jan 02 '11 at 19:57
1

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.

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • @Donal Fellows:If the framework uses the `Graphics` to show what I've drawn to display the component, how come it is adviced in books to: create a temporary Graphics object via `Graphics2D g2d = (Graphics2D)g.create();` inside the `paintComponent` method, and use the temp g2d for the all the graphical operations and in the end of the `paintComponent` method call `g2d.dispose()`?The framework toolkit will never get the operations I set in the `g2d` (it has been disposed) unless what I do on `g2d`, is directly done on screen. This part is a headache to me – Cratylus Jan 02 '11 at 12:24
  • That inner `Graphics` is delegating appropriate parts to the one that created it, and that's where the analogy breaks down a bit. It's not a piece of paper, but rather an abstract surface with a clip region applied to it and which may well not be the screen because of double buffering, and the one you've been handed may well not be the top-most `Graphics` either. Save yourself bother by not trying to understand it overmuch. :-) What you *do* know is that you use its methods to draw the component and you don't keep it around. Need you know more than that? – Donal Fellows Jan 02 '11 at 12:39
  • If you do need to know more, you're going from being a user of a toolkit to being a developer of a toolkit. Toolkits are **complex**. Really. There's a huge number of constraints involved, some of which are really ugly. It's also awkward as you quite quickly stop being able to work with a platform-independent abstraction. – Donal Fellows Jan 02 '11 at 12:41
  • @Donal Fellows:Well some internals are required, e.g. if I should modify the `Graphics` object or it will affect other components (e.g. rotation). Also I can not understand how to interpret the print statment of width and height `Width:184, Height:52` for my empty JComponent in my post – Cratylus Jan 02 '11 at 12:45
  • Modifying your derived object (as returned by `g.create()`) would seem easiest to me. I don't see what interpretation of those parameters is required, but that might just be me being slow. Experiment yourself! It's by far the easiest way with GUI code I've found. – Donal Fellows Jan 02 '11 at 13:00
  • @Donal Fellows:It is just that I do not understand, what is the object that has width of 184, height of 52 and no other visible attributes or children components being added in a JFrame of (200,90).Besides the frame of (200,90) nothing is displayed – Cratylus Jan 02 '11 at 13:10
  • I'm confused about your first comment. Graphics2D g2d = (Graphics2D)g.create(); seems bad style. You should simply cast the original "g" and never dispose in paint. – mtraut Jan 02 '11 at 15:22
  • @mtraut:Well I have read that it is best to create a copy of the Graphics object and then dispose it to avoid clobbering the state of the original object. Apparently this is required when for example you do rotation stuff. – Cratylus Jan 02 '11 at 18:15
  • 1
    Hmm, never heard about this before. From a stability point of view AWT has a sound design. So, if it is dangerous to change the Graphics it would either be stated in the doc or AWT would use a copy right from the start. But, in http://java.sun.com/products/jfc/tsc/articles/painting/index.html (quite a good overview) is explicitly stated: "Programs must use this Graphics object (or one derived from it) to render output. They are free to change the state of the Graphics object as necessary". But anyway this is academic.. you're on the right track. – mtraut Jan 02 '11 at 19:54