Why do we put all our graphical drawing code into a paintComponent() method? It seems odd, since it would seem we should be able to simply stick some simple graphics commands into our main() method in a Java application and just get the drawing done. Where does paintComponent come from? If we never call it in our code, how does it get executed?
It seems like magic, doesn't it? You just sort of set it up and somehow it happens. Hopefully when and how we want it to. It seems like a vaguely disquieting form of magic, perhaps.
When Java does graphics, suddenly you're not in the strict control of the program that you are with command line applications. With graphics, your program is now interacting with the other parts of your computer's operating system. It's not just running in its own little corner of the machine. Now it's got a window out on that shared resource, the display, and the window has to interact properly with everything else that's on the display.
Think of it this way, if you are just driving around on private property all by yourself you don't have to worry about traffic. Off on your own stretch of road, with an unused parking lot or two to tool around in, you don't have to worry about sharing. You can cross the center line, pull on and off the road without signalling, you don't even need a license so long as you don't go on a public road.
But once you head for the public roads and freeway, you've got to share. The rules matter. It gets more complicated than buzzing around in a go-cart or whatever on private property. And things happen that you don't have any direct control over, like red lights and merging traffic.
When you move your program on to the display in Java, you don't have to do everything on your own as you would have to in a car. Java's graphics system takes care of a lot for you. Unfortunately this means your code starts looking like magic. Methods get called without you doing the calling. But it's OK. All you need to know is what to expect.
The paintComponent() method redraws your graphics area. In our examples so far this has been a JPanel. There are two ways paintComponent() can get called. One is automatic, the other is up to you.
The automatic way is what we've used so far. The computer's window manager automatically calls for your program to redraw its graphics whenever a window it owns appears to need redrawing. The window manager can decide this for any of a number of reasons. Such as when the window initially appears on screen. Or if the window has been covered up in part or in whole by another window on screen, and the covered part has just been uncovered. Maybe the window had been minimized, and has just been restored.
When these sort of things happen, the Java Runtime Environment calls the paint() method for your container. In our examples so far, our container has been a JFrame. It will be a "heavyweight" component (see the JFrames article for a brief discussion on this.) Once the container redraws itself, it'll pass the command down to its children, that is, the lightweight components that live inside it. A lightweight component from the Swing package uses the paintComponent() method to redraw itself. So that's what gets called. Shazam! It's like magic!
This means you want to set up paintComponent() to draw things the way you want them when it gets called. If you're doing drawings that don't change, like the examples we've done to date, then you don't need to look up any information. If you're doing anything animated or that otherwise changes over time, you need to have paintComponent() be able to look things up at the time of drawing to properly reflect conditions in the drawing.
Now, let's say you've changed something in your program and you want to redraw to show the change. You probably don't want to wait until the window manager thinks your window needs redrawing. If nobody is changing things on the screen otherwise, it may not redraw until way too late!
Does that mean you put a call to paintComponent() in your program? No! You would be taking a chance of breaking something if you did. You want to start from the top, like the Java Runtime Environment did. In your case, you want to call repaint(). This will take things from the top. It also handles some possibilities that jumping in and calling paintComponent() on your own will not.
Like, let's say both you and the window manager decide to redraw at the same time. If you bypass repaint() you'll be slowing down your program by making it repaint twice when it really only needs to redraw once. When you call repaint(), Java catches this for you and saves you some computer time by only redrawing once. It'll also catch redraw requests made while the redrawing is going on and deal with them appropriately. Your paintComponent() method will get called in its own turn when you use repaint().
So, if you have new information you want depicted on screen, use repaint().
Put all your drawing inside paintComponent() and let the system take care of knowing when to call it otherwise. Give paintComponent() the ability to get the information it needs to draw things the way you want them now. If you're getting ahead of what I've written here, and aren't sure how to get through the Object Oriented barriers, look up "inner classes" for a start. It gives you a way to get the information where you need it when you need it.
Showing posts with label paint(). Show all posts
Showing posts with label paint(). Show all posts
Sunday, July 20, 2008
Saturday, July 19, 2008
Very Basic Java Graphics: 3 Examples
We're going to build up a graphics app from something so basic it doesn't really work, like the example from Java Graphics--Start with a JFrame and building to something more sophisticated. In the JFrame article and in A Most Basic Graphics App I've avoided the use of comments in the code to keep it shorter. In these examples I'm going to include comments in the code to describe what's happening. So don't be scared of the length of these examples, most of what's there is just comments, which Java ignores. They're there just for humans.
This example draws a line on the JFrame. The window decorations take up some of our drawing space, so the title bar may cover some of our line. With some JVMs the background area of the JFrame won't be cleared before you draw. Also, when you close the window using the close button, the application doesn't shut down. For many applications on Mac OS X this is normal behavior, but on other OSes, and even many applications under OS X it's normal to expect an application to close down completely if you click the close button on the last open window (for applications that have multiple windows) or the close button on the main window for other applications.
So here's our next example, where we take care of that last problem:
The magic line that makes the application exit when we click the close button is the one that reads: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);. We can tell that EXIT_ON_CLOSE is a type of variable called a "constant" because the name follows the convention of being written in all capital letters. So we're not YELLING when we say "EXIT_ON_CLOSE" we're using the value stored in EXIT_ON_CLOSE as part of the JFrame class.
A JFrame has this constant available because it implements the interface WindowConstants. By implementing this interface, in essence, the JFrame class of objects promise to do the right thing if one of these constants is passed to a method that handles it, in this case setDefaultCloseOperation.
Our final example for this entry goes one big step further:
Here, we've added a JPanel inside the drawable area of our JFrame. This means that if we start drawing at 0,0 we'll actually be drawing somewhere we can see it, it won't be hidden by the window decorations.
The other big change is the change of our drawing method's name from paint() to paintComponent(). This is because we changed from a JFrame to a JPanel. If you take a look at JPanel in the API Specification you'll see that it extends JComponent. This family of objects uses paintComponent() to manage drawing.
This last example is a good starting point for any graphical operation. In future examples, I'll be extending it further to add additional features.
/* BasicFrame.java
This is a really simple graphics program.
It opens a frame on the screen with a single
line drawn across it.
It's not very polished, but it demonstrates
a graphical program as simply as possible.mag-27Apr2008
*/
// Import the basic graphics classes.
import java.awt.*;
import javax.swing.*;
public class BasicFrame extends JFrame{
// Create a constructor method
public BasicFrame(){
// All we do is call JFrame's constructor.
// We don't need anything special for this
// program.
super();
}
// The following methods are instance methods.
/* Create a paint() method to override the one in JFrame.
This is where the drawing happens.
We don't have to call it in our program, it gets called
automatically whenever the frame needs to be redrawn,
like when it it made visible or moved or whatever.*/
public void paint(Graphics g){
g.drawLine(10,10,150,150); // Draw a line from (10,10) to (150,150)
}
public static void main(String arg[]){
// create an identifier named 'window' and
// apply it to a new BasicFrame object
// created using our constructor, above.
BasicFrame frame = new BasicFrame();
// Use the setSize method that our BasicFrame
// object inherited to make the frame
// 200 pixels wide and high.
frame.setSize(200,200);
// Make the window show on the screen.
frame.setVisible(true);
}
}
This example draws a line on the JFrame. The window decorations take up some of our drawing space, so the title bar may cover some of our line. With some JVMs the background area of the JFrame won't be cleared before you draw. Also, when you close the window using the close button, the application doesn't shut down. For many applications on Mac OS X this is normal behavior, but on other OSes, and even many applications under OS X it's normal to expect an application to close down completely if you click the close button on the last open window (for applications that have multiple windows) or the close button on the main window for other applications.
So here's our next example, where we take care of that last problem:
/* CloseFrame.java
This is a really simple graphics program.
It opens a frame on the screen with a single
line drawn across it.
We're starting to add a little bit of polish
here--we make the program close nicely when
the close box is clicked, rather than just
sort of hanging around half-dead.
mag-28Apr2008
*/
// Import the basic graphics classes.
import java.awt.*;
import javax.swing.*;
public class CloseFrame extends JFrame{
// Create a constructor method
public CloseFrame(){
// All we do is call JFrame's constructor.
// We don't need anything special for this.
super();
}
// The following methods are instance methods.
/* Create a paint() method to override the one in JFrame.
This is where the drawing happens.
We don't have to call it in our program,
it gets called automatically whenever the
frame needs to be redrawn, like when it is
made visible or moved or whatever.
*/
public void paint(Graphics g){
g.drawLine(10,10,150,150); // Draw a line from (10,10) to (150,150)
}
public static void main(String arg[]){
BasicFrame frame = new BasicFrame();
// This uses a constant EXIT_ON_CLOSE that's a member of JFrame.
// The constant is passed to the setDefaultCloseOperation method
// of our frame object, which is a CloseFrame object,
// which inherits the method from its parent JFrame.
// It makes the program exit (close completely) when we click
// the close button.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200,200);frame.setVisible(true);
}
}
The magic line that makes the application exit when we click the close button is the one that reads: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);. We can tell that EXIT_ON_CLOSE is a type of variable called a "constant" because the name follows the convention of being written in all capital letters. So we're not YELLING when we say "EXIT_ON_CLOSE" we're using the value stored in EXIT_ON_CLOSE as part of the JFrame class.
A JFrame has this constant available because it implements the interface WindowConstants. By implementing this interface, in essence, the JFrame class of objects promise to do the right thing if one of these constants is passed to a method that handles it, in this case setDefaultCloseOperation.
Our final example for this entry goes one big step further:
/* BasicJPanel.java
This is a somewhat more sophisticated drawing program.
It uses a new child of JPanel as the drawing surface
for a JFrame, to avoid the problems with drawing
directly on a JFrame.
A custom JPanel child class called BasicJPanel is created
with its own paintComponent method, which contains our
drawing code.
A generic JFrame is then created to hold the BasicJPanel
object, the BasicJPanel is created, made into the JPanel's
content pane, and our paintComponent method is called
automatically. *Whew!*
mag-28Apr2008
*/
// Import the basic graphics classes.
import java.awt.*;
import javax.swing.*;
public class BasicJPanel extends JPanel{
// Create a constructor method
public BasicJPanel(){
super();
}
// The following methods are instance methods.
/* Create a paintComponent() method to override the one in
JPanel.This is where the drawing happens. We don't have
to call it in our program, it gets called automatically
whenever the panel needs to be redrawn, like when it is
made visible or moved or whatever.
*/
public void paintComponent(Graphics g){
g.drawLine(10,10,150,150); // Draw a line from (10,10) to (150,150)
}
public static void main(String arg[]){
JFrame frame = new JFrame("BasicJPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200,200);
// Create a new identifier for a BasicJPanel called "panel",
// then create a new BasicJPanel object for it to refer to.
BasicJPanel panel = new BasicJPanel();
// Make the panel object the content pane of the JFrame.
// This puts it into the drawable area of frame, and now
// we do all our drawing to panel, using paintComponent(), above.
frame.setContentPane(panel);
frame.setVisible(true);
}
}
Here, we've added a JPanel inside the drawable area of our JFrame. This means that if we start drawing at 0,0 we'll actually be drawing somewhere we can see it, it won't be hidden by the window decorations.
The other big change is the change of our drawing method's name from paint() to paintComponent(). This is because we changed from a JFrame to a JPanel. If you take a look at JPanel in the API Specification you'll see that it extends JComponent. This family of objects uses paintComponent() to manage drawing.
This last example is a good starting point for any graphical operation. In future examples, I'll be extending it further to add additional features.
Labels:
application,
constants,
graphics,
JFrame,
JPanel,
paint(),
paintComponent()

Monday, July 7, 2008
Java Graphics--Start with a JFrame

In A Most Basic Graphics App I presented a skeleton program that's suitable as a starting point for a graphics application. It deserves some explanation before we add more to it.
There are two graphics "components" that appear in this program, a JPanel and a JFrame. Why use both? If we want a really basic graphics app, why isn't one or the other enough?
If we're going to choose between the two, we'd have to take the JFrame. A Frame component is a special graphics component that lives on top of the host computer's graphics system. In Java it's called a "heavyweight" component. The "backboard" on which you draw, or place other components, has to be a heavyweight component. Only a heavyweight component can be guaranteed to be drawn correctly when placed directly on the user's display. Lightweight components have to be placed on top of a heavyweight component.
Since JPanel is a lightweight component, we can't use it without a heavyweight like JFrame to place it on. So if we're just picking one component, it's going to be the JFrame:
import java.awt.*;
import javax.swing.*;
public class JustaFrame extends JFrame{
public JustaFrame(){
super();
}
public void paint(Graphics g){
g.drawLine(10,10,150,150);
}
public static void main(String[] arg){
JustaFrame frame = new JustaFrame();
frame.setSize(200,200);
frame.setVisible(true);
}
}
You'll notice a problem here if you run the program. The "window decorations" for the title bar, window borders, and so on take away a piece of the space from the drawing area. Unless you've got pretty narrow title bars on your system, the line drawn by this program will start up underneath the title bar.
It's possible to find out how much space these items take up, and compensate. Or you can add another component in the visible drawing area as I did with the JPanel in the original program.
There's another difference here. The drawing is done by a paint() method, rather than a paintComponent() method. This is part of the difference between different types of components. If you change paint() into paintComponent() this program will compile and run, but it won't draw the line.
Later, we'll talk about the magic behind the paint() and paintComponent() methods and some of the other hidden mysteries of Java's graphics.

Subscribe to:
Posts (Atom)
Subjects:
- Android (3)
- angle brackets (1)
- API (15)
- applet (2)
- application (15)
- arguments (2)
- array (3)
- array initializer (1)
- assignment (3)
- asterisk (2)
- beginner (8)
- book review (4)
- C (1)
- C# (1)
- C++ (1)
- calculations (4)
- classes (8)
- code blocks (3)
- collection (6)
- collision detection (3)
- command line (7)
- Comments (1)
- comparison (3)
- constants (1)
- constructor (2)
- cross-platform (1)
- curly braces (3)
- dates (1)
- declaration (4)
- documentation (3)
- dot notation (1)
- embedded (1)
- environment (11)
- equals (2)
- events (3)
- example (2)
- expressions (2)
- file (3)
- file names (1)
- framework (3)
- generics (2)
- graphics (22)
- graphics Swing CSS (1)
- heavyweight component (1)
- humor (1)
- i/o (3)
- IDE (9)
- import (2)
- inheritance (2)
- initializer (1)
- inner class (5)
- input (5)
- interface (3)
- Java (27)
- Java version (7)
- Javascript (1)
- JDK (5)
- JFrame (6)
- JPanel (7)
- JVM (10)
- lightweight component (2)
- List (3)
- listeners (3)
- literal (1)
- local (1)
- loop (2)
- main() (4)
- Math (3)
- method (4)
- methods (2)
- modulo (1)
- mouse (1)
- name (2)
- numeric (2)
- object code (1)
- objects (3)
- operators (4)
- output (3)
- packages (5)
- paint() (3)
- paintComponent() (4)
- parentheses (1)
- percent sign (2)
- PHP (1)
- public (2)
- read (2)
- reference (8)
- regular expressions (1)
- Resources (5)
- restore (1)
- save (1)
- Scanner (1)
- semicolons (1)
- signature (4)
- source code (4)
- square brackets (1)
- star (1)
- static (2)
- Swift (1)
- System.out.println() (2)
- threads (2)
- trigonometry (1)
- type (8)
- variables (9)
- video game (8)
- virtual machine (2)
- void (2)
- web (2)
- why java (9)