Как обновить графику класса Window в Java - PullRequest
3 голосов
/ 11 октября 2019

Я пытаюсь нарисовать поверх панели vlcj (java-привязка библиотеки VLC), чтобы я мог воспроизвести видео и нарисовать поверх него. И у меня возникли некоторые проблемы. Вот полный базовый код:

Список кодов 1: AppOverlay.java

package app;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.Window;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;

import com.sun.jna.platform.WindowUtils;

@SuppressWarnings("serial")
public class AppOverlay extends Window implements Runnable {

    private final boolean isRunning;
    private final int fps;
    private BufferedImage graphics;
    private BufferedImage img;
    private int x, y;
    private boolean ltr;

    public AppOverlay(Window owner) {
        super(owner, WindowUtils.getAlphaCompatibleGraphicsConfiguration());
        setBackground(new Color(0,0,0,0));

        graphics    = new BufferedImage(1280,800, BufferedImage.TYPE_INT_ARGB);
        isRunning   = true;
        img         = null;
        ltr         = true;
        fps         = 60;
        x           = 0;
        y           = 0;
    }

    @Override
    public void run(){
        while(isRunning){

            try{
                Thread.sleep(1000/fps);
            } catch(InterruptedException e){
                e.printStackTrace();
            }

            if(ltr) {
                if(x < 1280) x++;
                else ltr = false;
            } else {
                if(x < 0) ltr = true;
                else x--;
            }

            repaint();
        }
    }

    public void createAndShowGUI() {
        setVisible(true);

        Thread thread = new Thread(this);
        thread.start();

        String path = "Drive:\\path\\to\\image.png";
        try {
            img = ImageIO.read(new java.io.FileInputStream(path));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void paint(Graphics g) {
        Graphics2D g2d = (Graphics2D)g;
        Graphics2D gfx = graphics.createGraphics();
        gfx.setColor(new Color(255,255,255,0));
        gfx.clearRect(0, 0, 1280, 800);
        if(img != null) gfx.drawImage(img, x, y, null);
        gfx.dispose();
        g2d.drawImage(graphics, 0, 0, null);
    }
}

Список кодов 2: AppPlayer.java

package app;

import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent;

@SuppressWarnings("serial")
public class AppPlayer extends EmbeddedMediaPlayerComponent {

}

Листинг 3: AppFrame.java

package app;

import java.awt.Dimension;

import javax.swing.JFrame;

@SuppressWarnings("serial")
public class AppFrame extends JFrame {

    private AppPlayer appPlayer;
    private AppOverlay overlay;

    public AppFrame(){
        super();
    }

    public void createAndShowGUI() {

        appPlayer = new AppPlayer();
        appPlayer.setPreferredSize(new Dimension(1280,800));
        getContentPane().add(appPlayer);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setTitle("App");
        setVisible(true);
        pack();

        overlay = new AppOverlay(this);
        appPlayer.mediaPlayer().overlay().set(overlay);
        appPlayer.mediaPlayer().overlay().enable(true);
        overlay.createAndShowGUI();
    }
}

Листинг 4: Main.java

package main;

import javax.swing.SwingUtilities;

import app.AppFrame;

public class Main {

    public static void main(String[] args) {
        final AppFrame app = new AppFrame();
        SwingUtilities.invokeLater( new Runnable() {
            @Override
            public void run() {
                app.createAndShowGUI();
            }
        });
    }

}

с этим и библиотекой vlcj-4 вы сможете самостоятельно протестировать мой код. Моя проблема в том, что Overlay (класс AppOverlay, который расширяет класс Window) не отображает и не обновляет анимацию, если я не отменил выбор окна (я щелкаю по другому окну или на рабочем столе или на панели инструментов ОС), чтобы окно (приложение)неактивен, затем снова выберите окно (приложение). Он будет загружать только один кадр и все. Я должен отменить выбор и повторно выбрать окно, чтобы загрузить другой кадр (это только в случае с наложением, т. Е. Если я воспроизведу видео в классе AppPlayer, видео будет воспроизводиться очень хорошо.

Что яхочу иметь возможность рисовать анимированную графику на оверлее. Я знаю, что с классом JPanel есть метод paintComponent (), но класс Window не имеет этого метода (только методы paint () и repaint ()доступно).

Что мне нужно сделать, чтобы это исправить?

РЕДАКТИРОВАТЬ:

Я попытался добавить JPanel, на котором я рисую, вместо того, чтобы рисовать напрямуюна AppOverlay

Code-листинг 5: AppPanel.java

package app;

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JPanel;

public class AppPanel extends JPanel implements Runnable {
    private int x, y;
    private boolean ltr;

    public AppPanel() {
        x   = 0;
        y   = 0;
        ltr = true;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(new Color(0,0,0,0));
        g.clearRect(0, 0, 1280, 800);
        g.setColor(Color.RED);
        g.fillRect(x, y, 100, 100);
    }

    @Override
    public void run() {
        while(true){

            try{
                Thread.sleep(1000/60);
            } catch(InterruptedException e){
                e.printStackTrace();
            }
            if(ltr) {
                if(x < 1280) x++;
                else ltr = false;
            } else {
                if(x < 0) ltr = true;
                else x--;
            }

            repaint();
        }
    }
}

и добавление его в AppOverlay.

Code-листинг 6: AppOverlay.java с частичной модификацией

public class AppOverlay extends Window implements Runnable {
    //previous field declaration above ...
    AppPanel panel;
    AppPlayer player = null;

    public AppOverlay(Window owner) {
        //previous constructor instructions above...

        panel = new AppPanel();
        add(panel);
    }

    public void createAndShowGUI(AppPlayer player) {
        setVisible(true);

        /*
        Thread thread = new Thread(this);
        thread.start();

        String path = "Drive:\\path\\to\\image.png";
        try {
            img = ImageIO.read(new java.io.FileInputStream(path));
        } catch (IOException e) {
            e.printStackTrace();
        }
        */

        Thread panelThread = new Thread(panel);
        panelThread.start();
    }
}

При этом отобразятся графические элементы JPanel и анимированы их по мере необходимости.

Если вы знаете способ созданияJPanel фон прозрачный (так что мы можемn видеть насквозь), все еще позволяя ему отображать графику. Это наверняка решит проблему.

Ответы [ 3 ]

1 голос
/ 16 октября 2019

Я немного поэкспериментировал с вашим примером и придумал что-то работающее, но я бы не назвал это хорошим решением.

Основная проблема заключается в том, что невозможно определить оверлейобновить (или я просто не нашел его). Просто repainting наложение не обновляет его на экране, поэтому я использовал обходной путь, чтобы скрыть и показать его снова.

Для определения времени интервала обновления я использовал javax.swing.Timer. (В реальной версии вы, вероятно, захотите запустить и остановить таймер с помощью MediaPlayerEventListener).

В качестве побочного эффекта вызывается метод repaint, а координата x регулируется для перемещения изображения. вокруг экрана.

В приведенном ниже упрощенном примере (используйте main для его запуска), я переместил красный прямоугольник с координатой x вместо некоторого неизвестного изображения.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.Timer;

import com.sun.jna.platform.WindowUtils;

import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent;
import uk.co.caprica.vlcj.player.embedded.OverlayApi;

public class AppFrame extends JFrame {

    private static final long serialVersionUID = -1569823648323129877L;

    public class Overlay extends Window {

        private static final long serialVersionUID = 8337750467830040964L;

        private int x, y;
        private boolean ltr = true;

        public Overlay(Window owner) throws HeadlessException {
            super(owner, WindowUtils.getAlphaCompatibleGraphicsConfiguration());
            setBackground(new Color(0,0,0,0));
        }

        @Override
        public void paint(Graphics g) {

            super.paint(g);

            if (ltr) {
                if (x < 1180)
                    x += 1;
                else
                    ltr = false;
            } else {
                if (x < 0)
                    ltr = true;
                else
                    x -= 1;
            }

            g.setColor(Color.RED);
            g.fillRect(x, y, 100, 100);

            String s = Integer.toString(x);
            g.setColor(Color.WHITE);
            g.drawChars(s.toCharArray(), 0, s.length(), x+10, y+50);
        }
    }

    private EmbeddedMediaPlayerComponent appPlayer;

    public void createAndShowGUI() {

        appPlayer = new EmbeddedMediaPlayerComponent();
        appPlayer.setPreferredSize(new Dimension(1280, 800));
        getContentPane().add(appPlayer);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setTitle("App");
        setVisible(true);
        pack();

        Overlay overlay = new Overlay(this);

        OverlayApi api = appPlayer.mediaPlayer().overlay();
        api.set(overlay);
        api.enable(true);

        //appPlayer.mediaPlayer().media().play(" ... ");

        Timer timer = new Timer(0, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                api.enable(false);
                api.enable(true);
            }
        });
        timer.setRepeats(true);
        timer.setDelay(200);
        timer.start();
    }
}

Если это вариант для вас, может быть гораздо проще использовать animated gif. По крайней мере, это работает само по себе (таймер не нужен).


Обновление:

Как вы уже поняли, использование JPanel кажетсяработать лучшеПросто используйте setOpaque(false), чтобы сделать его прозрачным.

Вот скорректированный пример.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent;
import uk.co.caprica.vlcj.player.embedded.OverlayApi;

public class AppFrame2 extends JFrame {

