当前位置: 首页 > 知识库问答 >
问题:

扩展JPanel以绘制图像。但在添加到JFrame的同一面板上

郭云
2023-03-14

更新:正如Dmich所指出的,因为我在面板类之外绘制,这导致了对我的面板和动画类初始化的递归调用。所以我怎么还能在动画课上完成在我的面板上的绘图,但没有这个问题。

我的代码有一个非常具体的问题,我不知道如何描述正在发生的事情,但我会尽力。我尝试搜索stackOverFLow,但问题是我甚至不知道搜索什么。

我来了:

为了尽可能好地组织这篇文章,我将首先写下我正在处理的所有课程。

public class MyPanel extends JPanel implements Runnable{
private Animation anim = new Animation();
}
public class Animation extends ??? implements KeyListener{}

所以我有一个名为MyPanel的类,它扩展了JPanel。我已经使用add(new MyPanel())将这个类添加到了我的JFrame中。我在MyPanel中使用了一个线程,该线程调用@Overrid public void paintComponent(Graphics g)paintComponent调用动画中的一个方法,在该方法中我将图像绘制到屏幕g2d。drawImage(image,int,int,ImageObserver)。问题是使用这种方法我需要一个ImageObserver,如果动画扩展了JPanel,我就可以得到它。但是如果我扩展了JPanel,就不会在JFrame上绘制任何内容,因为这是一个新的JPanel,没有添加到JFrame中。

但是如果我扩展我的面板(它被添加到我的JFrame),我会得到一大堆错误。

Eclipse错误:

Exception in thread "main" java.lang.StackOverflowError
at java.awt.Component.setBackground(Unknown Source)
at javax.swing.JComponent.setBackground(Unknown Source)
at javax.swing.LookAndFeel.installColors(Unknown Source)
at javax.swing.LookAndFeel.installColorsAndFont(Unknown Source)
at javax.swing.plaf.basic.BasicPanelUI.installDefaults(Unknown Source)
at javax.swing.plaf.basic.BasicPanelUI.installUI(Unknown Source)
at javax.swing.JComponent.setUI(Unknown Source)
at javax.swing.JPanel.setUI(Unknown Source)
at javax.swing.JPanel.updateUI(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source) 

在这个错误消息下,这两行将继续运行,直到eclipse终止。

at MyPanel.<init>(MyPanel.java:9)
at Animation.<init>(Animation.java:9)

任何帮助都将不胜感激。

更新2根据建议添加代码示例:

import javax.swing.JFrame;

public class Simulation extends JFrame {
private MyPanel panel = new MyPanel();
public Simulation() {
    initUI();
}

private void initUI() {

    add(panel);
    setResizable(false);
    pack();

    setTitle("Simulation");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null); // centers
}
public static void main(String[] args) {

    Simulation ex = new Simulation();
    ex.setVisible(true);
}
}


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

public class MyPanel extends JPanel implements Runnable { 
private Animation anim;
public MyPanel() {
    anim = new Animation();
    initPanel();
}

private void initPanel() {
    //other customizables
}

@Override
public void run() {
//Thread that calls repaint();
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2D = (Graphics2D) g;
    anim.step(g2D, this);
}
}


import java.awt.Graphics2D;

public class Animation {
private Sprite player;
private KeyBinder kB;

public Animation() {
    player = new Sprite();
  kB = new KeyBinder(player);
}

public void step(Graphics2D g2d, JPanel p) {
    player.move();
    drawSprite(g2d, p);
}

private void drawSprite(Graphics2D g2d, JPanel p) {
    g2d.drawImage(player.getImage(), player.getX(), player.getY(), p);
}
}

import java.awt.Image;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;

public class Sprite {
private final String PLAYER_IMAGE_FILE = "Image location";
private Image image;
private int width, height;
private int x, y, dx, dy;

public Sprite() {
    x = 0;
    y = 0;
    loadImage();
}

private void loadImage() {
    ImageIcon ii = new ImageIcon(PLAYER_IMAGE_FILE);
    image = ii.getImage();

    width = image.getWidth(null);
    height = image.getHeight(null);
}

public void move() {
    x += dx;
    y += dy;
}

public void keyPressed(KeyEvent e) {

    //assings meaning to keypressed
}

public void keyReleased(KeyEvent e) {

    //assings meaning to keyReleased
}

public int getX() {
    return x;
}

public int getY() {
    return y;
}

public int getW() {
    return width;
}

public int getH() {
    return height;
}

public Image getImage() {
    return image;
}
}

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class KeyBinder extends MyPanel implements KeyListener {
Sprite p;
public KeyBinder(Sprite player) {
    p = player;
    addKeyListener(this);
    setFocusable(true);
    setFocusTraversalKeysEnabled(false);
}

@Override
public void keyPressed(KeyEvent e) {
    p.keyPressed(e);
}
@Override
public void keyReleased(KeyEvent e) {
    p.keyReleased(e);
}
@Override
public void keyTyped(KeyEvent e) {

}
}

通过尝试解决代码问题,我对代码进行了修改,主要是通过尝试将JPanel传递给nimation.step方法,并为密钥监听创建一个单独的类。

新的错误列表基本相同:

