在不可见之后使用.setVisible(true)后,Java JFrame将不会显示

时间:2014-07-31 19:58:01

标签: java swing user-interface

我将从下到上开始解释,这样您就可以真正了解我的目标,并更好地理解我的代码。

我正在创建一个库,让您捕获一个区域,无论捕获是gif动画还是图像。捕获完成后,库将返回包含ByteArrayInputStream的对象和createImage之类的util方法。

阅读本文时,您可以访问此库:https://github.com/BenBeri/WiseCapturer/

现在这是关于我的图书馆工作方式的一个虚拟示例:

您的应用程序使用capturer类创建一个bootstrap实例,并开始捕获:

public static void main(String[] args) throws AWTException {
    final Bootstrap b = new Bootstrap(new ScreenshotCapturer());
    b.beginCapture(new ScreenCaptureCallback() {
        @Override
        public void captureEnded(CapturedImage img) {
            b.beginCapture(new ScreenCaptureCallback() {
                @Override
                public void captureEnded(CapturedImage img) {
                    JFrame frame = new JFrame();
                    frame.add(new JLabel(new ImageIcon(img.getBufferedImage())));
                    frame.pack();
                    frame.setVisible(true);
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                }
            });
        }
    });
}

听众将返回CapturedImage,您可以使用它来做任何您想做的事情。 现在通过这个例子,这可以让你在完成后一次又一次地捕获两次,一旦完成,它将在JFrame窗口中显示第二次捕获。

现在我不是在谈论这个JFrame。

问题只发生在ScreeenshotCapturer,它可以与GifCapturer实例配合使用。

问题

完成第一次捕获后,第二个捕获JFrame透明窗口将不会出现,我不会在Windows工具栏中看到它,也不会在任何地方看到它,但应用程序仍在运行。

但是,正如我所说,如果我使用GifCapturer实例,它确实有用。

现在让我们调试我的库的工作方式:

Bootstrap构造函数:

/**
 * Bootstrap consturctor, let it bootstrap!
 * @param c
 *              Capturer instance
 * @throws AWTException
 */
public Bootstrap(Capturer c) throws AWTException {
    this.capturer = c;
}

现在Capturer类初始化,它是抽象类,是所有捕获器的相同构造函数:

public Capturer() throws AWTException {
    this.camera = new CaptureCamera(this);
}

这会创建一个新的捕获相机,这是我遇到问题的地方。 CaptureCamera的目的是拥有整个JFrame,透明大小与我的屏幕相同,并在其中包含负责选择矩形绘图的JPanel。

它的构造函数:

public CaptureCamera(Capturer c) throws AWTException {
    this.c = c;
    this.toolkit = Toolkit.getDefaultToolkit();
    this.screen = this.toolkit.getScreenSize();
    this.robot = new Robot();
    this.selector = new SelectionCamera();

    super.setSize(this.screen);
    super.setUndecorated(true);
    super.setBackground(new Color(255, 255, 255, 1));

    // Listeners for area selection
    super.addMouseListener(new SelectionAdapter(this, this.selector));
    super.addMouseMotionListener(new SelectionMotion(this, this.selector));

    super.add(this.selector);
}

好的,现在让我们来看看捕获是如何开始的。

bootstrap中的beginCapture方法:

/**
 * Starts capturing the screen, sends back a callback event with the
 * captured file.
 * 
 * The system saves a temporary file to send the file.
 * @param c
 *          Callback instance
 */
public void beginCapture(ScreenCaptureCallback c) {
    this.capturer.setCallback(c);
    this.capturer.beginSelection();
}

setCallback对此问题并不重要,因此beginSelection方法:

所有捕获者都一样

@Override
public void beginSelection() {
    super.init();
    this.setHotkeys();
    super.getCamera().startSelection();
}

startSelection方法(对于以后具有相同含义的笨重复名称而感到抱歉):

/**
 * Starts area selection event
 * @param c Capturer instance
 */
public void startSelection() {
    super.setVisible(true);
}

好的,这就是它应该让JFrame可见的地方,我之前尝试过打印并且显示正确,但JFrame在第二次尝试时没有显示。

现在框架可见,用户可以选择一个区域。 选择后,mouese适配器将执行startCapturing方法。

GifCapturer 中的

startCapturing

@Override
public void startCapturing(final int x, final int y, final int width, final int height) {
    this.border = new GifCaptureBorder(x, y, width, height);
    this.process = new TimerCaptureProcess(this, x, y, width, height);
    Timer timer = new Timer();
    timer.schedule(this.process, 0, 600);
}

