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:
- 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.
- 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.
- 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.
- 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); } }