在尝试填充JPanel时,在图形中出现NullPointerException



基本上,这样做的目的是让蠕虫在单击添加蠕虫按钮时展开。我试图使一个蠕虫扩展,并最终使用多线程来允许多个蠕虫在屏幕上扩展。现在我的麻烦只是允许一个蠕虫扩张。这是我的代码看起来像以下类,Main, ThreadFrame &DrawThread:

主:

public class Main {
public static void main(String[] args) {
    ThreadFrame myFrame = new ThreadFrame();
    myFrame.setSize(new Dimension(640, 480));
    myFrame.setLocationRelativeTo(null);
    myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    myFrame.setTitle("Worms! - Jonathan Perron");
    myFrame.setVisible(true);
    myFrame.setResizable(false);
}
}

ThreadFrame类:

public class ThreadFrame extends JFrame implements ActionListener {
JButton btnNewWorm, btnKillWorm;
JPanel myPanel2 = new JPanel();
ArrayList<DrawThread> worms = new ArrayList<DrawThread>();
public JPanel getMyPanel2(){
    return this.myPanel2;
}
public ThreadFrame() {
    JPanel myPanel = new JPanel();
    btnNewWorm = new JButton("New Worm");
    btnKillWorm = new JButton("Kill Worm");
    myPanel.setBounds(0, 400, 640, 80);
    myPanel.setLayout(new FlowLayout());
    myPanel2.setSize(new Dimension(640, 400));
    myPanel2.setLayout(null);
    myPanel2.setBackground(Color.WHITE);
    btnNewWorm.setBounds(100, 410, 200, 30);
    btnKillWorm.setBounds(340, 410, 200, 30);
    add(btnNewWorm);
    add(btnKillWorm);
    add(myPanel2);
    add(myPanel);
    btnNewWorm.addActionListener(this);
    btnKillWorm.addActionListener(this);
}
public void actionPerformed(ActionEvent AE) {
    if(AE.getSource() == btnNewWorm){
        DrawThread dw = new DrawThread(myPanel2);
        worms.add(dw);
        System.out.println("New worm!");
    }
    if(AE.getSource() == btnKillWorm){
        System.out.println("Kill worm!");
    }
}
}

DrawThread类:

public class DrawThread extends Thread implements Runnable{
Graphics2D g, graph;
private int sleepTime, wormDiameter, hue, saturation, brightness, randomWidth, randomHeight;
public DrawThread(int sleepTime, int wormDiamter, int hue, int saturation, int brightness, int randomWidth, int randomHeight) {
    this.sleepTime = sleepTime;
    this.brightness = brightness;
    this.hue = hue;
    this.saturation = saturation;
    this.randomWidth = randomWidth;
    this.randomHeight = randomHeight;
}
public int getSleepTime(){
    return sleepTime;
}
public void setSleepTime(int sleepTime){
    this.sleepTime = sleepTime;
}
public DrawThread(JPanel myPanel2){
    Random rand = new Random();
    //get panel dimensions
    int panelWidth = myPanel2.getWidth();
    int panelHeight = myPanel2.getHeight();
    //worm location
    randomWidth = rand.nextInt(panelWidth);
    randomHeight = rand.nextInt(panelHeight);
    //worm size
    wormDiameter = rand.nextInt(7)+3;
    //worm color 
    hue = rand.nextInt(255);
    saturation = rand.nextInt(255);
    brightness = rand.nextInt(255);
    Color color = new Color(hue, saturation, brightness);
    //sleep
    sleepTime = rand.nextInt(80) + 20;
    //Graphics
    System.out.println(myPanel2);
    g = (Graphics2D) myPanel2.getGraphics();
    g.setColor(color);
    Ellipse2D.Double ellipse2D = new Ellipse2D.Double(randomWidth, randomHeight, wormDiameter, wormDiameter);
    g.fill(ellipse2D);
    Thread thread1 = new Thread(new DrawThread(sleepTime, wormDiameter, hue, saturation, brightness, randomWidth, randomHeight));
    thread1.start();
}
public void run(){
    try { 
        while(true) { 
            sleep(sleepTime);
            Ellipse2D.Double ellipse2D = new Ellipse2D.Double(randomWidth + 10, randomHeight + 10, wormDiameter, wormDiameter);
            g.fill(ellipse2D); //This is where I receive my error
            System.out.println("ran");
        }
    } 
    catch (InterruptedException e) {
        System.out.println("ERROR!");
    }
}
public String toString() {
    String result = "SleepTime: " + sleepTime + "nWorm Diameter: " + wormDiameter
            + "nHue: " + hue + "nSaturation: " + saturation + "nBrightness: "
            + brightness + "nWidth: " + randomWidth + "nHeight: " + randomHeight;
    return result;
}
}

当尝试填充图形时,我在DrawThread类中收到错误,给我一个空指针异常。下面是准确的错误:

Exception in thread "Thread-4" java.lang.NullPointerException
at Main.DrawThread.run(DrawThread.java:78)
at java.lang.Thread.run(Unknown Source)

我如何修复我的代码,使我不得到这个错误?

实例变量g为空。这应该在相应的构造函数中给出一个值。

你已经在DrawThread(JPanel)构造函数中设置了它,但其他的没有。

你可能想要考虑这是否应该是一个实例变量。如果是,则在另一个构造函数中适当地设置它。

[Edit]您可以将JPanel传递给其他构造函数并从中设置g。

public DrawThread(int sleepTime, int wormDiamter, int hue, int saturation, int brightness, int randomWidth, int randomHeight, JPanel panel) {
    //...
    g = (Graphics2D) panel.getGraphics();
    //...
}

您的DrawThread中对Graphics2D的引用是null。虽然这是最初的问题,但它是一个大问题的征兆。

Swing中的自定义绘制应该在Swing组件的绘制方法的上下文中完成,最好是paintComponent方法。有关更多详细信息,请参见执行自定义绘画。

有很多原因…

首先

Swing不是线程安全的。这意味着,即使您获得了对组件Graphics上下文的引用,当您尝试在Swing绘制过程尝试同时更新时进行绘制时,您最终可能会使用脏油漆。

作为旁注,JComponent#getGraphics可以返回null,并且只不过是上次绘制周期的快照,这意味着绘制到它的任何内容将在下一个绘制周期中被擦除。

这也意味着,对UI的任何交互或修改都应该在事件调度线程的上下文中完成,而不是在任何其他Thread上下文中完成。

请参阅Swing中的并发以了解更多细节。

一般来说,对于大多数基本动画,您应该考虑使用javax.swing.Timer而不是Thread。这将确保计时器的所有"tick"都是从事件调度线程的上下文中调用的…

其次

Swing使用被动渲染算法。这意味着油漆更新是不规律的,可能有很多原因,其中大多数是你无法控制的。

为了能够处理这个问题,您需要在Swing的绘制过程的上下文中进行绘制,通常是通过重写paintComponent

通常绘画就像在画布上绘画,你之前在上面画过的东西会重新画,paintComponent的工作之一是擦除视图并从头开始画。

相关内容

最新更新