| Lesson 2 | AWT / Java 2D Painting |
| Objective | Draw text and shapes with Graphics/Graphics2D, using modern Swing painting on the EDT. |
Why this page was updated: The original text focused on applets and paint() on AWT components. Applets are removed from modern Java, and desktop painting should use Swing’s JComponent#paintComponent with Graphics2D. This revision updates the concepts, code, and terminology.
AWT exposes an abstract drawing surface via java.awt.Graphics. In practice you’ll usually receive a
Graphics2D instance (a subclass) that enables Java 2D features: antialiasing, strokes, transforms, composites, and advanced paints.
In Swing, custom painting belongs in paintComponent(Graphics g) of a JComponent (e.g., JPanel).
Always call super.paintComponent(g) first and perform drawing afterward. Swing handles double buffering.
// A minimal, modern painting example (runs on Java SE 8+)
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
public class PaintingDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame("AWT / Java 2D Painting");
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setContentPane(new CanvasPanel());
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
});
}
static final class CanvasPanel extends JPanel {
CanvasPanel() {
setBackground(new Color(0xF7F7FA));
setPreferredSize(new Dimension(520, 320));
}
@Override protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
try {
// 1) Rendering hints (antialias)
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// 2) Paint (color/gradient) and Stroke (line style)
g2.setPaint(new GradientPaint(20, 20, new Color(0x3B82F6),
200, 120, new Color(0x06B6D4)));
g2.fill(new RoundRectangle2D.Double(20, 20, 200, 120, 18, 18));
g2.setStroke(new BasicStroke(3f));
g2.setPaint(new Color(0x111827));
g2.draw(new Ellipse2D.Double(260, 30, 120, 120));
// 3) Font and text
g2.setFont(new Font("SansSerif", Font.BOLD, 16));
g2.drawString("Hello, Java 2D!", 20, 180);
// 4) Transform (rotate and draw again)
AffineTransform old = g2.getTransform();
g2.rotate(Math.toRadians(-12), 340, 210);
g2.setPaint(new Color(0xEF4444));
g2.fill(new Rectangle2D.Double(300, 180, 160, 60));
g2.setPaint(Color.WHITE);
g2.drawString("Transformed", 315, 215);
g2.setTransform(old);
} finally {
g2.dispose(); // release the Graphics context copy
}
}
}
}
Color, GradientPaint, TexturePaint).BasicStroke for width, caps, joins, dashes).Font, measured with FontMetrics).AlphaComposite).AffineTransform).setClip / clip).FontMetrics.The origin (0,0) is the top‑left of the component; +x grows rightward, +y downward. Units are device pixels unless a transform is applied. Java 2D uses a double‑precision user space that’s mapped to device space, enabling crisp vector graphics and resolution independence.
setBackground() and
super.paintComponent(g) will clear with it. Use g2.setPaint(...) for drawing/filling.Font on the Graphics2D, then use drawString. For layout, measure with FontMetrics.AffineTransform.SwingUtilities.invokeLater).paintComponent, not paint: and always call super.paintComponent first.SwingWorker, then repaint().Graphics2D: set rendering hints (antialiasing), strokes, and transforms for quality output.ImageIO.read; draw with drawImage; for off‑screen work use BufferedImage.-Djava.awt.headless=true to run graphics code that doesn’t require a display.
// Lines and shapes
g2.setPaint(Color.BLACK);
g2.draw(new Line2D.Double(10, 10, 120, 10));
g2.fill(new Rectangle2D.Double(10, 20, 80, 40));
// Stroke styles
g2.setStroke(new BasicStroke(5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER, 10f, new float[]{10f, 6f}, 0f));
g2.draw(new Ellipse2D.Double(120, 20, 80, 40));
// Text
g2.setFont(new Font("SansSerif", Font.PLAIN, 14));
g2.drawString("Sample", 12, 90);
// Images
// BufferedImage img = ImageIO.read(path);
// g2.drawImage(img, 200, 30, this);