< Day Day Up > |
This section gives an overview of what you need to know before you write code that paints Swing components . It concentrates on the coordinate system, including how borders restrict the coordinates available for painting, and on the Graphics object passed into paint methods . Although most of the API discussed isn't specific to Swing components, we concentrate on what JComponent s need. For information on actually painting, you should refer to the API documentation for the Graphics class [1] and to The Java Tutorial 's 2D Graphics trail. [2]
The Coordinate SystemEach component has its own integer coordinate system, ranging from (0, 0) to ( width - 1, height - 1), where width and height are the size of the component in pixels. As Figure 4 shows, the upper left corner of a component's painting area is (0, 0). The x coordinate increases to the right, and the y coordinate increases downward. Figure 4. A component's coordinate system.
When painting, you must take into account not only the component's size but also the size of the component's border, if any. For example, a border that paints a 1-pixel line around a component effectively changes the top leftmost corner of the component's nonbackground painting area from (0,0) to (1,1) and reduces the width and the height of the painting area by 2 pixels each (1 pixel per side). Figure 5 demonstrates this: Figure 5. If the component has a border, remember to account for it.
You can get the width and height of any JComponent using its getWidth and getHeight methods. The getSize method is another option that works for all Component s. To determine the border size, use the getInsets method. Here's some code that a component might use to determine the width and height available for custom painting: protected void paintComponent(Graphics g) { ... Insets insets = getInsets(); int currentWidth = getWidth() - insets.left - insets.right; int currentHeight = getHeight() - insets.top - insets.bottom; ... .../* First painting occurs at (x,y), where x is at least insets.left, and y is at least insets.top. */... } To familiarize yourself with the coordinate system, you can play with the CoordinatesDemo application, which features a component implemented using an inner class named CoordinateArea . The CoordinateArea has a preferred size of 400x75 and a solid red border that occupies 5 pixels at the left and bottom, and 1 pixel at the top and right. Like any good component, it starts painting its innards (a 20x20-pixel gray grid) where the border endsin this case, at (5,1). The CoordinateArea also paints a dot where the user clicks. As you can see from Figure 6, a label at the bottom of the GUI displays information about the cursor and click location. Figure 6. The CoordinatesDemo application.
Try This:
The SelectionDemo example shown in Figure 7 is another that can give you a feeling for coordinates. It paints a continuously updating rectangle, which the user sets by dragging the mouse. To avoid unnecessary painting, the component's mouse-dragged and mouse-released event handler uses a version of the repaint method that specifies which area of the component needs to be repainted. Figure 7. The SelectionDemo application.
The following code from SelectionDemo.java shows how repaint is invoked by the event handler for mouse-dragged and mouse-released events. The handler gets the current cursor location, updates the selection rectangle, calculates the total area to be repainted, and then specifies that area to the repaint method. Note that the total area to be repainted is equal to the previously painted area plus the newly selected area. This way, it cleans up the previous painting, avoiding the faux pas of leaving behind ghost images of the selection rectangle. void updateSize(MouseEvent e) { int x = e.getX(); int y = e.getY(); ...//save previous selection rectangle (rectToDraw) //in previousRectDrawn... //calculate new values for rectToDraw... Rectangle totalRepaint = rectToDraw.union(previousRectDrawn); repaint(totalRepaint.x, totalRepaint.y, totalRepaint.width, totalRepaint.height); } Here's the code that paints the selection rectangle on top of the image: g.drawRect(rectToDraw.x, rectToDraw.y, rectToDraw.width - 1, rectToDraw.height - 1) Note: The last two arguments to drawRect have -1 because the painting system draws lines just below the specified rectangle, instead of within the specified rectangle. The same rule of specifying one less than the desired width and height applies to other drawXxx methods. For fillXxx methods, on the other hand, you specify exactly the desired width and height in pixels. Here are examples of painting two rectangles of the same size, one filled and one not: g.fillRect(x, y, rectWidth, rectHeight); g.drawRect(x, y, rectWidth - 1, rectHeight - 1); The Graphics Object and Graphics2DThe Graphics object passed into the paintComponent method provides both a context and some methods for simple painting. [6] In almost every case, Graphics objects are actually Graphics2D objects. [7] The Graphics2D class extends Graphics to provide more sophisticated control over geometry, coordinate transformations, color management, and text layout. You can cast any Graphics parameter into a Graphics2D object as long as your program uses the Java 2 platform (1.2 or later) and doesn't use the old printing API.
Version Note: The old printing API is centered around the java.awt.PrintJob class. A newer printing API centered around java.awt.print.PrinterJob was introduced in 1.2; it uses Graphics2D . In 1.4.2, Java Plug-in printing was updated to use the newer printing API. You can learn more about this newer API in the "Printing" section of The Java Tutorial's 2D Graphics trail; this trail is included on the CD and is online at: http://java.sun.com/docs/books/tutorial/2d/printing/index.html. The painting methods defined by Graphics include such standbys as drawRect , fillRect , and drawLine . The Graphics2D class adds more flexible methods such as draw(Shape) and fill(Shape) . The graphics context provided by a Graphics object consists of state such as the current painting color, the current font, and the current painting area. The Graphics2D class adds more state, such as the alpha compositing mode, stroke, rendering hints, and color patterns such as textures and gradients. In JComponent s, the Graphics object is initialized just before it's passed to paintComponent , so that its color and font are set to the foreground color and font of the component. The same Graphics object is then passed to the paintBorder and paintChildren methods. This reuse is efficient but can lead to trouble if painting code permanently changes the Graphics object's state. For example, permanently changing the alpha compositing mode of a Graphics2D object could cause the component's border or children to be painted incorrectly. To avoid problems, remember this rule:
You can take two approaches to avoid permanently changing the Graphics object: Either restore the Graphics object's original state, or use a copy of the original object. If you take the first approach, you don't need to bother restoring the font and color, since those properties are usually set before painting. Here's an example of the first approach: //Example of restoring the Graphics object's state Graphics2D g2d = (Graphics2D)g; g2d.translate(x, y); //set the transform ... g2d.translate(-x, -y); //go back to the original transform Here's an example of the second approach: //Example of copying the Graphics object Graphics2D g2d = (Graphics2D)g.create(); //copy g g2d.translate(x, y); ... g2d.dispose(); //release the copy's resources The advantage of the first approach is that it doesn't create extra objects. The advantage of the second is that it's easy and guaranteed to succeed, no matter how much state you change. When writing your painting code, keep in mind that you can't depend on any graphics context except what's provided by the Graphics object. For example, if you specify a painting area to repaint , you can't rely on the same painting area being specified in the next call to paintComponent . For one thing, multiple repaint requests can be coalesced into a single paintComponent call. For another, the painting system occasionally calls paintComponent on its own, without any repaint request from your programfor example, in response to first being shown or to a window that obscured it from being moved. |
< Day Day Up > |