Java JFrame 不可见后使用 .setVisible(true) 不会显示 [英] Java JFrame won't show up after using .setVisible(true) after being invisible

查看:97
本文介绍了Java JFrame 不可见后使用 .setVisible(true) 不会显示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我将从下到上开始解释,这样您就会真正理解我想要做什么,并更好地理解我的代码.

I'll start explaining from the bottom to top, so you will actually understand what I was trying to do, and understand my code better.

我正在创建一个库,让您可以捕捉一个区域,无论捕捉的是 gif 动画还是图像.捕获完成后,库将返回一个对象,其中包含 ByteArrayInputStreamcreateImage 等 util 方法.

I am creating a library, that let's you capture an area, whether if the capture is a gif animation or an image. After capturing is finished, the library will return an object that contains ByteArrayInputStream and util methods like createImage etc.

阅读本文时,您可以访问此处的图书馆:https://github.com/BenBeri/WiseCapturer/

While reading this, you can have access to the library here: https://github.com/BenBeri/WiseCapturer/

现在这是一个关于我的库如何工作的虚拟示例:

Now this is a dummy example on how my library works:

您的应用程序使用捕获器类创建引导程序实例,并开始捕获:

Your application creates an instance of bootstrap with the capturer class, and begins a capture:

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 窗口中显示第二次捕获.

The listener will return CapturedImage which you can use to do whatever you wish to. Now with this example, this should let you capture twice, once and again after you're done, and once it's done, it will show the 2nd capture in a JFrame window.

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

Now I am not talking about this JFrame.

这个问题只发生在 ScreeenshotCapturer 上,它可以在 GifCapturer 实例中正常工作.

The problem only occurs with ScreeenshotCapturer, it will work fine with GifCapturer instance.

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

After finishing the first capture, the second capture JFrame transparent window will not come up, I don't see it in the windows toolbar, nor anywhere, but the application still runs.

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

However, as I said it does work if I use GifCapturer instance.

现在让我们调试一下我的库是如何工作的:

Now let's debug how my library works:

自举构造函数:

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

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

Now the Capturer class initializes, it's the abstract class, same constructor for all capturers:

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

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

This creates a new capture camera, which is where I am having the problem. The purpose of CaptureCamera is to have the whole JFrame, transparent sized the same as my screen, and contain in it the JPanel that is responsible to do the selection rectangle drawing.

它的构造函数:

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);
}

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

Okay, now let's take a look at how the capturing begins.

bootstrap 中的 beginCapture 方法:

the beginCapture method in bootstrap:

/**
 * 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 方法:

setCallback is not really important for this problem, so beginSelection method:

所有捕获器都相同

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

startSelection 方法(对不起,后面有相同含义的哑重名):

startSelection method (sorry for the dumb duplicated names with the same meaning ill change later):

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

好的,这是它应该使 JFrame 可见的地方,我之前尝试过打印并且显示为真,但是第二次尝试时 JFrame 没有显示.

Okay, this is where it should make the JFrame visible, I've tried printing before and it showed true, but the JFrame didn't show on the second attempt.

现在框架是可见的,用户可以选择一个区域.选择后,鼠标适配器将执行 startCapturing 方法.

Now the frame is visible, and the user can select an area. once selected, the mouese adapter will execute startCapturing method.

startCapturingGifCapturer 中:

@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 中的开始捕获:

@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 中的过程更长,因为它实际上启动了一个 Timer60ms 对每一帧进行截图.

Now in GifCapturer the process is longer, because it actually starts a Timer to take screenshots of every frame every 60ms.

要完成捕获 gif,请单击输入",我使用 JKeyMaster 来检测热键.点击ENTER"后,这个方法会在GifCapturer

To finish capturing a gif, you click on "enter", I used JKeyMaster to detect hotkeys. after clicking "ENTER", this method will be executed in 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;
}

差不多了时间!

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

I am not really sure why, could this be a bug in Swing? Maybe because GifCapturer takes longer before it makes the frame visible?

我做错了什么?

推荐答案

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

Okay, so from what I understand the problem you are having is with this code...

基本上,当您调用 beginCapture...

Basically, the WiseCapturer API allows you to "drag" transparent rectangle over the screen when you call 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 被调用时(对于外部捕获),内部捕获过程没有说明(并且您不能拖动"透明选择矩形)...

The problem you're having is when captureEnded is called (for the outer capture), the inner capture process is not stating (and you can't "drag" the transparent selection rectangle)...

这似乎是因为当 WiseCapturer 正在使用线程/事件队列时您正在阻塞并且 captureEnded 事件不允许完成...

This seems to be because you are blocking whenever thread/event queue WiseCapturer is using and the captureEnded event as not been allowed to finish...

如果我做类似...

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();
}

在外部 captureEnded 调用中启动一个新的 Thread,我可以让它工作......

Which starts a new Thread within the outer captureEnded call, I can get it to work...

另外,不知道这个API的线程安全规则,我也用了SwingUtilities.invokeLater

Also, not knowing the Thread safety rules for this API, I also used 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();
}

如果它有效......我也有点奇怪你为什么要这样做,但这只是我

And had it work...I'm also a little weirded out about why you would do this, but that's just me

这篇关于Java JFrame 不可见后使用 .setVisible(true) 不会显示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