` ScreenshotCapturer 中的startCapturing:

@Override
public void startCapturing(int x, int y, int width, int height) {
    Robot robot = super.getCamera().getRobot();
    BufferedImage image = robot.createScreenCapture(new Rectangle(x, y, width, height));
    super.disableSelectionFrame();
    try {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        ImageIO.write(image, "png", stream);
        super.setCaptureResult(stream);
        super.finish();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

现在在 GifCapturer 中,流程会更长,因为它实际上会启动Timer来截取每个60ms的每个帧的屏幕截图。

要完成捕获gif,请单击"输入",我使用JKeyMaster检测热键。 点击" ENTER"后,此方法将在GifCapturer

中执行
public void createGif() {
    super.disableSelectionFrame();

    AnimatedGifEncoder gif = new AnimatedGifEncoder();

    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    gif.start(stream);
    gif.setDelay(1000);

    this.border.updateProgress(10);

    for(int i = 0; i < this.frames.size(); i++) {
        gif.addFrame(this.frames.get(i));
    }

    this.border.updateProgress(50);

    gif.finish();

    super.setCaptureResult(stream);

    this.border.updateProgress(100);

    super.finish();
    this.border.setVisible(false);
    this.border = null;
}

这就是它,如果我将使用GifCapturer两次,一切正常,但如果我将使用ScreenshotCapturer两次,JFrame将不会第二次出现!

我不确定为什么,这可能是Swing中的一个错误吗?也许是因为GifCapturer需要更长时间才能使框架可见?

我做错了什么?

1 个答案:

答案 0 :(得分:2)

好的,据我所知,你遇到的问题是这个代码...

基本上,WiseCapturer API允许您拖动&#34;当您拨打beginCapture ...

时,屏幕上方会显示透明矩形
public static void main(String[] args) throws AWTException {
    final Bootstrap b = new Bootstrap(new ScreenshotCapturer());
    b.beginCapture(new ScreenCaptureCallback() {
        @Override
        public void captureEnded(CapturedImage img) {
            b.beginCapture(new ScreenCaptureCallback() {
                @Override
                public void captureEnded(CapturedImage img) {
                    JFrame frame = new JFrame();
                    frame.add(new JLabel(new ImageIcon(img.getBufferedImage())));
                    frame.pack();
                    frame.setVisible(true);
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                }
            });
        }
    });
}

你遇到的问题是在调用captureEnded时(对于外部捕获),内部捕获过程没有说明(并且你不能拖延&#34;透明选择矩形)...

这似乎是因为只要线程/事件队列WiseCapturer正在使用并且captureEnded事件未被允许完成就会阻塞...

如果我做某事......

try {
    final Bootstrap b = new Bootstrap(new ScreenshotCapturer());
    b.beginCapture(new ScreenCaptureCallback() {
        @Override
        public void captureEnded(CapturedImage img) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    b.beginCapture(new ScreenCaptureCallback() {
                        @Override
                        public void captureEnded(CapturedImage img) {
                            System.out.println("...");
                            JFrame frame = new JFrame();
                            frame.add(new JLabel(new ImageIcon(img.getBufferedImage())));
                            frame.pack();
                            frame.setVisible(true);
                            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                        }
                    });
                }
            });
            t.start();
        }
    });
    System.out.println("Hello");
} catch (AWTException exp) {
    exp.printStackTrace();
}

在外部Thread电话中开始新的captureEnded,我可以让它发挥作用......

另外,我不知道此API的线程安全规则,我还使用了SwingUtilities.invokeLater

try {
    final Bootstrap b = new Bootstrap(new ScreenshotCapturer());
    b.beginCapture(new ScreenCaptureCallback() {
        @Override
        public void captureEnded(CapturedImage img) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    b.beginCapture(new ScreenCaptureCallback() {
                        @Override
                        public void captureEnded(CapturedImage img) {
                            System.out.println("...");
                            JFrame frame = new JFrame();
                            frame.add(new JLabel(new ImageIcon(img.getBufferedImage())));
                            frame.pack();
                            frame.setVisible(true);
                            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                        }
                    });
                }
            });
        }
    });
    System.out.println("Hello");
} catch (AWTException exp) {
    exp.printStackTrace();
}

并且有它的工作......我也有点担心你为什么会这样做,但那只是我