Java 實現屏幕截圖主要依賴 java.awt.Robot 類的 createScreenCapture() 方法。本文提供三種遞進方案:基礎全屏截圖、Swing 交互式區域截圖、完整桌面級截圖工具,涵蓋從單行代碼到生產級應用的完整演進。
// 核心三行代碼
Robot robot = new Robot();
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage image = robot.createScreenCapture(screenRect);
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class SimpleScreenCapture {
public static void captureFullScreen(String savePath) throws Exception {
// 1. 獲取屏幕尺寸
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle screenRect = new Rectangle(screenSize);
// 2. 截圖
Robot robot = new Robot();
BufferedImage image = robot.createScreenCapture(screenRect);
// 3. 保存為 PNG
File outputFile = new File(savePath);
ImageIO.write(image, "png", outputFile);
System.out.println("截圖已保存至:" + outputFile.getAbsolutePath());
}
public static void main(String[] args) {
try {
captureFullScreen("screenshot_" + System.currentTimeMillis() + ".png");
} catch (Exception e) {
e.printStackTrace();
}
}
}
核心思路:全屏截圖 → 顯示於無邊框 JDialog → 滑鼠拖拽選區 → 截取子圖 → 返回。
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
public class RegionCaptureTool {
private final Robot robot;
private final JDialog dialog;
private final JLabel imageLabel;
private BufferedImage fullScreenImage;
private BufferedImage pickedImage;
// 選區座標
private int x1, y1, x2, y2;
private boolean isFirstPoint = true;
public RegionCaptureTool() throws AWTException {
robot = new Robot();
// 無邊框、全屏、置頂對話框
dialog = new JDialog();
dialog.setUndecorated(true);
dialog.setAlwaysOnTop(true);
dialog.setModal(true);
dialog.setSize(Toolkit.getDefaultToolkit().getScreenSize());
dialog.setLocation(0, 0);
dialog.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
imageLabel = new JLabel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (fullScreenImage != null) {
g.drawImage(fullScreenImage, 0, 0, null);
}
// 繪製選區矩形
if (x2 > x1 && y2 > y1) {
g.setColor(Color.RED);
g.drawRect(x1, y1, x2 - x1, y2 - y1);
}
// 繪製十字光標
g.setColor(Color.CYAN);
g.drawLine(x1, 0, x1, getHeight());
g.drawLine(0, y1, getWidth(), y1);
}
};
imageLabel.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
x1 = x2 = e.getX();
y1 = y2 = e.getY();
isFirstPoint = false;
}
@Override
public void mouseReleased(MouseEvent e) {
// 確認選區,截取子圖
int minX = Math.min(x1, x2), minY = Math.min(y1, y2);
int w = Math.abs(x2 - x1), h = Math.abs(y2 - y1);
if (w > 5 && h > 5) { // 忽略點擊
pickedImage = fullScreenImage.getSubimage(minX, minY, w, h);
}
dialog.setVisible(false);
dialog.dispose();
}
});
imageLabel.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
x2 = e.getX(); y2 = e.getY();
imageLabel.repaint();
}
@Override
public void mouseMoved(MouseEvent e) {
x1 = x2 = e.getX(); y1 = y2 = e.getY();
imageLabel.repaint();
}
});
dialog.add(imageLabel);
}
public BufferedImage capture() {
// 1. 截全屏
fullScreenImage = robot.createScreenCapture(
new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())
);
imageLabel.setIcon(new ImageIcon(fullScreenImage));
x1 = y1 = x2 = y2 = 0;
isFirstPoint = true;
// 2. 顯示對話框等待用戶選區
dialog.setVisible(true);
return pickedImage; // 可能為 null(用戶取消或點擊)
}
public void save(BufferedImage img, File file) throws Exception {
if (img != null) ImageIO.write(img, "png", file);
}
// 測試主程式
public static void main(String[] args) {
try {
RegionCaptureTool tool = new RegionCaptureTool();
BufferedImage img = tool.capture();
if (img != null) {
tool.save(img, new File("region_capture.png"));
System.out.println("區域截圖完成");
} else {
System.out.println("用戶取消截圖");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
完整代碼較長(約 400 行),此處給出核心架構與關鍵模組,完整可運行版本請參考附件或 GitHub 同款開源項目 java-screen-captor。
| 模組 | 關鍵技術點 |
|---|---|
| 系統托盤 | SystemTray + TrayIcon + PopupMenu,支援最小化到托盤、右鍵菜單操作 |
| 多圖管理 | JTabbedPane + 自定義 PicPanel,每張圖獨立標籤頁,含保存/複製/關閉按鈕 |
| 區域選取 | 無邊框 JFrame 全屏覆蓋 + MouseListener/MotionListener 繪製選區、八向縮放手柄 |
| 圖片持久化 | ImageIO.write 支援 PNG/JPG/BMP/GIF,JFileChooser + FileFilter 選擇格式 |
| 剪貼板 | Toolkit.getSystemClipboard().setContents(Transferable),實現 DataFlavor.imageFlavor |
| 批量保存 | SwingWorker 後台線程 + JProgressBar 顯示進度,避免阻塞 EDT |
| 外觀切換 | UIManager.setLookAndFeel 切換 System/CrossPlatform L&F |
public void copyToClipboard(BufferedImage image) {
Transferable trans = new Transferable() {
@Override public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] { DataFlavor.imageFlavor };
}
@Override public boolean isDataFlavorSupported(DataFlavor flavor) {
return DataFlavor.imageFlavor.equals(flavor);
}
@Override public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
if (isDataFlavorSupported(flavor)) return image;
throw new UnsupportedFlavorException(flavor);
}
};
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(trans, null);
}
enum ResizeDir {
NW(Cursor.NW_RESIZE_CURSOR), N(Cursor.N_RESIZE_CURSOR), NE(Cursor.NE_RESIZE_CURSOR),
E(Cursor.E_RESIZE_CURSOR), SE(Cursor.SE_RESIZE_CURSOR), S(Cursor.S_RESIZE_CURSOR),
SW(Cursor.SW_RESIZE_CURSOR), W(Cursor.W_RESIZE_CURSOR),
MOVE(Cursor.MOVE_CURSOR), DEFAULT(Cursor.DEFAULT_CURSOR);
final Cursor cursor;
ResizeDir(int cursorType) { this.cursor = Cursor.getPredefinedCursor(cursorType); }
}
// 在 mouseMoved 中判斷滑鼠落在哪個手柄矩形,設置對應游標與 resize 方向
// mouseDragged 中根據方向修改 x1/y1/x2/y2,repaint()
Toolkit.getScreenSize() 僅返回主屏尺寸。多屏需用 GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() 遍歷合併邊界。-Dsun.java2d.uiScale.enabled=true 或配置 dpiAware 清單。HeadlessException。需安裝 Xvfb 或使用 X11 虛擬顯示器。flush() 或 dispose() 會導致 Heap OOM。用完即釋放:image.flush()。| 場景 | 推薦方案 | 關鍵類 |
|---|---|---|
| 自動化測試、定時截圖、無 UI | 方案一 | Robot + ImageIO |
| 桌面應用集成「選區截圖」 | 方案二 | Robot + JDialog + MouseListener |
| 獨立截圖工具、需托盤、多圖、剪貼板 | 方案三 | 完整 Swing 應用架構 |
掌握 Robot.createScreenCapture() 核心 API,配合 Swing 事件模型與圖片 I/O,即可構建從簡單到專業的 Java 截圖解決方案。