核心介面
MouseListener:`mousePressed` (起點)、`mouseReleased` (終點)、`mouseClicked`、`mouseEntered`、`mouseExited`。
MouseMotionListener:`mouseDragged` (拖曳中)、`mouseMoved` (單純移動)。
完整範例:拖曳畫線
public class MouseDrawDemo extends JFrame
implements MouseListener, MouseMotionListener {
private int startX, startY, endX, endY;
private boolean dragging = false;
public MouseDrawDemo() {
addMouseListener(this);
addMouseMotionListener(this);
setSize(600, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public void mousePressed(MouseEvent e) {
startX = e.getX(); startY = e.getY();
dragging = true;
}
public void mouseReleased(MouseEvent e) {
endX = e.getX(); endY = e.getY();
dragging = false;
repaint(); // 確保最終線條繪製
}
public void mouseDragged(MouseEvent e) {
endX = e.getX(); endY = e.getY();
repaint(); // 即時回饋
}
// 未使用事件留空
public void mouseMoved(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
// 雙緩衝防閃爍
@Override
public void update(Graphics g) {
paint(g); // 直接呼叫 paint,JFrame 預設雙緩衝 (RepaintManager)
}
@Override
public void paint(Graphics g) {
super.paint(g); // 清除背景
g.setColor(Color.BLACK);
if (dragging) {
g.drawString("拖曳中: (" + endX + "," + endY + ")", 10, 20);
g.drawLine(startX, startY, endX, endY); // 即時預覽線
} else {
g.drawString("最後線段: (" + startX + "," + startY + ") -> (" + endX + "," + endY + ")", 10, 20);
g.drawLine(startX, startY, endX, endY); // 固定線
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(MouseDrawDemo::new);
}
}
進階:多線段/多圖形/橡皮筋效果
- 多線段:`List` 存儲所有完成線段,`paint` 迴圈繪製;拖曳時只畫預覽線。
- 橡皮筋矩形/橢圓:`mouseDragged` 計算 `Rectangle` (min/max X/Y),`paint` 畫 `drawRect`/`drawOval`。
- 雙緩衝手動實現 (舊版 JDK/JFrame 非預設):
private Image offscreen;
public void update(Graphics g) {
if (offscreen == null) offscreen = createImage(getWidth(), getHeight());
Graphics og = offscreen.getGraphics();
og.setColor(getBackground()); og.fillRect(0, 0, getWidth(), getHeight());
paint(og);
g.drawImage(offscreen, 0, 0, this);
}
事件處理最佳實踐
| 原則 | 說明 |
盡量用 MouseAdapter | 避免實現所有空方法,僅覆寫需要的 |
| 分離模型與視圖 | 滑鼠事件只更新資料模型 (如 List),`paint` 只負責渲染 |
節流 repaint() | 高頻拖曳可用 `Timer` 合併 16ms 重繪,避免事件風暴 |
使用 Graphics2D | 啟用抗鋸齒 `g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)` |