Search code examples
javaimageswingjframescreenshot

How to determine the drawing state of a JFrame?


I want to take a screenshot of Java JFrame.

Sometimes the frame rendering is not finished, but the screenshot operation is complete.But sometimes he does not appear this situation, as if the root computer performance has a certain relationship!

Situation screenshots <--- Situation screenshots

I think I can delay the screenshot with Thread.sleep(),but can't determine sleep() length of time.

Java code:

package com.richardtang.apktesttool;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class Test2 {

    public static Robot robot;

    static {
        try {
            robot = new Robot();
        } catch (AWTException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        JPanel jPanel = new JPanel();
        for (int i = 1; i < 1000; i++) {
            jPanel.add(new JLabel(String.valueOf(i)));
        }

        JFrame jFrame = new JFrame();
        jFrame.setSize(500, 500);
        jFrame.setContentPane(jPanel);
        jFrame.setVisible(true);

        BufferedImage img = captureFrame(jFrame);
        try {
            ImageIO.write(img, "png", new File("/tmp/1.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static BufferedImage captureFrame(JFrame window) {
        window.setAlwaysOnTop(true);
        Point windowPoint = window.getLocation();
        Dimension windowSize = window.getSize();
        Rectangle rectangle = new Rectangle(
                (int) windowPoint.getX(),
                (int) windowPoint.getY(),
                windowSize.width,
                windowSize.height
        );
        return robot.createScreenCapture(rectangle);
    }
}

Solution

  • You could use SwingUtilities invokeLater. As per the documentation, invokeLater causes doRun.run() to be executed asynchronously on the AWT event dispatching thread. This will happen after all pending AWT events have been processed. Thus, the screenshot will be captured after the frame/panel drawing is completed.

    Update

    As @camickr correctly pointed out, ensure that your GUI is also created in a similar way i.e., on the AWT Event Dispatch Thread (EDT). Example below.

    import javax.imageio.ImageIO;
    import javax.swing.*;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    
    public class ScreenCapture {
        JFrame jFrame = null;
    
        public void createGUI() {
            JPanel jPanel = new JPanel();
            for (int i = 1; i < 1000; i++) {
                jPanel.add(new JLabel(String.valueOf(i)));
            }
    
            jFrame = new JFrame();
            jFrame.setSize(500, 500);
            jFrame.setContentPane(jPanel);
            jFrame.setVisible(true);
        }
    
        public void capture() {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    try {
                        BufferedImage img = captureFrame(jFrame);
                        ImageIO.write(img, "png", new File("tmp/1.png"));
                    } catch (AWTException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    
        public static void main(String[] args) {
            ScreenCapture sc = new ScreenCapture();
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    sc.createGUI();
                    sc.capture();
                }
            });
        }
    
        public static BufferedImage captureFrame(JFrame window) throws AWTException {
            Robot robot = new Robot();
            window.setAlwaysOnTop(true);
            Point windowPoint = window.getLocation();
            Dimension windowSize = window.getSize();
            Rectangle rectangle = new Rectangle((int) windowPoint.getX(), (int) windowPoint.getY(), windowSize.width,
                    windowSize.height);
            return robot.createScreenCapture(rectangle);
        }
    }