【问题标题】:JAVA - Can't paint a component (which is also a Runnable and KeyListener) on JPanelJAVA - 无法在 JPanel 上绘制组件(也是 Runnable 和 KeyListener)
【发布时间】: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 类中添加了这个组件,但是如果我尝试绘制它,它不会显示出来。我不明白为什么。纠结了这么久,终于决定在这里问一个问题。

【问题讨论】:

    标签: java swing animation graphics components


    【解决方案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 中,它确实水平拉伸),但在这些边界之外绘制得很好,因此什么也看不到。
    • 非常感谢您的支持。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-12
    • 2012-06-19
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 2015-04-23
    相关资源
    最近更新 更多