Home
9GUI Components, Drawing, AWT & Swing
9

Lab Manual IX · CSE 202

GUI Components, Drawing, AWT & Swing

Build a Swing drawing application: a JFrame with layout managers, buttons and a colour box, event listeners, and a custom JPanel that draws rectangles and circles with Graphics.

← → move between slides · F full screen · M slide list

Before we start

Why this lab

Every desktop app is a window full of boxes that react to clicks. How does Java put a button on screen and know when it is pressed?

Swing gives you components (JFrame, JButton…), layout managers to arrange them and listeners that run your code on each click. You will build a small paint program.

Lab 9 of 10

Lab at a glance

After this lab you can

  • Explain the purpose of GUIs and distinguish AWT from Swing
  • Use JFrame, JPanel, JButton, JLabel, JTextField and JComboBox
  • Arrange components with BorderLayout, FlowLayout and GridLayout
  • Handle events with ActionListener
  • Draw shapes by overriding paintComponent() and using Graphics
  • Start Swing safely on the Event Dispatch Thread

Before you start

  • Labs 4–5: inheritance, abstract classes, overriding
  • Lab 7: threads (for the EDT idea)
  • ArrayList basics

Plan for the session

  1. Theory recap
  2. Walk through the program, section by section, and run it
  3. In-lab tasks, shown to the instructor
  4. Report exercises at home

Theory 1 of 6

AWT, Swing and applets

  • AWT is the original toolkit using native OS components; Swing is built on it with lightweight, platform-independent components whose names start with J.
  • Applets ran inside browsers; browsers no longer support them, so this lab uses a JFrame desktop app — the drawing and event ideas are the same.

Theory 2 of 6

Components and containers

  • A container holds components: the JFrame holds panels, panels hold buttons, labels and the drawing canvas.

Theory 3 of 6

Layout managers

  • BorderLayout: NORTH, SOUTH, EAST, WEST, CENTER. FlowLayout: left to right. GridLayout: equal cells in rows and columns.

Layout managers

JPanel buttons = new JPanel(new FlowLayout(FlowLayout.LEFT));
buttons.add(new JButton("Add Rectangle"));
mainPanel.add(drawingPanel, BorderLayout.CENTER);

Theory 4 of 6

Event handling

rectangleButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        drawingPanel.addRectangle(x, y, 100, 80);
        updateStatus();
    }
});
  • GUI programs are event-driven: Swing calls actionPerformed when the button is clicked.

Theory 5 of 6

Custom drawing and repaint()

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);     // clear the background first
    for (Shape shape : shapes) {
        shape.draw(g);           // polymorphic draw
    }
}
  • Graphics offers drawLine, drawRect, fillRect, drawOval, fillOval, drawString.
  • Change the data, then call repaint(); Swing calls paintComponent for you. Never call it yourself.

Theory 6 of 6

The Event Dispatch Thread

SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() { new DrawingApp(); }
});
  • Swing is not thread-safe: create and update components only on the EDT.

Implementation design

Program design

Implementation design

Execution flow

  1. main schedules GUI creation on the EDT
  2. DrawingApp builds the window: title (NORTH), canvas (CENTER), controls and status (SOUTH)
  3. Listeners are registered on the three buttons
  4. Add Rectangle / Add Circle: set colour, add a shape, repaint, update status
  5. Clear All: empty the list and repaint
  6. Closing the window ends the program

Layout (DrawingApp), drawing (DrawingPanel) and shape behaviour (Shape subclasses) are separate, so adding a TriangleShape is easy.

Code walkthrough 1 of 10 · Shape.java

An abstract drawable shape

public abstract class Shape {

    protected int x;
    protected int y;
    protected Color colour;

    public Shape(int x, int y, Color colour) {
        this.x = x;
        this.y = y;
        this.colour = colour;
    }

    public abstract void draw(Graphics g);
}
  • Position and colour for every shape; each subclass decides how to draw.

Code walkthrough 2 of 10 · RectangleShape.java

RectangleShape.draw()

@Override
public void draw(Graphics g) {
    g.setColor(colour);
    g.fillRect(x, y, width, height);
    g.setColor(Color.BLACK);
    g.drawRect(x, y, width, height);
}
  • Fill in the shape’s colour, then outline in black.

Code walkthrough 3 of 10 · CircleShape.java

CircleShape.draw()

