基本上,关键是当单击添加蠕虫按钮时蠕虫会扩展。我试图让一个蠕虫扩展并最终使用多线程允许多个蠕虫在屏幕上扩展。目前我很难让一只蠕虫扩大。以下是我的代码,以下类,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)
如何修复我的代码,以免出现此错误?
答案 0 :(得分:1)
实例变量g为null。这应该在适当的构造函数中给出一个值。
您已在DrawThread(JPanel)构造函数中设置它,但未在另一个构建函数中设置它。
您可能想要考虑这是否应该是实例变量。如果是这样,请在其他构造函数中正确设置。
[编辑]你可以将JPanel传递给另一个构造函数并从中设置g。
public DrawThread(int sleepTime, int wormDiamter, int hue, int saturation, int brightness, int randomWidth, int randomHeight, JPanel panel) {
//...
g = (Graphics2D) panel.getGraphics();
//...
}
答案 1 :(得分:1)
Graphics2D
中对DrawThread
的引用为null
。虽然这是最初的问题,但这是一个大问题的症状。
Swing中的自定义绘制应该在Swing组件的绘制方法的上下文中完成,最好是paintComponent
方法。有关详细信息,请参阅Performing Custom Painting。
这有很多原因......
Swing不是线程安全的。这意味着,即使您获得了对组件Graphics
上下文的引用,当您尝试在Swing绘制过程中进行绘制时,最终可能会使用脏涂料尝试同时更新。
作为旁注JComponent#getGraphics
可以返回null
,而不仅仅是上一个绘制周期的快照,这意味着在下一个绘制周期中绘制的任何内容都将被删除。
这也意味着,对UI的任何交互或修改都应该在事件调度线程的上下文中完成,而不是来自任何其他Thread
上下文。
请查看Concurrency in Swing了解详情。
一般来说,对于大多数基本动画,您应该考虑使用javax.swing.Timer
代替Thread
。这将确保在事件调度线程的上下文中调用计时器的所有“滴答”......
Swing使用被动撕裂算法。这意味着油漆更新会以不规则的间隔发生,并且可能由于多种原因而发生,其中大多数原因都超出了您的控制范围。
为了能够解决这个问题,你需要在Swing的绘画过程中进行绘画,通常是覆盖paintComponent
。
通常绘画就像在画布上画画一样,在绘制之前你画的是什么,paintComponent
的一个作业是擦除视图并从头开始重绘。