【JAVAFX】实现屏幕指定区域截图,带尺寸显示

关键思路

启动应用 -> 创建全屏透明窗口 -> 监听鼠标事件 -> 拖动绘制选区 -> 释放鼠标
↓ ↑ ↓
隐藏窗口 -> 截取选区图像 -> 转换格式 -> 复制到剪贴板 -> 关闭窗口

具体代码

最新代码请参考链接https://gitee.com/likexiang/source-code-records/blob/master/JAVA/ScreenCapture.java

package sample;

// JavaFX核心库导入
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

// AWT相关库导入(用于截图和剪贴板操作)
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.image.BufferedImage;
import java.io.IOException;

// JavaFX快捷键组合支持
import javafx.scene.input.KeyCombination;

/**
 * 屏幕截图工具 - 支持区域选择并复制到剪贴板
 * 主要功能:
 * 1. 全屏透明覆盖层
 * 2. 鼠标拖拽选择矩形区域
 * 3. 实时显示选区尺寸
 * 4. 截图复制到系统剪贴板
 * 5. ESC键退出截图模式
 */
public class ScreenCapture extends Application {

    // 成员变量声明
    private Point2D origin;        // 记录鼠标按下时的起始坐标
    private Pane root;             // 根布局容器
    private Canvas canvas;         // 用于绘制选择框的画布
    private Label sizeLabel;       // 显示选区尺寸的标签
    private GraphicsContext gc;    // 画布绘图上下文
    private Stage primaryStage;    // 主舞台(窗口)

    /**
     * JavaFX应用入口方法
     * @param primaryStage 主舞台对象
     */
    @Override
    public void start(Stage primaryStage) {
        this.primaryStage = primaryStage;
        setupUI();          // 初始化UI组件
        setupStage();       // 配置舞台属性
        setupEventHandlers(); // 设置事件监听
    }

    /**
     * 初始化用户界面组件
     */
    private void setupUI() {
        root = new Pane();
        // 创建覆盖整个屏幕的Canvas
        canvas = new Canvas(getTotalScreenWidth(), getTotalScreenHeight());
        gc = canvas.getGraphicsContext2D();  // 获取绘图上下文

        // 初始化尺寸标签样式
        sizeLabel = new Label();
        sizeLabel.setTextFill(Color.WHITE);  // 白色文字
        // 半透明黑色背景(rgba最后0.5表示50%透明度)
        sizeLabel.setStyle("-fx-background-color: rgba(0, 0, 0, 0.1); -fx-padding: 2 5;");

        // 将组件添加到根容器
        root.getChildren().addAll(canvas, sizeLabel);
    }

    /**
     * 配置舞台属性
     */
    private void setupStage() {
        // 创建透明场景
        Scene scene = new Scene(root);

        // 舞台初始化设置
        primaryStage.initStyle(StageStyle.TRANSPARENT);  // 透明无边框样式
        primaryStage.setScene(scene);           // 设置场景到舞台
        primaryStage.setOpacity(0.2);
        primaryStage.setX(0);
        primaryStage.setY(0);
        primaryStage.show();                    // 显示窗口
    }

    /**
     * 设置事件处理器
     */
    private void setupEventHandlers() {
        // 鼠标按下事件:记录起始点
        root.setOnMousePressed(e -> {
            if (e.getButton() == MouseButton.PRIMARY) {  // 左键点击
                origin = new Point2D(e.getScreenX(), e.getScreenY());
            }
        });

        // 鼠标拖动事件:实时绘制选择框
        root.setOnMouseDragged(e -> {
            if (origin != null) {
                clearCanvas();  // 清除旧绘制
                drawSelectionBox(origin, new Point2D(e.getScreenX(), e.getScreenY()));
                updateSizeLabel(origin, new Point2D(e.getScreenX(), e.getScreenY()));
            }
        });

        // 鼠标释放事件:执行截图操作
        root.setOnMouseReleased(e -> {
            if (origin != null) {
                try {
                    captureSelectedArea(origin, new Point2D(e.getScreenX(), e.getScreenY()));
                } catch (AWTException ex) {
                    ex.printStackTrace();
                }
                primaryStage.close();  // 关闭窗口
            }
        });

        // 键盘事件:ESC键退出
        root.setOnKeyPressed(e -> {
            if (e.getCode() == KeyCode.ESCAPE) {
                primaryStage.close();
            }
        });
    }

