Tuesday, July 22, 2025

Java Example Object Move, Resize, Fill Resizer on Mouse Click

# Java Swing Resizable, Movable, and Fillable Components

Here's a complete implementation of a Java Swing application that allows components to be resized, moved, and filled with color:

```java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;

public class ResizableMovablePanel extends JFrame {
    private List<ResizableShape> shapes = new ArrayList<>();
    private ResizableShape selectedShape;
    private Point dragStart;
    private Color currentColor = Color.BLUE;
    private JComboBox<String> shapeSelector;
    private JButton colorButton;

    public ResizableMovablePanel() {
        setTitle("Resizable and Movable Shapes");
        setSize(800, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // Create control panel
        JPanel controlPanel = new JPanel();
        shapeSelector = new JComboBox<>(new String[]{"Rectangle", "Oval"});
        colorButton = new JButton("Choose Color");
        colorButton.addActionListener(e -> chooseColor());
        controlPanel.add(new JLabel("Shape:"));
        controlPanel.add(shapeSelector);
        controlPanel.add(colorButton);
        add(controlPanel, BorderLayout.NORTH);
        
        // Create drawing panel
        JPanel drawingPanel = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g;
                
                // Draw all shapes
                for (ResizableShape shape : shapes) {
                    shape.draw(g2d);
                }
            }
        };
        
        drawingPanel.setBackground(Color.WHITE);
        drawingPanel.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                handleMousePress(e);
            }
            
            @Override
            public void mouseReleased(MouseEvent e) {
                handleMouseRelease();
            }
            
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    createNewShape(e.getPoint());
                }
            }
        });
        
        drawingPanel.addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                handleMouseDrag(e);
            }
        });
        
        add(drawingPanel, BorderLayout.CENTER);
    }
    
    private void chooseColor() {
        Color newColor = JColorChooser.showDialog(this, "Choose Shape Color", currentColor);
        if (newColor != null) {
            currentColor = newColor;
            if (selectedShape != null) {
                selectedShape.setColor(currentColor);
                repaint();
            }
        }
    }
    
    private void createNewShape(Point location) {
        String shapeType = (String) shapeSelector.getSelectedItem();
        ResizableShape newShape;
        
        if ("Rectangle".equals(shapeType)) {
            newShape = new ResizableRectangle(location.x, location.y, 100, 100, currentColor);
        } else {
            newShape = new ResizableOval(location.x, location.y, 100, 100, currentColor);
        }
        
        shapes.add(newShape);
        selectedShape = newShape;
        repaint();
    }
    
    private void handleMousePress(MouseEvent e) {
        selectedShape = null;
        
        // Check if clicked on any shape
        for (int i = shapes.size() - 1; i >= 0; i--) {
            ResizableShape shape = shapes.get(i);
            if (shape.contains(e.getPoint())) {
                selectedShape = shape;
                dragStart = e.getPoint();
                
                // Check if clicked on resize handle
                if (shape.isResizeHandle(e.getPoint())) {
                    shape.setResizing(true);
                } else {
                    shape.setMoving(true);
                }
                
                // Bring to front
                shapes.remove(i);
                shapes.add(shape);
                break;
            }
        }
    }
    
    private void handleMouseRelease() {
        if (selectedShape != null) {
            selectedShape.setMoving(false);
            selectedShape.setResizing(false);
        }
    }
    
    private void handleMouseDrag(MouseEvent e) {
        if (selectedShape != null) {
            int dx = e.getX() - dragStart.x;
            int dy = e.getY() - dragStart.y;
            
            if (selectedShape.isResizing()) {
                selectedShape.resize(dx, dy);
            } else if (selectedShape.isMoving()) {
                selectedShape.move(dx, dy);
            }
            
            dragStart = e.getPoint();
            repaint();
        }
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            ResizableMovablePanel panel = new ResizableMovablePanel();
            panel.setVisible(true);
        });
    }
}

abstract class ResizableShape {
    protected int x, y, width, height;
    protected Color color;
    protected boolean moving = false;
    protected boolean resizing = false;
    protected static final int HANDLE_SIZE = 8;
    
    public ResizableShape(int x, int y, int width, int height, Color color) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        this.color = color;
    }
    
    public abstract void draw(Graphics2D g2d);
    public abstract boolean contains(Point p);
    
    public boolean isResizeHandle(Point p) {
        Rectangle handle = new Rectangle(x + width - HANDLE_SIZE, y + height - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE);
        return handle.contains(p);
    }
    
    public void move(int dx, int dy) {
        x += dx;
        y += dy;
    }
    
    public void resize(int dx, int dy) {
        width += dx;
        height += dy;
        // Ensure minimum size
        width = Math.max(width, 20);
        height = Math.max(height, 20);
    }
    
    public void setColor(Color color) {
        this.color = color;
    }
    
    public void setMoving(boolean moving) {
        this.moving = moving;
    }
    
    public void setResizing(boolean resizing) {
        this.resizing = resizing;
    }
    
    public boolean isMoving() {
        return moving;
    }
    
    public boolean isResizing() {
        return resizing;
    }
}

class ResizableRectangle extends ResizableShape {
    public ResizableRectangle(int x, int y, int width, int height, Color color) {
        super(x, y, width, height, color);
    }
    
    @Override
    public void draw(Graphics2D g2d) {
        // Draw the rectangle
        g2d.setColor(color);
        g2d.fillRect(x, y, width, height);
        
        // Draw border
        g2d.setColor(Color.BLACK);
        g2d.drawRect(x, y, width, height);
        
        // Draw resize handle if selected
        if (moving || resizing) {
            g2d.setColor(Color.RED);
            g2d.fillRect(x + width - HANDLE_SIZE, y + height - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE);
        }
    }
    
    @Override
    public boolean contains(Point p) {
        return new Rectangle(x, y, width, height).contains(p);
    }
}

class ResizableOval extends ResizableShape {
    public ResizableOval(int x, int y, int width, int height, Color color) {
        super(x, y, width, height, color);
    }
    
    @Override
    public void draw(Graphics2D g2d) {
        // Draw the oval
        g2d.setColor(color);
        g2d.fillOval(x, y, width, height);
        
        // Draw border
        g2d.setColor(Color.BLACK);
        g2d.drawOval(x, y, width, height);
        
        // Draw resize handle if selected
        if (moving || resizing) {
            g2d.setColor(Color.RED);
            g2d.fillRect(x + width - HANDLE_SIZE, y + height - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE);
        }
    }
    
    @Override
    public boolean contains(Point p) {
        // Simple ellipse collision detection
        double centerX = x + width / 2.0;
        double centerY = y + height / 2.0;
        double radiusX = width / 2.0;
        double radiusY = height / 2.0;
        
        double dx = p.x - centerX;
        double dy = p.y - centerY;
        
        return (dx * dx) / (radiusX * radiusX) + (dy * dy) / (radiusY * radiusY) <= 1.0;
    }
}
```

## Features of This Implementation:

1. **Shape Creation**:
   - Create rectangles or ovals by double-clicking
   - Select shape type from combo box
   - Choose fill color with color chooser

2. **Movement**:
   - Click and drag to move shapes
   - Selected shapes are brought to front

3. **Resizing**:
   - Drag the bottom-right handle to resize
   - Minimum size enforced (20x20 pixels)

4. **Visual Feedback**:
   - Red resize handle appears when selected
   - Black border around all shapes
   - Fill color can be customized

5. **User Interface**:
   - Control panel for shape selection and color
   - Clean drawing area with white background

## How to Enhance This Application:

1. **Add More Shape Types**:
```java
// Add to shapeSelector combo box
shapeSelector.addItem("Triangle");
shapeSelector.addItem("Line");

// Create corresponding shape classes
class ResizableTriangle extends ResizableShape {
    // Implement triangle-specific methods
}
```

2. **Add Undo/Redo Functionality**:
```java
// Use a stack to track changes
private Stack<List<ResizableShape>> history = new Stack<>();

// Save state before modifications
private void saveState() {
    history.push(new ArrayList<>(shapes));
}

// Undo action
private void undo() {
    if (!history.isEmpty()) {
        shapes = history.pop();
        repaint();
    }
}
```

3. **Add Save/Load Functionality**:
```java
// Save shapes to file
private void saveToFile(File file) {
    try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {
        oos.writeObject(shapes);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

// Load shapes from file
private void loadFromFile(File file) {
    try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
        shapes = (List<ResizableShape>) ois.readObject();
        repaint();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
```

4. **Add Rotation Capability**:
```java
// Add rotation field to ResizableShape
protected double rotation = 0;

// Modify draw methods to use AffineTransform
AffineTransform old = g2d.getTransform();
g2d.rotate(rotation, x + width/2, y + height/2);
// Draw shape normally
g2d.setTransform(old);
```

5. **Add Grid Snapping**:
```java
// Snap to grid when moving/resizing
private static final int GRID_SIZE = 10;

public void move(int dx, int dy) {
    x = ((x + dx) / GRID_SIZE) * GRID_SIZE;
    y = ((y + dy) / GRID_SIZE) * GRID_SIZE;
}
```

This implementation provides a solid foundation for creating a drawing application with resizable, movable, and fillable components in Java Swing. The architecture is designed to be easily extensible with additional features.

No comments: