【问题标题】:Repaint() does not work after adding more components to JPanel in Swing, Java在 Java 的 Swing 中向 JPanel 添加更多组件后,Repaint() 不起作用
【发布时间】:2019-04-23 10:17:55
【问题描述】:

我从 Java 开始,想制作一个简单的乒乓球游戏,以了解在 Java 中显示内容的方式。我创建了 2 个类,它们都扩展 JPanel 并每 16.6 毫秒调用一次 repaint() 函数。我将两者都添加到了我添加到框架中的 JPanel 中,但只有我首先添加的组件才会显示。

我在两个类中都尝试在repaint() 之后使用revalidate(),并确保 Timer 确实有效,并且实际上调用了 actionPerformed() 方法。

这是主要的方法:

public class Main {
  public static void main(String[] args){
    JFrame frame = new JFrame("Pong");
    //...
    JPanel mainPanel = new JPanel();

    mainPanel.add(new Player());
    mainPanel.add(new Ball(frameWidth/2, frameHeight/2 -40));

    frame.add(mainPanel);
  }
}

这是类的重要代码(两者基本相同):

public class Player extends JPanel {

  public Player(){
    setPreferredSize(new Dimension(Main.frameWidth, Main.frameHeight));
    setBackground(Color.BLACK);

    new Timer(1000/60, new ActionListener(){
      public void actionPerformed(ActionEvent e){
        update();
        repaint();
      }
    }).start();
  }
//...
  public void paintComponent(Graphics g){
    super.paintComponent(g);

    g.setColor(Color.WHITE);
    g.fillRect(x, y, width, height);
  }
}

(当然,我留下了诸如 @Override 或不必要的函数之类的东西以缩短代码)

此代码仅绘制播放器,尽管我希望它显示mainPanel 的所有组件。

这是一个您可以(希望)运行的示例。我不得不把它分成 3 个文件,因为我很讨厌匿名类:

Main.java

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

public class Main {

  public static void main(String[] args){
    JFrame frame = new JFrame("Pong");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(800, 800);

    JPanel mainPanel = new JPanel();

    mainPanel.add(new Player());
    mainPanel.add(new Ball());

    frame.add(mainPanel);

    frame.setVisible(true);
  }
}
///////
Player.java
//////
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class Player extends JPanel{
  private int x = 20, y = 300, width = 20, height = 100;
  public Player(){
    setPreferredSize(new Dimension(800, 800));
    setBackground(Color.BLACK);

    new Timer(1000/60, new ActionListener(){
      @Override
      public void actionPerformed(ActionEvent e){
        repaint();
      }
    }).start();
  }


  @Override
  public void paintComponent(Graphics g){
    super.paintComponent(g);

    g.setColor(Color.WHITE);
    g.fillRect(x, y, width, height);
  }
}
//////
Ball.java
//////
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class Ball extends JPanel{
  private int x = 20, y = 300, width = 20, height = 100;
  public Ball(){
    setPreferredSize(new Dimension(800, 800));
    setBackground(Color.BLACK);
    new Timer(1000/60, new ActionListener(){
      @Override
      public void actionPerformed(ActionEvent e){
        repaint();
      }
    }).start();
  }

  @Override
  public void paintComponent(Graphics g){
    super.paintComponent(g);

    g.setColor(Color.WHITE);
    g.fillRect(x, y, width, height);
  }
}

【问题讨论】:

  • PlayerBall 是否定义了首选尺寸? JPanel 是一个容器,旨在根据其包含的 GUI 组件调整自身大小。看起来您的类 PlayerBall 不包含任何 GUI 组件,因此它们的大小可能为 0(零)。
  • 确实如此。我这样设置它们; setPreferredSize(new Dimension(800, 800));
  • 那么我认为如果您发布 minimal reproducible example 会帮助人们帮助您。
  • 好的,我会在收到问题后编辑问题。
  • 好的,已经出来了。

标签: java swing graphics jpanel


【解决方案1】:

Player 类的方法paintComponent() 中,您每次都绘制完全相同的Rectangle。为了实现动画效果,每次绘制矩形时都需要更改其大小或位置,或两者都更改。你希望Player 做什么?它应该沿着窗口的左边缘上下移动吗?您是否看过题为 How to Use Swing Timers 的课程,它是 OracleJava 教程 的一部分?

编辑

我现在看到Player 隐藏了Ball,因为JPanel 的默认布局管理器。下面的代码本质上是您发布的代码,但我将GridLayout 设置为mainPanel 的布局管理器。此外,一个 java 源代码文件可能包含多个类,但只有一个类必须是 public。因此,在下面的代码中,只有类 Mainpublic

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

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

public class Main {

    public static void main(String[] args) {
        JFrame frame = new JFrame("Pong");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel mainPanel = new JPanel(new GridLayout(0, 2));
        mainPanel.add(new Player());
        mainPanel.add(new Ball());
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }
}

class Player extends JPanel {

    public Player() {
        setBorder(BorderFactory.createLineBorder(Color.RED, 2, false));
        setPreferredSize(new Dimension(800, 800));
        setBackground(Color.BLACK);

        new Timer(1000 / 60, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                repaint();
            }
        }).start();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.setColor(Color.WHITE);
        g.fillRect(20, 100, 20, 100);
    }
}

class Ball extends JPanel {

    public Ball() {
        setBorder(BorderFactory.createLineBorder(Color.CYAN, 2, false));
        setPreferredSize(new Dimension(800, 800));
        setBackground(Color.BLACK);
        new Timer(1000 / 60, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                repaint();
            }
        }).start();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.setColor(Color.WHITE);
        g.fillRect(300, 300, 10, 10);
    }
}

这是 GUI 的屏幕截图...

【讨论】:

  • 我在我的真实代码中使用变量来绘制它,但是为了节省一些行,它的位置是固定的。然而,首先移动 Player 并不是问题。问题是,只有添加到 mainPanel 的第一个组件被绘制。
  • 谢谢,显然 Stack Overflow 直到现在才向我展示这个(是的,我不时刷新页面;))。但是,由于小球需要与玩家发生碰撞,因此存在一个问题:如何让这些 JPanel 与另一个重叠?
  • 如何使这些 JPanel 与另一个重叠? 你没有。您需要在同一个JPanel 上同时绘制球和球员。上网搜索一下,有几个用java写的乒乓游戏的例子。而我看到的少数似乎只使用了一个JPanel 来绘制球和球员。
  • 这似乎不是解决这个问题的干净方法。这意味着,我必须将 Ball 类移植到 Player 类,并从那里执行所有逻辑和绘图。也许我不应该使用 JPanels 来扩展类?正如我所说,我是 java 新手,这是我知道创建自定义组件的唯一方法。
  • 谢谢@AndrewThompson 和 Abra,现在可以使用了!
【解决方案2】:

我刚刚意识到,如果我手动扩展我的窗口,第二个 JPanel 会显示在负责播放器的那个下面!这意味着我需要以某种方式设置面板位置,对吧?

@阿布拉

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-13
    • 2012-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多