JAVA - 无法在 JPanel 上绘制组件(也是 Runnable 和 KeyListener)

Posted

技术标签:

【中文标题】JAVA - 无法在 JPanel 上绘制组件(也是 Runnable 和 KeyListener)【英文标题】:JAVA - Can't paint a component (which is also a Runnable and KeyListener) on JPanel 【发布时间】:2014-12-29 09:10:10 【问题描述】:

这些是我正在为一个使用 Java 和图形的简单游戏编写的类

主要:

import javax.swing.*;
import java.awt.*;

public class FrameTest extends JFrame 

    public FrameTest() 
        setSize(800,600);
        setVisible(true);
    

    public static void main(String[] args) 
        FrameTest frame = new FrameTest();
        GamePanel panel = new GamePanel();
        frame.add(panel);
    

面板测试:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class PanelTest extends JPanel 

    private PlayerTest playerRunnable;
    private Thread playerThread;

    public PanelTest() 
        setSize(800,600);

        playerRunnable = new PlayerTest();
        playerThread = new Thread(playerRunnable);

        //New
        setLayout(new BorderLayout());
        add(playerRunnable, BorderLayout.NORTH);
    

    public void paintComponent(Graphics g) 
        super.paintComponent(g);
        setBackground(Color.red);
    



它只是创建了一个具有红色背景的面板(我这样做是为了查看paintComponent() 是否适用于这项工作,它实际上是否有效)。

玩家测试:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.*;
import javax.swing.JComponent;

public class PlayerTest extends JComponent implements Runnable, KeyListener 

    public int x;
    public int y;
    private int speed;

    public PlayerTest() 
        speed = 10;
        x = 800/2;
        y = 600/4;

        addKeyListener(this);
        setFocusable(true);
    

    public void run() 
        while(true) 
    

    //New
    public Dimension getPreferredSize() 
        return new Dimension(30,30);
    

    public void paintComponent(Graphics g) 
        g.setColor(Color.BLUE);
        g.fillRect(x, y, 30, 30);
    

    public void keyReleased(KeyEvent e) 
    public void keyTyped(KeyEvent e) 

    public void keyPressed(KeyEvent e) 
        if(e.getKeyCode() == KeyEvent.VK_LEFT) 
            //Spostamento a sinistra player
            move(speed*-1);
        

        if(e.getKeyCode() == KeyEvent.VK_RIGHT) 
            //Spostamento a destra player
            move(speed);
        

        if(e.getKeyCode() == KeyEvent.VK_SPACE) 
            //Lancio bomba
        
    

    public void move(int s) 
        if (s < 0) 
            if(x-s > 0) 
                x += s;
                System.out.println("[PLAY] move left, x: " + x);
                repaint();
            
         else 
            if(x+s < 800) 
                x += s;
                System.out.println("[PLAY] move right, x: " + x);
                repaint();
            
        
    

问题来了。这个类是一个 Runnable(用于动画)、一个 KeyListener(用于玩家移动)和一个 JComponent(用于显示玩家的图像,现在假设它是一个矩形) 我已经在 Panel 类中添加了这个组件,但是如果我尝试绘制它,它不会显示出来。我不明白为什么。纠结了这么久,终于决定在这里问一个问题。

【问题讨论】:

【参考方案1】:

我发现您的代码存在一些问题,但最大的直接问题是您的 PlayerTest 组件没有设置首选大小,因此它的大小为 [1, 1],因此根本看不到。为其容器 PanelTest 提供一个 BorderLayout,以便 PlayerTest 组件填充其容器。

其他问题:

不要设置组件的大小。如果必须,请覆盖getPreferredSize(),并让它为您的 GUI 返回最佳维度。这将更适合您的布局管理器。 请务必使用布局管理器,例如上面提到的 BorderLayout,以便您的 GUI 在需要时展开并放置在需要去的地方。 考虑使用 Swing 计时器而不是线程和空的 run() 方法,因为它更易于使用并保证您的代码是 Swing 线程安全的。 避免使用 KeyListener,而是使用 Key Binding,因为它们在组件焦点问题方面更易于使用。

例如:How to make an image move while listening to a keypress in Java.


在您的 JPanel 周围放置一个边框,您会发现它的大小等于 GUI 的宽度,但只有 30 磅高,因为您将其 preferredSize 限制为 30 x 30,但试图在其中绘制一些东西远远超出此范围,因此绘图组件不足以显示您的绘图。我将通过使 Player 类从 Swing 组件扩展,而是使其成为一个 logical 类,一个知道如何绘制自身的类来构造不同的东西。我会让主 JPanel 成为绘图 JPanel,并给它一个 Player 对象来移动和显示。

例如,此代码显示并移动一个正方形,但仅在按右箭头时向右移动:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class MyAnimationTest extends JPanel 
   private static final int PREF_W = 800;
   private static final int PREF_H = 600;
   private static final Color BG = Color.RED;
   private static final int TIMER_DELAY = 15;
   public static final int X_STEP = 5;
   private MyPlayer player = new MyPlayer(PREF_W / 2, PREF_H / 4);
   private boolean right = false;
   private Timer timer = new Timer(TIMER_DELAY, new TimerListener());

   public MyAnimationTest() 
      setBackground(BG);
      setUpKeyBindings();
      timer.start();
   

   private void setUpKeyBindings() 
      int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
      InputMap inMap = getInputMap(condition);
      ActionMap actMap = getActionMap();

      KeyStroke rightArrowDownStroke = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false);
      KeyStroke rightArrowUpStroke = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true);

      inMap.put(rightArrowDownStroke, rightArrowDownStroke.toString());
      inMap.put(rightArrowUpStroke, rightArrowUpStroke.toString());

      actMap.put(rightArrowDownStroke.toString(), new RightMoveAction(true));
      actMap.put(rightArrowUpStroke.toString(), new RightMoveAction(false));
   

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

   @Override
   protected void paintComponent(Graphics g) 
      super.paintComponent(g);
      player.draw(g);
   

   private class RightMoveAction extends AbstractAction 
      private boolean move;

      public RightMoveAction(boolean move) 
         this.move = move;
      

      @Override
      public void actionPerformed(ActionEvent e) 
         right = move;
      
   

   private class TimerListener implements ActionListener 
      @Override
      public void actionPerformed(ActionEvent e) 
         if (right) 
            int newX = player.getX() + X_STEP;
            player.setX(newX);
            repaint();
         
      
   

   private static void createAndShowGui() 
      MyAnimationTest mainPanel = new MyAnimationTest();

      JFrame frame = new JFrame("My Animation");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_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();
         
      );
   


/**
 * A logical class that does not extend a Swing component
 * 
 * @author Pete
 *
 */
class MyPlayer 
   public static final Color COLOR = Color.blue;
   public static final int WIDTH = 30;
   private BufferedImage sprite = new BufferedImage(WIDTH, WIDTH, BufferedImage.TYPE_INT_ARGB);
   private int x;
   private int y;

   public MyPlayer(int x, int y) 
      this.x = x;
      this.y = y;

      Graphics g = sprite.getGraphics();
      g.setColor(COLOR);
      g.fillRect(0, 0, WIDTH, WIDTH);
      g.dispose();
   

   public int getX() 
      return x;
   

   public void setX(int x) 
      this.x = x;
   

   public int getY() 
      return y;
   

   public void setY(int y) 
      this.y = y;
   

   public void draw(Graphics g) 
      g.drawImage(sprite, x, y, null);
   

【讨论】:

感谢您的回答。不过,如果我覆盖getPreferredSize(),我还看不到绘制的组件。我还不能使用 Swing Timer,因为我们是为学校做的,而且我们只是在 Thread 段落上。编辑:我会尝试使用容器并报告。 @sonnhy:魔鬼在细节中。考虑editing your question 并在您当前的文本和代码下方添加您最新的代码尝试。 我可能不明白您的回答的真正含义,或者程序无法正常工作:|无论如何感谢帮助。 @sonnhy:见编辑回答。您正在绘制一个 30 x 30 的组件(实际上,因为它被放置在 BorderLayout.NORTH 中,它确实水平拉伸),但在这些边界之外绘制得很好,因此什么也看不到。 非常感谢您的支持。

以上是关于JAVA - 无法在 JPanel 上绘制组件(也是 Runnable 和 KeyListener)的主要内容,如果未能解决你的问题,请参考以下文章

Swing:如何在每个组件、JPanel、JButton 等上绘制动画?

paint() 方法不会在 JPanel 上绘制

在 java swing 组件上绘制边框

在 JFrame 上更改 JPanel 组件时无法显示它

在 Java 中单击按钮在 JPanel 中绘制一条线

PaintComponent 破坏网格绘图