    /**
     * 绘制选择框和半透明遮罩
     * @param start 起点坐标
     * @param end   终点坐标
     */
    private void drawSelectionBox(Point2D start, Point2D end) {
        // 计算选区坐标和尺寸
        double x = Math.min(start.getX(), end.getX());
        double y = Math.min(start.getY(), end.getY());
        double width = Math.abs(end.getX() - start.getX());
        double height = Math.abs(end.getY() - start.getY());

        // 绘制半透明填充(20%不透明度)
        gc.setFill(Color.rgb(0, 0, 0, 0.6));
        gc.fillRect(x, y, width, height);

        // 绘制红色边框
        gc.setStroke(Color.RED);
        gc.setLineWidth(1);
        // +0.5偏移量用于消除抗锯齿导致的模糊
        gc.strokeRect(x + 0.5, y + 0.5, width - 1, height - 1);
    }

    /**
     * 更新尺寸标签的位置和内容
     * @param start 起点坐标
     * @param end   终点坐标
     */
    private void updateSizeLabel(Point2D start, Point2D end) {
        // 计算宽高
        int width = (int) Math.abs(end.getX() - start.getX());
        int height = (int) Math.abs(end.getY() - start.getY());
        sizeLabel.setText(width + " x " + height);

        // 将标签定位在选区右下角
        double labelX = Math.max(start.getX(), end.getX()) - sizeLabel.getWidth() - 5;  // 右侧留5像素
        double labelY = Math.max(start.getY(), end.getY()) - sizeLabel.getHeight() - 5; // 底部留5像素
        sizeLabel.setLayoutX(labelX);
        sizeLabel.setLayoutY(labelY);
    }

    /**
     * 捕获选定区域并复制到剪贴板
     * @param start 起点坐标
     * @param end   终点坐标
     */
    private void captureSelectedArea(Point2D start, Point2D end) throws AWTException {
        // 计算截图区域参数
        int x = (int) Math.min(start.getX(), end.getX());
        int y = (int) Math.min(start.getY(), end.getY());
        int width = (int) Math.abs(end.getX() - start.getX());
        int height = (int) Math.abs(end.getY() - start.getY());

        if (width > 0 && height > 0) {
            primaryStage.hide();  // 隐藏窗口避免影响截图

            try {
                Robot robot = new Robot();  // 创建AWT机器人
                // 截取屏幕区域
                BufferedImage screenshot = robot.createScreenCapture(
                        new java.awt.Rectangle(x, y, width, height));

                screenshot = convertToARGB(screenshot);  // 转换图像格式
                copyImageToClipboard(screenshot);        // 复制到剪贴板
                System.out.println("截图已复制到剪贴板,尺寸:" + width + "x" + height);
            } finally {
                primaryStage.close();  // 确保窗口关闭
            }
        }
    }

    /**
     * 转换图像为ARGB格式(支持透明度)
     * @param src 原始图像
     * @return 转换后的图像
     */
    private static BufferedImage convertToARGB(BufferedImage src) {
        // 创建新图像(TYPE_INT_ARGB支持透明度)
        BufferedImage dest = new BufferedImage(
                src.getWidth(),
                src.getHeight(),
                BufferedImage.TYPE_INT_ARGB
        );
        // 绘制原始图像到新图像
        Graphics2D g = dest.createGraphics();
        g.drawImage(src, 0, 0, null);
        g.dispose();
        return dest;
    }

    /**
     * 复制图像到系统剪贴板
     * @param image 要复制的图像
     */
    private void copyImageToClipboard(BufferedImage image) {
        TransferableImage transferable = new TransferableImage(image);  // 创建可传输对象
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); // 获取剪贴板

        try {
            clipboard.setContents(transferable, null);  // 设置剪贴板内容
        } catch (IllegalStateException e) {
            System.err.println("剪贴板访问失败: " + e.getMessage());
        }
    }

    /**
     * 自定义可传输图像类(实现剪贴板传输协议)
     */
    private static class TransferableImage implements Transferable {
        private final BufferedImage image;

        public TransferableImage(BufferedImage image) {
            this.image = image;
        }

        @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)) {
                throw new UnsupportedFlavorException(flavor);  // 抛出格式不支持异常
            }
            return image;  // 返回图像对象
        }
    }

    //============ 辅助方法 ============//

    /**
     * 清空画布内容
     */
    private void clearCanvas() {
        gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
    }

    /**
     * 获取多显示器总宽度
     */
    private double getTotalScreenWidth() {
        return Screen.getScreens().stream()
                .mapToDouble(s -> s.getBounds().getMaxX())
                .max()
                .orElse(0);
    }

    /**
     * 获取多显示器总高度
     */
    private double getTotalScreenHeight() {
        return Screen.getScreens().stream()
                .mapToDouble(s -> s.getBounds().getMaxY())
                .max()
                .orElse(0);
    }

    /**
     * 程序入口
     */
    public static void main(String[] args) {
        launch(args);  // 启动JavaFX应用
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值