Снимок экрана Java - JFrame на секунду рисует старый компонент и исчезает, даже если все компоненты были удалены и перекрашены

1

Прежде чем вы прочитаете, это будет информативным: Java JFrame не появится после использования.setVisible(true) после невидимости

Здравствуйте, я работаю над библиотечным API, который позволяет вам захватить область экрана и возвращает вам класс, содержащий методы ByteArrayInputStream и утилиты, такие как createBufferedImage, createFile и т.д.

В основном вы создаете экземпляр Bootstrap и передаете тип захвата, который вы хотите в качестве зависимости (ScreenshotCapturer или GifCapturer):

Bootstrap b = new Bootstrap(new ScreenshotCapturer());

И метод beginCapture получает объект, который реализует ScreenCaptureCallback который является событием обратного вызова, к которому будет передан полученный результат.

Это короткий фон.

Теперь, когда вы используете метод beginCapture, в основном он создает новый экземпляр SelectionCamera, это в основном компонент, который выделяет область выбора, которую вы выбираете при перетаскивании мыши, и обновляет слушателей.

после созданного экземпляра он вызывает super.setVisible(true);

После того, как этот метод будет вызван, будет отображаться рамка, а также показать старый раскрашенный экран как 600-500 миллисекунд, я не совсем уверен, но он так быстро исчезает.

Взгляните на этот живой пример:

Примечание используйте параметр видео, иначе вы не увидите, что я вижу, поскольку gif слишком медленно, чтобы показать его!

http://gyazo.com/d2f0432ada37842966b42dfd87be4240

Вы можете увидеть, как я снова нажму Скриншот, он показывает старую выделенную область и быстро исчезает. (кстати, рамка, которую вы видите в gif, не входит в приложение, просто используется для использования в качестве примера).

Процесс захвата изображения.

Шаг 1

beginCapture:

public void beginCapture(final ScreenCaptureCallback c) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            capturer.setCallback(c);
            capturer.beginSelection();

        }
    });
}

Шаг 2

beginSelection в классе Capturer (ScreenshotCapturer расширяет Capturer (abstract)

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

Шаг 3

CaptureCamera#startSelection()

public void startSelection() {
    super.getContentPane().removeAll();
    super.getContentPane().repaint();

    super.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));

    this.selector = new SelectionCamera();
    this.selectionMosueAdapter.updateCamera(this.selector);
    this.selectionMouseMotion.updateCamera(this.selector);

    super.add(this.selector);

    super.setVisible(true);
    super.repaint();
    super.getContentPane().repaint();
}

Шаг 4

Пользователь выбирает область, и слушатели мыши и мыши прислушиваются к ней (см. Движение мыши):

@Override
public void mouseDragged(MouseEvent e) {
    Point dragPoint = e.getPoint();
    Point startPoint = this.selector.getStartPoint();

    int x = Math.min(startPoint.x, dragPoint.x);
    int y = Math.min(startPoint.y, dragPoint.y);
    int width = Math.max(startPoint.x - dragPoint.x, dragPoint.x - startPoint.x);
    int height = Math.max(startPoint.y - dragPoint.y, dragPoint.y - startPoint.y);

    this.selector.setCameraDimension(width, height);
    this.selector.setCoordinates(x, y);

    this.camera.repaint(); // important
}

кстати, this.selector - это SelectorCamera который является компонентом, который рисует область выбора.

Шаг 5

Вызывается CaptureCamera # endSelection(), этот метод получает x, y, width, height от камеры выбора и передает его классу захватчика, который использует Robot для получения снимка экрана с этим прямоугольником, и до этого он удаляет ВСЕ компоненты из содержимого панели, и перерисовывает все, а затем устанавливает видимость в false.

public void endSelection() {
    super.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));

    int x = this.selector.getCameraX();
    int y = this.selector.getCameraY();
    int w = this.selector.getCameraWidth();
    int h = this.selector.getCameraHeight();

    super.getContentPane().removeAll();
    super.getContentPane().repaint();

    //super.repaint();
    super.setVisible(false);

    this.c.startCapturing(x, y, w, h);

}

В основном это последний шаг, шаги для отдыха не нужны для отладки, поскольку он только отправляет обратный вызов.

Я очень старался объяснить процесс моего приложения, я попытался выяснить его в течение 5 с половиной часов и не повезло. Пробовал разные способы, создавая новый объект SelectionCamera как вы видите, не работает.

Почему он это делает? Это как-то связано с ядром свинга?

Код SelectionCamera: https://github.com/BenBeri/WiseCapturer/blob/master/src/il/ben/wise/SelectionCamera.java

