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);