Java的截屏生成Gif的小工具

Java的截屏生成Gif的小工具

    • 文章说明
    • 工具源码
    • 效果展示
    • 修正部分bug后的版本

文章说明

考虑到偶尔会有使用Gif动图进行演示截图的情况,选择自己制作了一款简单的截屏生成Gif的小工具

需要使用到hutool工具包

        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>5.8.15version>
        dependency>

主要用到的技术点有:
(1)Java自带的截屏函数
(2)hutool里的将图片转为gif图片的函数
(3)监听全局键盘事件
(4)Swing界面的简单绘制

工具源码

package com.boot.util;

import cn.hutool.core.img.gif.AnimatedGifEncoder;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author bbyh
 * @date 2023/12/19 13:59
 */
public class ScreenToGifFrame extends JFrame {
    private static final String IMG = "img";
    private static String RESULT_DIR = System.getProperty("user.dir") + File.separator + IMG;

    private static final List<String> RESULT_IMG_LIST = new ArrayList<>(10);
    private static Integer SEQUENCE = 1;
    private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd hh-mm-ss");
    private static final Integer COUNT_DOWN = 3;
    private static final JLabel RESULT_DISPLAY = new JLabel();

    private static final JFrame COUNT_FRAME = new JFrame();
    private static final JLabel COUNT_DOWN_LABEL = new JLabel("4");