@Override
public void draw(Graphics g) {
    int diameter = 2 * radius;
    g.setColor(colour);
    g.fillOval(x - radius, y - radius, diameter, diameter);
    g.setColor(Color.BLACK);
    g.drawOval(x - radius, y - radius, diameter, diameter);
}
  • fillOval takes the top-left corner, so subtract the radius from the centre.

Code walkthrough 4 of 10 · DrawingPanel.java

paintComponent draws the frame

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    drawGrid(g);
    for (Shape shape : shapes) {
        shape.draw(g);
    }
}

private void drawGrid(Graphics g) {
    g.setColor(new Color(220, 220, 220));
    for (int x = 0; x < getWidth(); x += 50) {
        g.drawLine(x, 0, x, getHeight());
    }
    for (int y = 0; y < getHeight(); y += 50) {
        g.drawLine(0, y, getWidth(), y);
    }
}
  • Clear, draw the grid, then call draw(g) on every shape (polymorphism).

Code walkthrough 5 of 10 · DrawingPanel.java

Changing the picture

public void addRectangle(int x, int y, int width, int height) {
    shapes.add(new RectangleShape(x, y, width, height, currentColor));
    repaint();
}

public void addCircle(int x, int y, int radius) {
    shapes.add(new CircleShape(x, y, radius, currentColor));
    repaint();
}

public void clear() {
    shapes.clear();
    repaint();
}
  • Change the list, then repaint(); never call paintComponent yourself.

Code walkthrough 6 of 10 · DrawingApp.java

Building the window: north and centre

private void setupUI() {
    JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
    mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

    JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
    JLabel titleLabel = new JLabel("Drawing Application");
    titleLabel.setFont(new Font("Arial", Font.BOLD, 16));
    topPanel.add(titleLabel);

    drawingPanel = new DrawingPanel();
    drawingPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
  • BorderLayout for the frame, FlowLayout for the title row.

Code walkthrough 7 of 10 · DrawingApp.java

Building the window: the controls

JPanel controlPanel = new JPanel(new BorderLayout());
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
JLabel colorLabel = new JLabel("Colour:");
colorComboBox = new JComboBox<String>(
        new String[] {"Blue", "Red", "Green", "Yellow", "Orange", "Purple"});
rectangleButton = new JButton("Add Rectangle");
circleButton = new JButton("Add Circle");
clearButton = new JButton("Clear All");

buttonPanel.add(colorLabel);
buttonPanel.add(colorComboBox);
buttonPanel.add(rectangleButton);
buttonPanel.add(circleButton);
buttonPanel.add(clearButton);
  • Create each component, then add it to a FlowLayout row.

Code walkthrough 8 of 10 · DrawingApp.java

Placing the panels

statusLabel = new JLabel("Ready. Shapes: 0");
controlPanel.add(buttonPanel, BorderLayout.CENTER);
controlPanel.add(statusLabel, BorderLayout.SOUTH);

mainPanel.add(topPanel, BorderLayout.NORTH);
mainPanel.add(drawingPanel, BorderLayout.CENTER);
mainPanel.add(controlPanel, BorderLayout.SOUTH);
setContentPane(mainPanel);
  • Panels nest: the button row and status go in controlPanel, which goes in SOUTH.

Code walkthrough 9 of 10 · DrawingApp.java

Responding to clicks

private void addActionListeners() {
    rectangleButton.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            drawingPanel.setCurrentColor(getSelectedColor());
            int x = (int) (Math.random() * 600);
            int y = (int) (Math.random() * 350);
            drawingPanel.addRectangle(x, y, 100, 80);
            updateStatus();
        }
    });
  • actionPerformed runs on each click: pick the colour, add a shape, update the label.

Code walkthrough 10 of 10 · Lab9Demo.java

Starting on the Event Dispatch Thread

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            new DrawingApp();
        }
    });
}
  • invokeLater runs the GUI code on Swing’s own thread.

Step through the code · 1 of 2

Trace: change the model, then repaint()

Each click adds a shape to the list; paintComponent draws whatever is in the list.

Step through the code · 1 of 2

Trace: change the model, then repaint() (cont.)

Step through the code · 2 of 2

Trace: main, the EDT and a click

invokeLater hands the work to the Event Dispatch Thread. Every listener and every paint then runs on that one thread.

Step through the code · 2 of 2

Trace: main, the EDT and a click (cont.)

For reference · 6 files

The complete program

import java.awt.Color;
import java.awt.Graphics;

public abstract class Shape {

    protected int x;
    protected int y;
    protected Color colour;

