點擊按鈕 → 全螢幕顯示當前桌面截圖 → 滑鼠拖曳選取矩形區域 → 釋放滑鼠 → 完成區域截圖並顯示於主視窗 → ESC/右鍵取消。
Robot.createScreenCapture 實際截圖import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
public class QQScreenCaptureDemo extends JFrame {
private JLabel previewLabel;
public QQScreenCaptureDemo() {
setTitle("QQ 風格截圖 Demo");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400, 300);
setLocationRelativeTo(null);
JButton btn = new JButton("開始截圖 (右鍵取消)");
btn.addActionListener(e -> startCapture());
previewLabel = new JLabel("截圖預覽區");
previewLabel.setHorizontalAlignment(SwingConstants.CENTER);
previewLabel.setBorder(BorderFactory.createLineBorder(Color.GRAY));
JScrollPane scroll = new JScrollPane(previewLabel);
add(btn, BorderLayout.NORTH);
add(scroll, BorderLayout.CENTER);
setVisible(true);
}
private void startCapture() {
// 在 EDT 外執行耗時截圖,避免凍結 UI
new Thread(() -> {
try {
// 1. 先截全螢幕作為遮罩背景
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
BufferedImage fullImg = ScreenImage.capture(0, 0, screen.width, screen.height);
// 2. 建立全螢幕無邊框視窗
JFrame overlay = new JFrame();
overlay.setUndecorated(true);
overlay.setBounds(0, 0, screen.width, screen.height);
overlay.setAlwaysOnTop(true);
JLabel imgLabel = new JLabel(new ImageIcon(fullImg));
imgLabel.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
overlay.add(imgLabel);
// 3. 滑鼠選區邏輯
final Point start = new Point();
final Rectangle selection = new Rectangle();
boolean[] dragging = {false};
imgLabel.addMouseListener(new MouseAdapter() {
@Override public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) { overlay.dispose(); return; }
start.setLocation(e.getPoint());
dragging[0] = true;
}
@Override public void mouseReleased(MouseEvent e) {
if (!dragging[0]) return;
dragging[0] = false;
Point end = e.getPoint();
selection.setFrame(
Math.min(start.x, end.x), Math.min(start.y, end.y),
Math.abs(end.x - start.x), Math.abs(end.y - start.y)
);
overlay.dispose(); // 先關閉遮罩
// 4. 截取選區
if (selection.width > 0 && selection.height > 0) {
BufferedImage sub = ScreenImage.capture(
selection.x, selection.y, selection.width, selection.height);
SwingUtilities.invokeLater(() ->
previewLabel.setIcon(new ImageIcon(sub)));
}
}
});
imgLabel.addMouseMotionListener(new MouseMotionAdapter() {
@Override public void mouseDragged(MouseEvent e) {
if (!dragging[0]) return;
// 這裡可繪製即時選區框(需自行在 imgLabel 上繪製或用 GlassPane)
// 為簡潔略過,實際專案建議用 JXLayer 或自定義 GlassPane 繪製虛線框
}
});
overlay.setVisible(true);
} catch (Exception ex) {
ex.printStackTrace();
SwingUtilities.invokeLater(() ->
JOptionPane.showMessageDialog(this, "截圖失敗:" + ex.getMessage()));
}
}).start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(QQScreenCaptureDemo::new);
}
}
class ScreenImage {
public static BufferedImage capture(int x, int y, int w, int h) throws AWTException {
Robot robot = new Robot();
Rectangle rect = new Rectangle(x, y, w, h);
return robot.createScreenCapture(rect);
}
}
| 技術 | 說明 |
|---|---|
| Robot.createScreenCapture | 跨平台螢幕截圖,返回 BufferedImage,需處理 AWTException |
| 無邊框全螢幕 JFrame | setUndecorated(true) + setBounds(screen) + setAlwaysOnTop(true) |
| 滑鼠座標轉換 | 元件座標 = 螢幕座標(因 JFrame 佔全螢幕),直接用 e.getPoint() |
| 選區正規化 | Math.min/max + Math.abs 確保 width/height 為正 |
| EDT 線程安全 | 截圖耗時操作放入背景執行緒,UI 更新用 SwingUtilities.invokeLater |
JLayer 或 GlassPane 在拖曳時繪製虛線矩形(Graphics2D.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, new float[]{5,5}, 0)))GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() 遍歷所有螢幕,合成虛擬桌面截圖Robot 截圖可能偏移,需配合 sun.java2d.uiScale 或 GDK_SCALE 處理Java 實作桌面截圖核心在於 Robot 截圖 + 無邊框全螢幕遮罩 + 滑鼠選區幾何計算。本範例提供最小可行性架構,生產級工具需在選區繪製、多螢幕、高 DPI、編輯工具上深度打磨。參考開源專案 ScreenCapture、ShareX (C#) 設計可加速開發。