Заранее спасибо.

  • 0
    Это выглядит так, как будто selector должен быть сброшен ... может быть ... Вы также можете выделить ваш код и код из библиотеки захвата ...
  • 0
    Все в библиотеке, все это мой код
Показать ещё 3 комментария
Теги:
user-interface
swing

2 ответа

1
Лучший ответ

Основываясь на этом примере...

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

Я не буду сосредотачиваться на начальной стадии первого раунда, я сосредоточусь на инициализации второго раунда, поскольку именно здесь проблема...

b.beginCapture вызовите this.capturer.beginSelection(); , который вызывает super.getCamera().startSelection(); который вызывает setVisible(true) (CaptureCamera был JFrame).

Это немедленно покажет, что когда-либо было показано на CaptureCamera. Здесь важно отметить, что никаких новых экземпляров объектов в процессе не было создано...

Теперь я сделал много изменений в базовом тестировании, но, похоже, проблема заключается в восстановлении кадра, когда он стал видимым во второй раз. Это, похоже, проблема с поддержкой прозрачности окна, поскольку, похоже, оно восстанавливает последнее "известное" состояние, а не сразу перерисовывает окно...

Теперь я попробовал очистить selector прежде чем сделать CaptureCamera невидимым без eval, поскольку окно кажется невидимым перед тем, как selector будет нарисован.

Окончательное решение, которое я придумал было назвать dispose на CaptureCamera, который выпускает его родной сверстников и, следовательно, разрушает старые графический контекст, заставляя кадр, чтобы восстановить себя, когда он становится видимым снова.

"Проблема" с этим может заключаться в том, что когда все окна расположены (и только бегущие потоки - потоки демона), JVM выйдет из...

Это было проблемой во время моего тестирования, поскольку я использовал javax.swing.Timer для задержки между первым и вторым процессом захвата, чтобы я мог видеть, где возникла проблема, и это вызвало выход моей JVM (поскольку таймер использует демона, и у меня не было других открытых окон).

Я обошел это, создав крошечное (1x1) прозрачное окно в классе Capturer, помните об этом, если JVM существует изящно без причины;)

Боковые заметки...

Теперь есть проблема с SelectionCamera (которая расширяет JPanel), она непрозрачна, но использует прозрачный фон, это невероятно опасно, поскольку Swing знает, как бороться с непрозрачными или полностью прозрачными компонентами.

public SelectionCamera() {
    super.setBackground(new Color(0, 0, 0, 0));
    super.setVisible(false);
}

Должно быть обновлено до чего-то вроде...

public SelectionCamera() {
    setOpaque(false);
    //super.setBackground(new Color(0, 0, 0, 0));
    super.setVisible(false);
}

Я также запутался в использовании super.xxx, единственной причиной, по которой вы это сделаете, является то, что вы переопределили эти методы и не хотели называть их в это время... В ходе тестирования я удалил все вызовы для super где метод не был переопределен в текущем классе (и я еще не был в методе overriden)

Кроме того, метод paintComponent должен быть super.paintComponent

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.setColor(new Color(0, 0, 0, 0.5f));
    g.fillRect(this.x, this.y, this.width, this.height);
}
1

Сделайте Frame оставшимся для -10, 000, затем установите видимое значение true, добавьте таймер 2 секунды (попробуйте уменьшить значение до 25 -10 0 миллисекунд, чтобы дать ему sligth pause для аннулирования содержимого), по таймеру: слева до 0. Я думаю, что это работает из-за кеширования и двойного буферизации. Кадр показывает, что у него было в буфере, буфер указывает на старое изображение из-за кэширования/ленивой перерисовки.


Альтернатива: Возможно, перерисовка или недействительность до того, как ваше шоу тоже будет работать, и не нужно делать левые -10, 000. Я не очень много работаю с ui-swing, всего лишь несколько лет назад и помню некоторые странные вещи, подобные этому.

  • 0
    Да, это решение, но очень ужасное решение для моей библиотеки, учитывая, что пользователь нажимает кнопку захвата, и ему нужно подождать 2 секунды, но он не знает, что ему нужно, он попытается выбрать и потеряет фокус от кадр, который заботится о выборе области. Для этого я подумал о добавлении курсора.WAIT, но я все еще не могу этого сделать, потому что на X-asis кадр -10 000. Я думал о создании временного кадра с таким же размером на 0,0, чтобы показывать сообщение или курсор, пожалуйста, подождите, но я не думаю, что это хорошая идея.
  • 0
    Вы пытались сделать недействительным и перекрасить? Может быть, сделать это 100 миллисекунд вместо 2 секунд.
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню