При рисовании захваченного изображения экрана в JPanel изображение отображается еще один раз?Как я могу исправить это, чтобы отображать один timw?

#java #swing #drawing #java-2d

#java #swing #рисование #java-2d

Вопрос:

Я хочу захватить изображение с экрана и нарисовать его в JPanel, это работает, но оно отображается еще раз, как ввод в цикл, меня это смущает, как я могу это исправить, пожалуйста? Переменные Iwidth, Ihieght инициализированы выше, но я беру блок кода, который вызывает проблему

  protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Image img;            
         ImageIcon i = null;            
        Rectangle screenRect=new     Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
         try {
             BufferedImage capture=new Robot().createScreenCapture(screenRect);
             capture.getHeight();
             capture.getWidth();
             i=new ImageIcon(capture);
         } catch (AWTException ex) {
             Logger.getLogger(TestDrawing.class.getName()).log(Level.SEVERE, null, ex);
         }
         img = i.getImage();
         g.drawImage(img,Iwidth,Ihieght,null);
         super.repaint();
  }
  

Ответ №1:

Никогда не используйте подобный код внутри paintComponent. Этот метод должен быть зарезервирован для рисования и только для рисования, и это метод, над которым вы на самом деле не имеете полного контроля, поскольку он вызывается JVM в ответ как на ваш запрос, так и на запросы от ОС, и даже если вы запросите перерисовку, нет гарантии, что он будет выполнен, особенно если запросы накапливаются. Кроме того, воспринимаемая отзывчивость вашего графического интерфейса часто будет зависеть от того, насколько быстро выполняется рисование, в то время как чтение файлов и захват изображений никогда не должны выполняться внутри paintComponent.

Вместо этого вы должны прочитать изображение в качестве реакции на какое-либо событие, возможно, таймер, затем после считывания изображения вызовите repaint() и в paintComponent нарисуйте полученное изображение.

Также никогда не вызывайте repaint() внутри paintComponent.

Что-то вроде псевдокода

 // inside of the ActionListener of a Swing Timer (if you want to do this repeatedly)
get screen image from robot and feed it into the capture BufferedImage field
consider doing this in a SwingWorker
call repaint() when SwingWorker is done (via a PropertyChangeListener)
  

Внутри paintComponent:

 @Override
protected void paintComponent(Graphics g) {
   super.paintComponent(g);
   if (capture != null) {
      g.drawImage(capture, capture.getWidth(), capture.getHeight());
   }
}
  

Редактировать

Например:

 import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;

import javax.swing.*;
import javax.swing.SwingWorker.StateValue;

public class ScreenCaptureTest extends JPanel {
   private static final int PREF_W = 600;
   private static final int PREF_H = 400;
   private JButton btn = new JButton(new ScreenCaptureAction(this,
         "Capture Screen", KeyEvent.VK_C));
   private ImagePanel imagePanel = new ImagePanel();

   public ScreenCaptureTest() {
      JPanel buttonPanel = new JPanel();
      buttonPanel.add(btn);

      setLayout(new BorderLayout());
      add(new JScrollPane(imagePanel), BorderLayout.CENTER);
      add(buttonPanel, BorderLayout.SOUTH);
   }

   public void setImagePanelImage(BufferedImage img) {
      imagePanel.setImage(img);
      revalidate();
      repaint();
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }

   private static void createAndShowGui() {
      ScreenCaptureTest mainPanel = new ScreenCaptureTest();

      JFrame frame = new JFrame("ScreenCaptureTest");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class ImagePanel extends JPanel {
   private BufferedImage image;

   public void setImage(BufferedImage image) {
      this.image = image;
      revalidate();
      repaint();
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (image != null) {
         g.drawImage(image, 0, 0, null);
      }
   }

   @Override
   public Dimension getPreferredSize() {
      if (image != null) {
         return new Dimension(image.getWidth(), image.getHeight());
      }
      return super.getPreferredSize();
   }

}

class ScreenCaptureAction extends AbstractAction {
   private ScreenCaptureTest screenCaptureTest;

   public ScreenCaptureAction(ScreenCaptureTest screenCaptureTest, String name,
         int mnemonic) {
      super(name);
      putValue(MNEMONIC_KEY, mnemonic);
      this.screenCaptureTest = screenCaptureTest;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      setEnabled(false);
      final SwingWorker<BufferedImage, Void> mySwingWorker = new SwingWorker<BufferedImage, Void>() {

         @Override
         protected BufferedImage doInBackground() throws Exception {
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            Dimension screenSize = toolkit.getScreenSize();
            Robot robot = new Robot();
            BufferedImage capture = robot.createScreenCapture(new Rectangle(
                  screenSize));
            return capture;
         }

      };
      mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent pcEvt) {
            if ("state".equals(pcEvt.getPropertyName())
                  amp;amp; pcEvt.getNewValue() == StateValue.DONE) {
               setEnabled(true);
               try {
                  screenCaptureTest.setImagePanelImage(mySwingWorker.get());
               } catch (InterruptedException | ExecutionException e) {
                  e.printStackTrace();
               }
            }
         }
      });
      mySwingWorker.execute();
   }
}
  

Редактировать
Обратите внимание, что если бы это была моя программа, я бы отображал изображение как ImageIcon в JLabel, поскольку его намного проще кодировать. Тогда вы могли бы отказаться от класса ImagePanel и его метода paintComponent и просто закодировать main как:

 public class ScreenCaptureTest extends JPanel {
   private static final int PREF_W = 600;
   private static final int PREF_H = 400;
   private JButton btn = new JButton(new ScreenCaptureAction(this,
         "Capture Screen", KeyEvent.VK_C));
   //!! private ImagePanel imagePanel = new ImagePanel();
   private JLabel screenLabel = new JLabel(); //!!

   public ScreenCaptureTest() {
      JPanel buttonPanel = new JPanel();
      buttonPanel.add(btn);

      setLayout(new BorderLayout());
      //!! add(new JScrollPane(imagePanel), BorderLayout.CENTER);
      add(new JScrollPane(screenLabel), BorderLayout.CENTER); //!! 
      add(buttonPanel, BorderLayout.SOUTH);
   }

   public void setImagePanelImage(BufferedImage img) {
      //!! imagePanel.setImage(img);
      Icon icon = new ImageIcon(img);
      screenLabel.setIcon(icon);
//!!      revalidate();
//!!      repaint();
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }

   private static void createAndShowGui() {
      ScreenCaptureTest mainPanel = new ScreenCaptureTest();

      JFrame frame = new JFrame("ScreenCaptureTest");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}