    public Shape(int x, int y, Color colour) {
        this.x = x;
        this.y = y;
        this.colour = colour;
    }

    public abstract void draw(Graphics g);
}
import java.awt.Color;
import java.awt.Graphics;

public class RectangleShape extends Shape {

    private int width;
    private int height;

    public RectangleShape(int x, int y, int width, int height, Color colour) {
        super(x, y, colour);
        this.width = (width > 0) ? width : 100;
        this.height = (height > 0) ? height : 80;
    }

    @Override
    public void draw(Graphics g) {
        g.setColor(colour);
        g.fillRect(x, y, width, height);
        g.setColor(Color.BLACK);
        g.drawRect(x, y, width, height);
    }
}
import java.awt.Color;
import java.awt.Graphics;

public class CircleShape extends Shape {

    private int radius;

    public CircleShape(int x, int y, int radius, Color colour) {
        super(x, y, colour);
        this.radius = (radius > 0) ? radius : 50;
    }

    @Override
    public void draw(Graphics g) {
        int diameter = 2 * radius;
        g.setColor(colour);
        g.fillOval(x - radius, y - radius, diameter, diameter);
        g.setColor(Color.BLACK);
        g.drawOval(x - radius, y - radius, diameter, diameter);
    }
}
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;

public class DrawingPanel extends JPanel {

    private ArrayList<Shape> shapes;
    private Color currentColor;

    public DrawingPanel() {
        shapes = new ArrayList<Shape>();
        currentColor = Color.BLUE;
        setBackground(Color.WHITE);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        drawGrid(g);
        for (Shape shape : shapes) {
            shape.draw(g);
        }
    }

    private void drawGrid(Graphics g) {
        g.setColor(new Color(220, 220, 220));
        for (int x = 0; x < getWidth(); x += 50) {
            g.drawLine(x, 0, x, getHeight());
        }
        for (int y = 0; y < getHeight(); y += 50) {
            g.drawLine(0, y, getWidth(), y);
        }
    }

    public void addRectangle(int x, int y, int width, int height) {
        shapes.add(new RectangleShape(x, y, width, height, currentColor));
        repaint();
    }

    public void addCircle(int x, int y, int radius) {
        shapes.add(new CircleShape(x, y, radius, currentColor));
        repaint();
    }

    public void clear() {
        shapes.clear();
        repaint();
    }

    public void setCurrentColor(Color colour) {
        this.currentColor = colour;
    }

    public int getShapeCount() {
        return shapes.size();
    }
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class DrawingApp extends JFrame {

    private DrawingPanel drawingPanel;
    private JLabel statusLabel;
    private JButton rectangleButton;
    private JButton circleButton;
    private JButton clearButton;
    private JComboBox<String> colorComboBox;

    public DrawingApp() {
        setTitle("Drawing Application");
        setSize(800, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setupUI();
        addActionListeners();
        setVisible(true);
    }

    private void setupUI() {
        JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
        mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

        JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        JLabel titleLabel = new JLabel("Drawing Application");
        titleLabel.setFont(new Font("Arial", Font.BOLD, 16));
        topPanel.add(titleLabel);

        drawingPanel = new DrawingPanel();
        drawingPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));

        JPanel controlPanel = new JPanel(new BorderLayout());
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        JLabel colorLabel = new JLabel("Colour:");
        colorComboBox = new JComboBox<String>(
                new String[] {"Blue", "Red", "Green", "Yellow", "Orange", "Purple"});
        rectangleButton = new JButton("Add Rectangle");
        circleButton = new JButton("Add Circle");
        clearButton = new JButton("Clear All");

        buttonPanel.add(colorLabel);
        buttonPanel.add(colorComboBox);
        buttonPanel.add(rectangleButton);
        buttonPanel.add(circleButton);
        buttonPanel.add(clearButton);

        statusLabel = new JLabel("Ready. Shapes: 0");
        controlPanel.add(buttonPanel, BorderLayout.CENTER);
        controlPanel.add(statusLabel, BorderLayout.SOUTH);

        mainPanel.add(topPanel, BorderLayout.NORTH);
        mainPanel.add(drawingPanel, BorderLayout.CENTER);
        mainPanel.add(controlPanel, BorderLayout.SOUTH);
        setContentPane(mainPanel);
    }

