Double Buffering Drag in Java Swing Applications

Double Buffering Drag in Java Swing Applications

This Java program is a simple interactive application that allows users to drag a red square within a window. It uses JFrame to display the UI structure. The purpose of this code is to demonstrate a basic graphical user interface (GUI) application in Java, introducing concepts such as drag and drop, event handling, drawing on a panel, and using a JFrame for the application window.

Learning Points:

  • Understanding mouse events (press, release, drag) in Java.
  • Basic drawing on a panel using the Graphics object.
  • Creating a simple GUI application with a window and interactive elements.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class DoubleBufferedDrag extends JFrame {

    public static void main(String[] args) {
        // Create and configure the main frame
        DoubleBufferedDrag frame = new DoubleBufferedDrag();
        frame.setTitle("Double Buffered Drag");
        frame.setSize(400, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Add the Display panel to the frame
        frame.setContentPane(new Display());

        // Make the frame visible
        frame.setVisible(true);
    }

    static class Display extends JPanel implements MouseListener, MouseMotionListener {
        // Nested class for the drawing surface of the application.

        int x1, y1; // Coords of top-left corner of the red square.

        /* Some variables used during dragging */        boolean dragging; // Set to true when a drag is in progress.
        int offsetX, offsetY; // Offset of mouse-click coordinates from
                              // top-left corner of the square.

        Display() {
            // Constructor sets the initial position of the square
            // and sets up mouse listening.
            x1 = 10;
            y1 = 10;
            setBackground(Color.white);
            addMouseListener(this);
            addMouseMotionListener(this);
        }

        public void paintComponent(Graphics g) {
            // Draw a background of horizontal and vertical lines,
            // with the red square that the user drags on top.
            // Also draw a 2-pixel black frame around the panel.
            int width = getSize().width; // width of this panel
            int height = getSize().height; // height of this
            super.paintComponent(g); // clear to background color
            g.setColor(Color.gray);
            for (int i = 3; i < width; i += 2) // vertical lines
                g.drawLine(i, 0, i, height);
            for (int j = 3; j < height; j += 2) // horizontal lines
                g.drawLine(0, j, width, j);
            g.setColor(Color.red);
            g.fillRect(x1, y1, 50, 50); // the red square
            g.setColor(Color.black);
            g.drawRect(0, 0, width - 1, height - 1); // frame
            g.drawRect(1, 1, width - 3, height - 3);
        }

        public void mousePressed(MouseEvent evt) {
            // Respond when the user presses the mouse on the panel.
            // If the user clicked in the red square, start dragging it.

            if (dragging) // Exit if a drag is already in progress.
                return;

            int x = evt.getX(); // Location where the user clicked.
            int y = evt.getY();

            if (x >= x1 && x < x1 + 50 && y >= y1 && y < y1 + 50) {
                // The user clicked the red square.
                dragging = true;
                offsetX = x - x1; // Distance from corner of square to click point.
                offsetY = y - y1;
            }
        }

        public void mouseReleased(MouseEvent evt) {
            // Dragging stops when the user releases the mouse button.
            // If the square is (almost) off the screen, move it back to
            // its starting point.
            dragging = false;
            if (x1 + 50 < 3 || x1 > getWidth() - 3 || y1 + 50 < 3 || y1 > getHeight() - 3) {
                x1 = 10;
                y1 = 10;
                repaint();
            }
        }

        public void mouseDragged(MouseEvent evt) {
            // Respond when the user drags the mouse. If a square is
            // not being dragged, then exit. Otherwise, change the position
            // of the square that is being dragged to match the position
            // of the mouse. Note that the corner of the square is placed
            // in the same position with respect to the mouse that it had
            // when the user started dragging it.
            if (!dragging)
                return;
            int x = evt.getX(); // position of the mouse
            int y = evt.getY();
            x1 = x - offsetX; // move the square
            y1 = y - offsetY;
            repaint();
        }

        public void mouseMoved(MouseEvent evt) {
        }

        public void mouseClicked(MouseEvent evt) {
        }

        public void mouseEntered(MouseEvent evt) {
        }

        public void mouseExited(MouseEvent evt) {
        }
    }
}

What is Double Buffering?

Double buffering is a technique used in graphical applications, including Swing-based Java applications, to reduce flickering and improve the overall visual quality of animations or dynamic updates on the screen. The basic idea is to draw the entire image off-screen (in a separate buffer) before displaying it on the screen, which helps eliminate the flickering that can occur when updating parts of the image in-place.

What are the advantages of Double Buffering?

In the context of Swing Java applications, which often involve complex GUIs with various components and animations, double buffering is particularly beneficial. Here’s how it typically works:

  1. Back Buffer: A back buffer, also known as an off-screen buffer, is created. This is essentially an image or a region of memory where the drawing operations take place off-screen.
  2. Draw Operations: All drawing operations, such as rendering components, graphics, or animations, are performed on the back buffer rather than directly on the visible screen.
  3. Buffer Swap: Once the drawing is complete on the back buffer, the content of the back buffer is swapped with the front buffer (the visible screen). This operation is atomic and happens quickly.
  4. Flicker Reduction: Since the entire image is drawn off-screen before being displayed, there is a moment when the screen is not updated partially. This reduces flickering and creates a smoother visual experience for the user.

In Swing, double buffering is often achieved using the BufferedImage class. The BufferedImage serves as the back buffer where the drawing operations take place. The ImageObserver interface, which is implemented by many Swing components, helps manage the loading and drawing of images.

Double Buffering Java Swing Example

Here’s a simple example of double buffering in a Swing application:

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;

public class DoubleBufferingExample extends JFrame {

    private BufferedImage backBuffer;

    public DoubleBufferingExample() {
        setTitle("Double Buffering Example");
        setSize(400, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Initialize the back buffer
        backBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);

        // Perform initial drawing on the back buffer
        drawOnBackBuffer();

        // Set up the event handling, components, etc.

        // Make the frame visible
        setVisible(true);
    }

    private void drawOnBackBuffer() {
        // Perform drawing operations on the back buffer
        Graphics g = backBuffer.getGraphics();
        g.setColor(Color.RED);
        g.fillRect(50, 50, 100, 100);
        // Add more drawing operations as needed
        g.dispose();
    }

    @Override
    public void paint(Graphics g) {
        // Copy the contents of the back buffer to the visible screen
        g.drawImage(backBuffer, 0, 0, this);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(DoubleBufferingExample::new);
    }
}

 

M. Saqib: Saqib is Master-level Senior Software Engineer with over 14 years of experience in designing and developing large-scale software and web applications. He has more than eight years experience of leading software development teams. Saqib provides consultancy to develop software systems and web services for Fortune 500 companies. He has hands-on experience in C/C++ Java, JavaScript, PHP and .NET Technologies. Saqib owns and write contents on mycplus.com since 2004.
Related Post