java - 图形 2d 对象的引用在孤立线程中不起作用

标签 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 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 的输出必须在美国东部时间完成

  • 使用 Swing Timer 而不是 new Thread(new Runnable(){

  • 调用重绘()

关于java - 图形 2d 对象的引用在孤立线程中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16545075/

相关文章:

java - 就像 Java 的对象类一样,将类保留为项目的最终基类是一个好习惯吗?

java - Maven 没有从 settings.xml 中为存储库选择用户名

java - JPA @SqlResultSetMapping 无法处理要映射到空 POJO 的空 sql 结果 - 而是抛出异常

c# - 在 C# 中锁定线程

c# - 并行树打印方法中的错误

java - findComponentAt 返回 JList 的 self

java - JScrollPane 和文本换行尚未运行

java - Apache POI 在 tomcat 中崩溃但在 IDE 中不崩溃

c++ - pthread - 使用线程访问多个对象

java - 在不同类的 JPanel 之间切换