    private void addActionListeners() {
        rectangleButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                drawingPanel.setCurrentColor(getSelectedColor());
                int x = (int) (Math.random() * 600);
                int y = (int) (Math.random() * 350);
                drawingPanel.addRectangle(x, y, 100, 80);
                updateStatus();
            }
        });
        circleButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                drawingPanel.setCurrentColor(getSelectedColor());
                int x = 60 + (int) (Math.random() * 600);
                int y = 60 + (int) (Math.random() * 350);
                drawingPanel.addCircle(x, y, 50);
                updateStatus();
            }
        });
        clearButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                drawingPanel.clear();
                updateStatus();
            }
        });
    }

    private Color getSelectedColor() {
        String selected = (String) colorComboBox.getSelectedItem();
        if ("Red".equals(selected)) {
            return Color.RED;
        } else if ("Green".equals(selected)) {
            return Color.GREEN;
        } else if ("Yellow".equals(selected)) {
            return Color.YELLOW;
        } else if ("Orange".equals(selected)) {
            return new Color(255, 165, 0);
        } else if ("Purple".equals(selected)) {
            return new Color(128, 0, 128);
        } else {
            return Color.BLUE;
        }
    }

    private void updateStatus() {
        statusLabel.setText("Ready. Shapes: " + drawingPanel.getShapeCount());
    }
}
import javax.swing.SwingUtilities;

public class Lab9Demo {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new DrawingApp();
            }
        });
    }
}

Copy or download each file, save it under the same name, then compile and run: javac *.java → java Lab9Demo

Implementation

Expected output

The program opens a window instead of printing to the console. When it works you should see:

  • A window titled “Drawing Application” with a white canvas and a light grey grid
  • A colour box and Add Rectangle, Add Circle and Clear All buttons
  • Each Add button draws a filled shape in the selected colour at a random position
  • Clear All empties the canvas
  • The status label shows the current number of shapes

Interactive

Try the drawing application

Interactive 1 of 2

Explore layout managers

Interactive 2 of 2

Drawing with Graphics calls

In-lab task 1 of 3

Task 1: Add a TriangleShape

Support triangles in the drawing app.
Requirements
Show a hint
Use g.fillPolygon(xs, ys, 3) and g.drawPolygon(xs, ys, 3) with two int arrays.

In-lab task 2 of 3

Task 2: Mouse-click drawing

Draw where the user clicks.
Requirements
Show a hint
Extend MouseAdapter and override only mouseClicked(MouseEvent e); read e.getX(), e.getY().

In-lab task 3 of 3

Task 3: Colour picker

Choose any colour with JColorChooser.
Requirements
Show a hint
JColorChooser.showDialog(this, "Pick a colour", current) returns null if the user cancels.

In-lab tasks

Show your work during the lab

For each task, show the instructor:

  • the source code
  • a successful compilation
  • the running output
  • a short explanation of the logic you used

Lab report · exercise 1 of 2

Undo and redo

Extend the drawing app with history.
Requirements

Lab report · exercise 2 of 2

Freehand drawing with mouse drag

Draw by dragging the mouse.
Requirements

Lab report

Writing the lab report

  • Problem statement and your design (classes, fields, methods)
  • Complete source code, formatted and commented
  • Sample output or screenshots for normal and edge cases
  • A short discussion: what each OOP concept did in your program

Academic integrity

Copying from the internet, classmates, seniors or any other unauthorized source is strictly prohibited. Full marks may be deducted if plagiarism, copied work or academic dishonesty is detected. Complete the tasks, implementation, output analysis and report yourself.

Discussion and analysis

Viva questions (1 of 2)

Why call super.paintComponent(g) first?
Show the answer
It paints the background and clears the previous frame; without it old drawings remain as trails.
Why should you call repaint() instead of paintComponent() directly?
Show the answer
repaint() asks Swing to redraw at the right time on the EDT with a proper Graphics object.

Discussion and analysis

Viva questions (2 of 2)

How does the program show polymorphism?
Show the answer
DrawingPanel keeps an ArrayList<Shape> and calls draw(g) on each; Java runs the RectangleShape or CircleShape version.
Why start the GUI with SwingUtilities.invokeLater?
Show the answer
Swing is not thread-safe; invokeLater runs the creation code on the Event Dispatch Thread.

Check yourself

Quiz

Summary

Summary

  • Swing builds on AWT with lightweight J-components; applets are obsolete.
  • Containers hold components; layout managers position them.
  • Listeners make the GUI event-driven.
  • Draw in paintComponent and trigger it with repaint().
  • Create and update Swing on the Event Dispatch Thread.