    private static Boolean STATUS = false;
    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(5), new ThreadPoolExecutor.AbortPolicy());

    static {
        COUNT_FRAME.setSize(300, 150);
        COUNT_FRAME.setLocationRelativeTo(null);
        COUNT_FRAME.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        COUNT_FRAME.setTitle("倒计时");
        COUNT_FRAME.setLayout(new GridLayout(1, 1));
        COUNT_FRAME.add(COUNT_DOWN_LABEL);
        COUNT_DOWN_LABEL.setFont(new Font("楷体", Font.BOLD, 50));
        COUNT_DOWN_LABEL.setHorizontalAlignment(SwingConstants.CENTER);

        RESULT_DISPLAY.setFont(new Font("楷体", Font.BOLD, 14));

        File file = new File(IMG);
        if (!file.exists()) {
            if (!file.mkdir()) {
                RESULT_DISPLAY.setText(IMG + "文件夹,存放录屏临时文件夹创建失败");
                JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
            }
        }
    }

    public ScreenToGifFrame() {
        setTitle("将屏幕录制变为Gif");
        setSize(600, 150);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JLabel screenIntervalLabel = new JLabel("录屏间隔(ms):");
        JTextField screenIntervalInput = new JTextField(20);
        screenIntervalInput.setText("300");

        JButton startButton = new JButton("开始录制");
        JButton stopButton = new JButton("结束录制");
        JButton resultDirButton = new JButton("保存目录");
        JButton specificationButton = new JButton("使用说明");

        setLayout(new GridLayout(2, 1));
        JPanel screenIntervalPanel = new JPanel();
        JPanel buttonPanel = new JPanel();

        add(screenIntervalPanel);
        add(buttonPanel);

        screenIntervalPanel.add(screenIntervalLabel);
        screenIntervalPanel.add(screenIntervalInput);

        buttonPanel.add(startButton);
        buttonPanel.add(stopButton);
        buttonPanel.add(resultDirButton);
        buttonPanel.add(specificationButton);

        Font font = new Font("楷体", Font.BOLD, 20);
        screenIntervalLabel.setFont(font);
        screenIntervalInput.setFont(font);
        startButton.setFont(font);
        stopButton.setFont(font);
        resultDirButton.setFont(font);
        specificationButton.setFont(font);

        startButton.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                start(screenIntervalInput);
            }
        });

        stopButton.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                stop();
            }
        });

        Toolkit.getDefaultToolkit().addAWTEventListener(event -> {
            if (event.getID() == KeyEvent.KEY_PRESSED) {
                KeyEvent e = (KeyEvent) event;
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    stop();
                } else if (e.getKeyCode() == KeyEvent.VK_TAB) {
                    start(screenIntervalInput);
                }
            }
        }, AWTEvent.KEY_EVENT_MASK);

        JFileChooser chooser = new JFileChooser("./");
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

        resultDirButton.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
                    RESULT_DIR = chooser.getSelectedFile().getPath();
                    RESULT_DISPLAY.setText("您当前设置的输出目录为:" + RESULT_DIR);
                    JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
                    chooser.setVisible(false);
                }
            }
        });

        specificationButton.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                RESULT_DISPLAY.setText("在点击开始或者按下Tab键3秒后,按照指定间隔截屏,点击结束或者按下ESC键后生成Gif图片到指定目录下,默认在当前运行目录下");
                JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
            }
        });
    }

    private void stop() {
        STATUS = false;
        EXECUTOR.execute(this::generateGif);
        RESULT_DISPLAY.setText("正在生成Gif,请稍候");
        JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
    }

    private void start(JTextField screenIntervalInput) {
        COUNT_FRAME.setVisible(true);
        COUNT_FRAME.setAlwaysOnTop(true);
        for (int i = COUNT_DOWN; i > 0; i--) {
            COUNT_DOWN_LABEL.setText(i + "");
            COUNT_FRAME.update(COUNT_FRAME.getGraphics());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                throw new RuntimeException(ex);
            }
        }
        COUNT_FRAME.setVisible(false);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }
        EXECUTOR.execute(() -> {
            STATUS = true;
            int screenInterval = 0;
            boolean isScreenIntervalValid = true;
            try {
                screenInterval = Integer.parseInt(screenIntervalInput.getText());
            } catch (NumberFormatException ex) {
                isScreenIntervalValid = false;
            }
            if (!isScreenIntervalValid || screenInterval <= 0) {
                RESULT_DISPLAY.setText("设置的录屏间隔需要为正整数");
                JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
                return;
            }
            while (STATUS) {
                screenCapture();
                try {
                    Thread.sleep(screenInterval);
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
            }
        });
    }

    private void generateGif() {
        try {
            AnimatedGifEncoder animatedGifEncoder = new AnimatedGifEncoder();
            animatedGifEncoder.start(Files.newOutputStream(Paths.get(RESULT_DIR + File.separator + FORMAT.format(new Date()) + ".gif")));
            animatedGifEncoder.setDelay(500);
            animatedGifEncoder.setRepeat(0);
            for (String imgName : RESULT_IMG_LIST) {
                File file = new File(imgName);
                if (file.exists()) {
                    BufferedImage image = ImageIO.read(file);
                    animatedGifEncoder.addFrame(image);
                    if (!file.delete()) {
                        RESULT_DISPLAY.setText("录屏临时文件删除失败");
                        JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
                    }
                }
            }
            animatedGifEncoder.finish();
            RESULT_DISPLAY.setText("Gif生成成功");
            JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private void screenCapture() {
        try {
            Robot robot = new Robot();
            Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
            Rectangle rectangle = new Rectangle(dimension);
            BufferedImage bufferedImage = robot.createScreenCapture(rectangle);
            String captureName = RESULT_DIR + File.separator + "screenshot" + SEQUENCE + ".jpg";
            SEQUENCE++;
            RESULT_IMG_LIST.add(captureName);
            ImageIO.write(bufferedImage, "jpg", new File(captureName));
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void main(String[] args) {
        new ScreenToGifFrame().setVisible(true);
    }
}

效果展示

Java的截屏生成Gif的小工具_第1张图片

修正部分bug后的版本

之前的代码含有不少隐藏bug,我对此进行了一些简单的修正,下面是修正后的工具类源码

package com.boot.util;

import cn.hutool.core.img.gif.AnimatedGifEncoder;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author bbyh
 * @date 2023/12/19 13:59
 */
public class ScreenToGifFrame extends JFrame {
    private static final String IMG = "img";
    private static final String TEMP = "temp";
    private static String RESULT_DIR = System.getProperty("user.dir") + File.separator + IMG;
    private static final String RESULT_TEMP_DIR = System.getProperty("user.dir") + File.separator + IMG + File.separator + TEMP;

    private static final List<String> RESULT_IMG_LIST = new ArrayList<>(200);
    private static Integer SEQUENCE = 1;
    private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
    private static final Integer COUNT_DOWN = 3;
    private static final JLabel RESULT_DISPLAY = new JLabel();

    private static final JFrame COUNT_FRAME = new JFrame();
    private static final JLabel COUNT_DOWN_LABEL = new JLabel("4");

    private static Boolean STATUS = false;
    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(5), new ThreadPoolExecutor.AbortPolicy());

    static {
        COUNT_FRAME.setSize(300, 150);
        COUNT_FRAME.setLocationRelativeTo(null);
        COUNT_FRAME.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        COUNT_FRAME.setTitle("倒计时");
        COUNT_FRAME.setLayout(new GridLayout(1, 1));
        COUNT_FRAME.add(COUNT_DOWN_LABEL);
        COUNT_DOWN_LABEL.setFont(new Font("楷体", Font.BOLD, 50));
        COUNT_DOWN_LABEL.setHorizontalAlignment(SwingConstants.CENTER);

        RESULT_DISPLAY.setFont(new Font("楷体", Font.BOLD, 14));

        checkDir();
    }

    private static void checkDir() {
        File file = new File(IMG);
        if (!file.exists()) {
            if (!file.mkdir()) {
                RESULT_DISPLAY.setText(IMG + "文件夹,存放录屏临时文件夹创建失败");
                JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
            }
        }
        file = new File(IMG + File.separator + TEMP);
        if (!file.exists()) {
            if (!file.mkdir()) {
                RESULT_DISPLAY.setText(IMG + "文件夹,存放录屏临时文件夹创建失败");
                JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
            }
        }
    }

    public ScreenToGifFrame() {
        setTitle("将屏幕录制变为Gif");
        setSize(800, 150);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JLabel screenIntervalLabel = new JLabel("录屏间隔(ms):");
        JTextField screenIntervalInput = new JTextField(20);
        screenIntervalInput.setText("100");

        JButton startButton = new JButton("开始录制");
        JButton stopButton = new JButton("结束录制");
        JButton resultDirButton = new JButton("保存目录");
        JButton clearTempDirButton = new JButton("清空临时目录");
        JButton specificationButton = new JButton("使用说明");

        setLayout(new GridLayout(2, 1));
        JPanel screenIntervalPanel = new JPanel();
        JPanel buttonPanel = new JPanel();

        add(screenIntervalPanel);
        add(buttonPanel);

        screenIntervalPanel.add(screenIntervalLabel);
        screenIntervalPanel.add(screenIntervalInput);

        buttonPanel.add(startButton);
        buttonPanel.add(stopButton);
        buttonPanel.add(resultDirButton);
        buttonPanel.add(clearTempDirButton);
        buttonPanel.add(specificationButton);

        Font font = new Font("楷体", Font.BOLD, 20);
        screenIntervalLabel.setFont(font);
        screenIntervalInput.setFont(font);
        startButton.setFont(font);
        stopButton.setFont(font);
        resultDirButton.setFont(font);
        clearTempDirButton.setFont(font);
        specificationButton.setFont(font);

        startButton.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                start(screenIntervalInput);
            }
        });

        stopButton.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                stop();
            }
        });

        Toolkit.getDefaultToolkit().addAWTEventListener(event -> {
            if (event.getID() == KeyEvent.KEY_PRESSED) {
                KeyEvent e = (KeyEvent) event;
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    stop();
                } else if (e.getKeyCode() == KeyEvent.VK_TAB) {
                    start(screenIntervalInput);
                }
            }
        }, AWTEvent.KEY_EVENT_MASK);

        JFileChooser chooser = new JFileChooser("./");
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

        resultDirButton.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
                    RESULT_DIR = chooser.getSelectedFile().getPath();
                    RESULT_DISPLAY.setText("您当前设置的输出目录为:" + RESULT_DIR);
                    JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
                    chooser.setVisible(false);
                }
            }
        });

        clearTempDirButton.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                File dir = new File(RESULT_TEMP_DIR);
                if (dir.exists()) {
                    File[] files = dir.listFiles();
                    if (files != null) {
                        boolean deleteSuccess = true;
                        for (File file : files) {
                            if (!file.delete()) {
                                deleteSuccess = false;
                                RESULT_DISPLAY.setText("录屏临时文件删除失败");
                                JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
                            }
                        }
                        if (deleteSuccess) {
                        	RESULT_IMG_LIST.clear();
                            RESULT_DISPLAY.setText("录屏临时文件删除成功");
                            JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
                        }
                    }
                }
            }
        });

        specificationButton.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                RESULT_DISPLAY.setText("在点击开始或者按下Tab键3秒后,按照指定间隔截屏,点击结束或者按下ESC键后生成Gif图片到指定目录下,默认在当前运行目录下");
                JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
            }
        });
    }

    private void stop() {
        STATUS = false;
        EXECUTOR.execute(this::generateGif);
        RESULT_DISPLAY.setText("正在生成Gif,请稍候");
        JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
    }

    private void start(JTextField screenIntervalInput) {
        COUNT_FRAME.setVisible(true);
        COUNT_FRAME.setAlwaysOnTop(true);
        for (int i = COUNT_DOWN; i > 0; i--) {
            COUNT_DOWN_LABEL.setText(i + "");
            COUNT_FRAME.update(COUNT_FRAME.getGraphics());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                throw new RuntimeException(ex);
            }
        }
        COUNT_FRAME.setVisible(false);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }
        EXECUTOR.execute(() -> {
            STATUS = true;
            int screenInterval = 0;
            boolean isScreenIntervalValid = true;
            try {
                screenInterval = Integer.parseInt(screenIntervalInput.getText());
            } catch (NumberFormatException ex) {
                isScreenIntervalValid = false;
            }
            if (!isScreenIntervalValid || screenInterval <= 0) {
                RESULT_DISPLAY.setText("设置的录屏间隔需要为正整数");
                JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
                return;
            }
            SEQUENCE = 0;
            RESULT_IMG_LIST.clear();
            while (STATUS) {
                screenCapture();
                try {
                    Thread.sleep(screenInterval);
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
            }
        });
    }

    private void generateGif() {
        checkDir();

        try {
            AnimatedGifEncoder animatedGifEncoder = new AnimatedGifEncoder();
            animatedGifEncoder.start(Files.newOutputStream(Paths.get(RESULT_DIR + File.separator + FORMAT.format(new Date()) + ".gif")));
            animatedGifEncoder.setDelay(300);
            animatedGifEncoder.setRepeat(0);
            for (String imgName : RESULT_IMG_LIST) {
                File file = new File(imgName);
                if (file.exists()) {
                    BufferedImage image = ImageIO.read(file);
                    animatedGifEncoder.addFrame(image);
                }
            }
            animatedGifEncoder.finish();
            RESULT_DISPLAY.setText("Gif生成成功");
            JOptionPane.showMessageDialog(null, RESULT_DISPLAY);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private void screenCapture() {
        checkDir();

        try {
            Robot robot = new Robot();
            Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
            Rectangle rectangle = new Rectangle(dimension);
            BufferedImage bufferedImage = robot.createScreenCapture(rectangle);
            String captureName = RESULT_TEMP_DIR + File.separator + "screenshot" + System.currentTimeMillis() + SEQUENCE + ".jpg";
            SEQUENCE++;
            RESULT_IMG_LIST.add(captureName);
            ImageIO.write(bufferedImage, "jpg", new File(captureName));
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void main(String[] args) {
        new ScreenToGifFrame().setVisible(true);
    }
}

你可能感兴趣的:(java,python,windows)