Java repaint() не вызывает paintComponent()

Пытаясь сделать очень простую игру-пулю, чтобы узнать о Java, я столкнулся с препятствием: функция repaint() не вызывала paintComponent().

Вот вся программа, которая на данный момент просто рисует изображение, которое я создал 50 раз в секунду, на JPanel, который опирается на JFrame.

/*
 * Bullet hell, by Nematodes
 */

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class bulletHell extends JFrame
{
    private static final long serialVersionUID = 0L;
    JPanel gamePanel = new JPanel();
    int gameTimerDelay = 20;
    int x, y = 0;
    BufferedImage lightOrb;
    javax.swing.Timer gameTimer;

    public static void main(String[] args)
    {
        bulletHell createFrame = new bulletHell();
        createFrame.frameConstructor();
    }

    public void frameConstructor()
    {
        // Construct frame and frame components
        setTitle("Bullet hell");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);
        getContentPane().setLayout(new GridBagLayout());
        setVisible(true);

        GridBagConstraints gridConstraints;

        gridConstraints = new GridBagConstraints();
        gridConstraints.gridx = 0;
        gridConstraints.gridy = 0;
        gamePanel.setBackground(Color.BLACK);
        gamePanel.setPreferredSize(new Dimension(700, 700));
        getContentPane().add(gamePanel, gridConstraints);

        pack();

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds((int) (0.5 * (screenSize.width - getWidth())),
                 (int) (0.5 * (screenSize.height - getHeight())), getWidth(), getHeight());

        try
        {
            lightOrb = ImageIO.read(new File("C:/Users/Owner/Downloads/orb.bmp"));
        }
        catch(IOException e)
        {
            System.out.println("An issue occurred while trying to read orb.bmp");
        }

        // Start timer that draws game objects 50 times per second (50 FPS)
        gameTimer = new javax.swing.Timer(gameTimerDelay, gameTimerAction);
        gameTimer.setInitialDelay(0);
        gameTimer.start();
    }

    ActionListener gameTimerAction = new ActionListener()
    {
        public void actionPerformed(ActionEvent e)
        {
            repaint();
        }
    };

    class GraphicsPanel extends JPanel
    {
        public GraphicsPanel()
        {

        }

        // Draw all of the components
        @Override
        public void paintComponent(Graphics g)
        {
            Graphics2D g2D = (Graphics2D) g;
            super.paintComponent(g2D);
            g2D.drawImage(lightOrb, x, y, this);
            g2D.dispose();
        }
    }
}

После некоторой отладки с точками останова и методами println я могу подтвердить, что считывается правильное изображение, таймер в gameTimerAction вызывается 50 раз в секунду, а repaint() вообще не вызывает paintComponent().

Я новичок в программировании на Java и могу просто упустить что-то простое.

Редактировать: проблема была решена путем замены gamePanel на объект GraphicsPanel. К сожалению, это также означает, что мой гораздо более крупный проект pong (из которого была по существу скопирована ошибочная логика рисования этого проекта) работал только чудом и мог быть нестабильным с некоторыми дополнениями кода.


person Nematodes    schedule 31.01.2015    source источник


Ответы (1)


Сразу вижу несколько проблем:

  • Самое важное: вы никогда не создаете экземпляр объекта GraphicsPanel и не добавляете его ни к чему. Метод paintComponent(...) никогда не будет вызываться для панели JPanel, которая не визуализируется и не создается. Почему бы не сделать вашу переменную gamePanel объектом GraphicsPanel, а не объектом JPanel?
  • Вы никогда не меняете x и y в своем Таймере, поэтому без изменений анимация не произойдет.
  • Также вы вызываете dispose для объекта Graphics, предоставленного вам JVM, чего никогда не следует делать. Это разрывает цепочку рисования Swing, делая графику вашего графического интерфейса нестабильной.

Так что держись, у тебя все получится.

Например:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.*;

public class BulletExample extends JPanel {
   public static final String IMG_PATH = "http://www.i2clipart.com/cliparts/f/0/5/8/clipart-blue-circle-f058.png";
   private static final int PREF_W = 700;
   private static final int PREF_H = PREF_W;
   private static final int TIMER_DELAY = 20;
   private BufferedImage bullet;
   private int bulletX;
   private int bulletY;

   public BulletExample() throws IOException {
      URL imgUrl = new URL(IMG_PATH);
      bullet = ImageIO.read(imgUrl);
      new Timer(TIMER_DELAY, new BulletListener()).start();
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (bullet != null) {
         g.drawImage(bullet, bulletX, bulletY, this);
      }
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(PREF_W, PREF_H);
   }

   private class BulletListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         bulletX++;
         bulletY++;
         repaint();
      }
   }

   private static void createAndShowGui() throws IOException {

      // create the drawing JPanel
      BulletExample mainPanel = new BulletExample();

      JFrame frame = new JFrame("BulletExample");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

      // add it to the JFrame
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            try {
               createAndShowGui();
            } catch (IOException e) {
               e.printStackTrace();
            }
         }
      });
   }
}
person Hovercraft Full Of Eels    schedule 31.01.2015
comment
Спасибо за помощь! Инициализация объекта GraphicsPanel вместо JPanel для рендеринга изображения на разрешенном вызове paintComponent, который рисовал изображение. - person Nematodes; 31.01.2015
comment
(Не редактировал мой предыдущий комментарий достаточно быстро) Я еще не планировал анимацию, но, возможно, я смогу чему-то научиться из вашего примера кода. - person Nematodes; 31.01.2015