    private static final long serialVersionUID = -1569823648323129877L;

    public class OverlayPanel extends JPanel {

        private static final long serialVersionUID = 8070414617530302145L;

        private int x, y;
        private boolean ltr = true;

        public OverlayPanel() {
            this.setOpaque(false);
        }

        @Override
        public void paint(Graphics g) {

            super.paint(g);

            if (ltr) {
                if (x < 1180)
                    x += 1;
                else
                    ltr = false;
            } else {
                if (x < 0)
                    ltr = true;
                else
                    x -= 1;
            }

            g.setColor(Color.RED);
            g.fillRect(x, y, 100, 100);

            String s = Integer.toString(x);
            g.setColor(Color.WHITE);
            g.drawChars(s.toCharArray(), 0, s.length(), x+10, y+50);
        }
    }

    public class Overlay extends Window {

        private static final long serialVersionUID = 8337750467830040964L;

        OverlayPanel panel;

        public Overlay(Window owner) throws HeadlessException {
            super(owner);
            setBackground(new Color(0,0,0,0));

            panel = new OverlayPanel();
            this.add(panel);
        }
    }

    private EmbeddedMediaPlayerComponent appPlayer;

    public void createAndShowGUI() {

        appPlayer = new EmbeddedMediaPlayerComponent();
        appPlayer.setPreferredSize(new Dimension(1280, 800));
        getContentPane().add(appPlayer);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setTitle("App");
        setVisible(true);
        pack();

        Overlay overlay = new Overlay(this);

        OverlayApi api = appPlayer.mediaPlayer().overlay();
        api.set(overlay);
        api.enable(true);

        //appPlayer.mediaPlayer().media().play(" ... ");

        Timer timer = new Timer(0, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                overlay.panel.repaint();
            }
        });
        timer.setRepeats(true);
        timer.setDelay(17);
        timer.start();
    }
}
0 голосов
/ 14 октября 2019

Вы можете использовать следующие методы из JComponent: (http://download.oracle.com/javase/6/docs/api/javax/swing/JComponent.html)

void    repaint(long tm, int x, int y, int width, int height)
 //**Adds the specified region to the dirty region list if the component is showing.*//
void    repaint(Rectangle r)
 /**Adds the specified region to the dirty region list if the component is showing.*//
You can call those before redraw()
0 голосов
/ 14 октября 2019

Вы уже сделали основную часть работы. Просто перекрашивайте рамку каждый раз, когда вы рисуете над ней, вызывая app.repaint();

...