# 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:
Post a Comment