Exception in thread "main" java.lang.StackOverflowError
at java.awt.Component.setBackground(Unknown Source)
at javax.swing.JComponent.setBackground(Unknown Source)
at javax.swing.LookAndFeel.installColors(Unknown Source)
at javax.swing.LookAndFeel.installColorsAndFont(Unknown Source)
at javax.swing.plaf.basic.BasicPanelUI.installDefaults(Unknown Source)
at javax.swing.plaf.basic.BasicPanelUI.installUI(Unknown Source)
at javax.swing.JComponent.setUI(Unknown Source)
at javax.swing.JPanel.html" target="_blank">setUI(Unknown Source)
at javax.swing.JPanel.updateUI(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source)

然后无限循环,直到以下三行终止:

at MyPanel.<init>(MyPanel.java:10)
at KeyBinder.<init>(KeyBinder.java:6)
at Animation.<init>(Animation.java:11)

共有3个答案

程皓轩
2023-03-14

这是另一个继承不良的例子。我修复了它通过实现KeyListener在MYPanel类,并传递作为参数的动画的我的面板。对我来说,尝试从不同的类实现一个关键监听器到一个JPanel是不好的做法。

柯建业
2023-03-14

我不建议让类扩展JPanel,而是在实例化时引入JPanel参数

public class Panel implements Runnable{
    private JPanel panel;

    public Panel(JPanel panel){
        this.panel = panel;
    }
}

此外,我认为明智的做法是将您拥有的Panel类扩展为组件,以便在将其添加到标题中提到的现有面板时实现无缝集成:

public class Panel extends Component implements Runnable{
    private JPanel panel;

    public Panel(JPanel panel){
        this.panel = panel;
    }
}

请让我知道,如果这不起作用,或者你有其他问题!

秦俊豪
2023-03-14

所以我有一个名为Panel的类,它扩展了JPanel。我已经使用add(new Panel())将这个类添加到我的JFrame中。我正在使用面板中的一个线程,该线程调用@Overrid public void paintComponent(Graphics g)

首先,Swing不是线程安全的,您不应该从事件调度线程的上下文之外更新UI。

其次,在任何情况下,都不应该直接调用paintComponent,也不需要将其设置为public,因为不应该调用它。

有关更多详细信息,请参阅Swing中的并发

paintComponent调用动画中的一个方法,在该方法中,我将图像绘制到屏幕g2d。drawImage(image,int,int,ImageObserver)。问题是使用这种方法我需要一个ImageObserver,如果动画扩展了JPanel,我就可以得到它。

为什么不让动画“可绘制”,提供一种方法,要求调用者传递完成工作所需的信息?

public class Animation {
    public void paint(Graphics2D g2d, ImageObserver observer) {
        //...
    }
}

您还可以将可能需要的其他信息传递给paint方法,例如建模当前状态的模型。

这基本上是一个委托模型——您将特定任务的责任“委托”给另一个类

那么KeyListener呢?

我听到你问。

嗯,首先,处理它可能不是动画的责任。相反,控制器应该修改状态,以响应事件,最终由渲染器绘制

另外,您应该改用密钥绑定API,它将解决与KeyListener

 类似资料:
  • 问题内容: 我正在设计一个程序,该程序在JFrame中包含两个JPanel,一个用于保存图像,另一个用于保存GUI组件(Searchfields等)。我想知道如何将图像绘制到JFrame中的第一个JPanel? 这是我的构造函数的示例代码: } 我试图覆盖JPanel的paintComponent方法来绘制图像,但是当我尝试编写时,这在我的构造函数中导致了一个问题: 因为这将只允许我传递null的

  • 问题内容: 如何将图像绘制到JPanel或JFrame,我已经阅读了oracle的相关教程,但似乎无法正确完成。我需要将图像“ ”显示在一组特定的坐标上。这是我到目前为止所拥有的。 在Main()中 提前致谢! 问题答案: 试试这个:

  • 我希望来自两个不同类的两个图像并排扩展JPanel。 我遇到的问题是两个JPanel应该在JFrame内部,但是当我做framename.add(panel)时,它会替换另一个,而不是并排添加其中两个。 我尝试在主类中添加flowlayout和其他布局,但是没有一个图像显示出来。 所以我的问题是,如果我有两个扩展Jpanel的类,我如何在Jframe中添加这两个面板,以便它们并排(彼此相邻)而不替

  • 我正在尝试将此 JPanel 添加到 JFrame 中,但我看不到他。当我创建新的 java 类并自己制作这个框架时,一切都很好。 来自JFrame表单的代码。 来自 JPanel 的代码: 来自Java类的代码。 我想不出区别在哪里。有人能帮我吗?

  • 问题内容: 我有一个带有两个按钮的面板。我正在尝试在面板内插入图像,我想在单击按钮后在图像内画线。我使用了下面的代码,但这似乎不起作用。 最大的问题是我的其他班级使用的相同代码。 问题答案: 尝试在更新后包装内部。同样,当您完成对图形上下文的渲染时,还应该调用。 您还应该依靠布局管理器,而不是自己动手做,这只会使您的生活更轻松。 就个人而言,我认为将其直接绘制到自定义组件会更容易。查看执行自定义绘

  • 我目前正在尝试制作一个画布,我可以绘制的东西,并使它出现在一个JFrame。 为此,我打算在一个JPanel组件中有一个BufferedImage,paintComponent方法可以从中进行绘制。 理想情况下,我希望能够从给定的JFrame中引用这个缓冲图像,然后使用其Graphics2D向其绘制素材,paintComponent方法可以在使用缓冲图像绘制时显示这些素材。 我这样做是为了避免直接