Java 图形2d对象的引用在孤立线程中不起作用
我试图在JPanel中使用Graphics2D设计一个简单的游戏。我可以通过重写paintComponent方法来绘制法线对象。但是,当我在孤立线程中引用Graphics2D对象时,它不起作用。我哪里做错了Java 图形2d对象的引用在孤立线程中不起作用,java,multithreading,swing,graphics2d,paintcomponent,Java,Multithreading,Swing,Graphics2d,Paintcomponent,我试图在JPanel中使用Graphics2D设计一个简单的游戏。我可以通过重写paintComponent方法来绘制法线对象。但是,当我在孤立线程中引用Graphics2D对象时,它不起作用。我哪里做错了 public void paintComponent(Graphics g) { super.paintComponent(g); g2d = (Graphics2D) g; g2d.drawString("sample",60,100); //Works fin
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.drawString("sample",60,100); //Works fine
if(<Certain Condition>){
new Thread(new Runnable(){
//Some Code Here
public void run() {
try{
g2d.drawString("sample2",60,100); //Does not work.. :(
System.out.println("Test Print"); //Shows Output
}
catch (Exception e)
{
}
}
}).start();
}
}
下面是完整的代码供参考。这本质上是一种“乒乓球”游戏。这很好,但我不能强调当球击中前锋时得分的增加。代码的重要部分将突出显示。是SSCCE
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
public class MovingBall extends JPanel {
int XPos, YPos;
int speedX, speedY;
int diameter;
private JButton jButton1 = new JButton();
private JButton jButton2 = new JButton();
private JLabel jLabel1 = new JLabel();
private static Timer timer;
private static MovingBall movingball;
private int w,h;
private int strikerHeight;
private int strikerWidth;
private int score;
private boolean isBallMoving;
int strikerYPos;
Graphics2D g2d;
public MovingBall() {
//Striker Properties
strikerHeight = 100;
strikerWidth = 20;
strikerYPos = strikerHeight/2;
//Ball Properties
isBallMoving = false;
XPos = strikerWidth + 5;
YPos = 0;
Random r = new Random();
speedX = 2+ Math.abs(r.nextInt()) % 5;
speedY = 2+ Math.abs(r.nextInt()) % 5;
diameter = 50;
//UI Objects
try {
jbInit();
} catch (Exception e) {
e.printStackTrace();
}
movingball = this; //Helps to access the current class object in inner classes
//Create a timer for animation
timer = new Timer(1, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
movingball.repaint();
}
});
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
Dimension size = getSize();
Insets insets = getInsets();
w = size.width - insets.left - insets.right;
h = size.height - insets.top - insets.bottom;
//Paint the striker
g2d.setColor(Color.DARK_GRAY);
if(strikerYPos < strikerHeight/2) //Top End
g2d.fillRect(0,0, strikerWidth, strikerHeight);
else if(strikerYPos > (h-strikerHeight/2)) //Bottom End
g2d.fillRect(0,h-strikerHeight, strikerWidth, strikerHeight);
else //Anywhere in the middle
g2d.fillRect(0,strikerYPos - (strikerHeight/2), strikerWidth, strikerHeight);
//Paint the ball
if (isBallMoving) {
XPos += speedX;
YPos += speedY;
g2d.drawOval(XPos, YPos, diameter,diameter);
if((XPos+diameter) >= w)
{
//speedX *= -1;
speedX = ((int)Math.signum((double)speedX))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
XPos = w-diameter-1;
}
if(XPos <= strikerWidth)
{
if((YPos+diameter/2) >= (strikerYPos-strikerHeight/2) && (YPos+diameter/2) <= (strikerYPos+strikerHeight/2))
{
score++;
//////////////////////////////////////////////////////////////////////
/////THIS IS THE PART TO FOCUS ON///////////////////////////////////////
/////WHEN THE BALL HITS THE STRIKER, I SHOW A '+1' TEXT FADING UPWARDS FROM THE POINT OF HIT
/////(THIS IS TO HIGHLIGHT A +1 INCREASE IN SCORE)///////////////////
//////NOW SINCE THE BALL MAY HIT THE STRIKER AGAIN BEFORE THE PREVIOUS +1 HAS COMPLETELY FADED,
//////I HAVE MADE THIS SIMPLE THREAD TO CREATE A +1 EVERY TIME THERE IS A HIT. SO THERE CAN BE MULTIPLE
//////+1 ON THE SCREEN.
//-------------------------------SADLY, SOMETHING IS WRONG-------------------
//Print a '+1' to show score increase
new Thread(new Runnable(){
int yStart = strikerYPos;
int fadeLength = 0;
Timer pointTimer;
int MAX_FADE_LEN = 50;
public void run() {
try
{
pointTimer = new Timer(1, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if(fadeLength >= MAX_FADE_LEN)
pointTimer.stop();
g2d.setColor(new Color(0,0,0,255));
g2d.setFont(new Font("Times",Font.BOLD,20));
g2d.drawString("+1",60,yStart - fadeLength);
g2d.drawOval(100,100,50,50);
System.out.println("Drawn +1 at x = " + 60 + " y = " + (yStart - fadeLength));
fadeLength++;
}
});
pointTimer.start();
}
catch (Exception e)
{
}
}
}).start();
////////////////THREAD ENDS HERE//////////////////////
}
else
{
score--;
}
//SHOW THE SCORE ON THE LABEL
jLabel1.setText("Score: " + score);
speedX = ((int)Math.signum((double)speedX))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
XPos = strikerWidth+1;
}
if(YPos <= 0)
{
speedY = ((int)Math.signum((double)speedY))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
YPos = 0;
}
if((YPos+diameter) >= h)
{
speedY = ((int)Math.signum((double)speedY))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
YPos = h-diameter;
}
} else {
g2d.drawOval(XPos,YPos,diameter,diameter);
return;
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Magic Ball");
movingball = new MovingBall();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(movingball);
frame.setSize(450, 700);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void jbInit() throws Exception {
jButton1.setText("Start");
jButton1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);
}
});
jButton2.setText("Stop");
jButton2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton2_actionPerformed(e);
}
});
jLabel1.setText("Score:0");
this.add(jButton1, null);
this.add(jButton2, null);
this.add(jLabel1, null);
this.setBackground(Color.white);
this.addMouseMotionListener(new MouseMotionListener() {
public void mouseMoved(MouseEvent e) {
int coordX = e.getX();
if(coordX < 200)
strikerYPos = e.getY();
}
public void mouseDragged(MouseEvent e) {
}
});
}
private void jButton1_actionPerformed(ActionEvent e) {
if(!isBallMoving)
isBallMoving = true;
}
private void jButton2_actionPerformed(ActionEvent e) {
isBallMoving = false;
}
}
paintComponent中的所有内容都会在API中实现的每个鼠标、键和内部方法上自动重新绘制,然后您的线程可能永远不会结束,可能会有一堆并发线程,没有任何内容被重新绘制、显示
到Swing GUI的输出必须在EDT上完成
使用Swing计时器而不是new Threadnew Runnable{
呼叫重绘
paintComponent中的所有内容都会在API中实现的每个鼠标、键和内部方法上自动重新绘制,然后您的线程可能永远不会结束,可能会有一堆并发线程,没有任何内容被重新绘制、显示
到Swing GUI的输出必须在EDT上完成
使用Swing计时器而不是new Threadnew Runnable{
呼叫重绘
打印graphicsobject,如果它在paintComponent中的调用之间发生更改,则表示您传递给孤立线程的对象是死对象,swing不再使用它在当前组件上进行绘制。打印graphicsobject,如果它在paintComponent中的调用之间发生更改,则表示您传递给孤立线程的对象是一个死对象,swing不再使用它在当前组件上绘制。据我所知-您将Graphics2D对象保存到g2d变量中,并尝试稍后从单独的线程在其上绘制一些内容?如果是,请不要这样做。这是一件非常糟糕的事情。真的 如果要修改动画/更改在组件上绘制的内容,只需更改影响绘制的数据/模型,然后重新绘制整个组件或其修改部分组件边界内的任何矩形 例如,在您的示例中-将绘制的字符串坐标保持在paint方法之外,并在单独的线程中修改它们,然后每次更改它们时只调用组件上的repaint。每次重新绘制时,字符串将以更新的坐标绘制 还请注意,重新绘制可能会在EDT事件调度线程之外调用,因为它无论如何都会在EDT中执行实际的重新绘制 下面是一些随机的动画示例: 你可以在这里找到: 使用单个初始元素绘制和动画所基于的数据列表 通过鼠标交互修改数据 对任何数据更改进行正确的画布更新
示例不是太优化,但应该足以理解这个概念。据我所知-您将Graphics2D对象保存到g2d变量中,并尝试稍后从单独的线程在其上绘制一些内容?如果是,请不要这样做。这是一件非常糟糕的事情。真的 如果要修改动画/更改在组件上绘制的内容,只需更改影响绘制的数据/模型,然后重新绘制整个组件或其修改部分组件边界内的任何矩形 例如,在您的示例中-将绘制的字符串坐标保持在paint方法之外,并在单独的线程中修改它们,然后每次更改它们时只调用组件上的repaint。每次重新绘制时,字符串将以更新的坐标绘制 还请注意,重新绘制可能会在EDT事件调度线程之外调用,因为它无论如何都会在EDT中执行实际的重新绘制 下面是一些随机的动画示例: 你可以在这里找到: 使用单个初始元素绘制和动画所基于的数据列表 通过鼠标交互修改数据 对任何数据更改进行正确的画布更新
示例并没有太优化,但应该足以理解这个概念。Swing是一个独立于平台的Java模型-视图-控制器GUI框架,遵循单线程编程模型
您应该使用dispatcher。Swing是一个独立于平台的Java模型-视图-控制器GUI框架,遵循单线程编程模型
<>你应该使用调度器。 < P>我不认为很多人会认为差不多250个LOC是“短”,尽管我必须承认我在编写SSCCE文档时故意含糊不清。Otoh我把我的短源修改为一个动画例子,它显示了鼠标点击的“渐变效果”。欧 此源代码显示如何在5秒内更改绘制的字符串。它使用相同的线程EDT处理主反弹球和淡入淡出动画
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.*;
class ShapeCollision {
private BufferedImage img;
private Area walls;
int x;
int y;
int xDelta = 3;
int yDelta = 2;
ArrayList<Strike> strikes;
/**
* A method to determine if two instances of Area intersect
*/
public boolean doAreasCollide(Area area1, Area area2) {
boolean collide = false;
Area collide1 = new Area(area1);
collide1.subtract(area2);
if (!collide1.equals(area1)) {
collide = true;
}
Area collide2 = new Area(area2);
collide2.subtract(area1);
if (!collide2.equals(area2)) {
collide = true;
}
return collide;
}
ShapeCollision() {
int w = 400;
int h = 200;
img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
final JLabel imageLabel = new JLabel(new ImageIcon(img));
x = w / 2;
y = h / 2;
strikes = new ArrayList<Strike>();
MouseListener strikeListener = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
Strike s = new Strike(e.getPoint(),System.currentTimeMillis());
strikes.add(s);
}
};
imageLabel.addMouseListener(strikeListener);
walls = new Area(new Rectangle(0, 0, w, h));
ActionListener animate = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
animate();
imageLabel.repaint();
}
};
Timer timer = new Timer(50, animate);
timer.start();
JOptionPane.showMessageDialog(null, imageLabel);
timer.stop();
}
public void animate() {
Graphics2D g = img.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLACK);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
x += xDelta;
y += yDelta;
int s = 15;
Area player = new Area(new Ellipse2D.Double(x, y, s, s));
// Acid test of edge collision;
if (doAreasCollide(player, walls)) {
if (x + s > img.getWidth() || x < 0) {
xDelta *= -1;
}
if (y + s > img.getHeight() || y < 0) {
yDelta *= -1;
}
}
g.setColor(Color.ORANGE);
g.setColor(Color.YELLOW);
g.fill(player);
for (Strike strike : strikes) {
strike.draw(g);
}
g.dispose();
}
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
new ShapeCollision();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
class Strike {
private Point point;
private long started;
private final long DURATION = 5000;
private boolean expired = false;
Strike(Point point, long time) {
this.point = point;
started = time;
}
public void draw(Graphics g) {
long now = System.currentTimeMillis();
long age = now - started;
if (age>DURATION) {
expired = true;
return;
}
double fraction = 1d-((double)age/(double)DURATION);
int alpha = (int)(fraction*255d);
Color c = new Color(255,255,255,alpha);
g.setColor(c);
String s = point.x + "," + point.y;
g.drawString( s, point.x, point.y );
}
public boolean isExpired() {
return expired;
}
}
我不认为很多人会考虑。 虽然我必须承认我在写SSCCE文件时故意含糊其辞,但几乎250 LOC被认为是“短的”。OTOH我将我看到的较短的源代码改编为一个动画示例,该示例显示了鼠标点击时的“淡入淡出效果”。让它适应你的需要是留给你的一个练习 此源显示如何在5秒内更改绘制的字符串。它对主反弹球和淡入淡出动画使用相同的线程EDT
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.*;
class ShapeCollision {
private BufferedImage img;
private Area walls;
int x;
int y;
int xDelta = 3;
int yDelta = 2;
ArrayList<Strike> strikes;
/**
* A method to determine if two instances of Area intersect
*/
public boolean doAreasCollide(Area area1, Area area2) {
boolean collide = false;
Area collide1 = new Area(area1);
collide1.subtract(area2);
if (!collide1.equals(area1)) {
collide = true;
}
Area collide2 = new Area(area2);
collide2.subtract(area1);
if (!collide2.equals(area2)) {
collide = true;
}
return collide;
}
ShapeCollision() {
int w = 400;
int h = 200;
img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
final JLabel imageLabel = new JLabel(new ImageIcon(img));
x = w / 2;
y = h / 2;
strikes = new ArrayList<Strike>();
MouseListener strikeListener = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
Strike s = new Strike(e.getPoint(),System.currentTimeMillis());
strikes.add(s);
}
};
imageLabel.addMouseListener(strikeListener);
walls = new Area(new Rectangle(0, 0, w, h));
ActionListener animate = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
animate();
imageLabel.repaint();
}
};
Timer timer = new Timer(50, animate);
timer.start();
JOptionPane.showMessageDialog(null, imageLabel);
timer.stop();
}
public void animate() {
Graphics2D g = img.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLACK);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
x += xDelta;
y += yDelta;
int s = 15;
Area player = new Area(new Ellipse2D.Double(x, y, s, s));
// Acid test of edge collision;
if (doAreasCollide(player, walls)) {
if (x + s > img.getWidth() || x < 0) {
xDelta *= -1;
}
if (y + s > img.getHeight() || y < 0) {
yDelta *= -1;
}
}
g.setColor(Color.ORANGE);
g.setColor(Color.YELLOW);
g.fill(player);
for (Strike strike : strikes) {
strike.draw(g);
}
g.dispose();
}
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
new ShapeCollision();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
class Strike {
private Point point;
private long started;
private final long DURATION = 5000;
private boolean expired = false;
Strike(Point point, long time) {
this.point = point;
started = time;
}
public void draw(Graphics g) {
long now = System.currentTimeMillis();
long age = now - started;
if (age>DURATION) {
expired = true;
return;
}
double fraction = 1d-((double)age/(double)DURATION);
int alpha = (int)(fraction*255d);
Color c = new Color(255,255,255,alpha);
g.setColor(c);
String s = point.x + "," + point.y;
g.drawString( s, point.x, point.y );
}
public boolean isExpired() {
return expired;
}
}
我已经在程序中详细实现了swing定时器和重绘。没有在这里写完整的代码。。定时器会影响这个吗?我已经在程序中详细实现了swing定时器和重绘。没有在这里写完整的代码。。计时器是否会影响这一点?为了更快地获得更好的帮助,请发布一个。是的,Andrew,为了更好地理解,现在添加了完整的代码。没有Ashis。这既不是“完整代码”,也不是没有主字符串[]或其他东西可以在屏幕上显示,而且它肯定不是SSCCE,这是我建议您发布的..@Andrew。。请现在检查。它符合SSCCE标准。感谢您的回复。为了更快地获得更好的帮助,请发布一个。是的,Andrew,为了更好地理解,现在添加了完整的代码。没有Ashis。这既不是“完整代码”,也不是没有主字符串[]或其他东西可以在屏幕上显示,而且它肯定不是SSCCE,这是我建议您发布的..@Andrew。。请现在检查。它符合SSCCE标准。谢谢回复。但是我已经在全局范围内声明了Graphics2D引用变量。现在,正在使用单独的计时器重复调用repaint方法。这会定期刷新引用。我认为这应该是可行的。但是我已经在全局范围内声明了Graphics2D引用变量。现在,正在使用单独的计时器重复调用repaint方法。这会定期刷新引用。我想这应该行得通谢谢你安德鲁我想我现在明白了。很高兴你把它整理好了谢谢你,安德鲁我想我现在明白了。很高兴你把它整理好了