简体   繁体   English

重绘线程不会重绘内部类JPanel

[英]Repainting-Thread doesn't repaint Inner-Class JPanel

I want to make a little rain program in swing, but for some reason I cannot repaint the panel from another class. 我想在秋千上做一些下雨程序,但是由于某种原因,我无法从另一个班级重新粉刷面板。 I tried using an inner class for the panel this time, but it doesn't seem to work with repainting it from another class/thread. 这次我尝试为面板使用内部类,但是似乎无法从另一个类/线程重新粉刷它。 Does someone know why? 有人知道为什么吗?

sscce: sscce:

import javax.swing.JPanel;
import javax.swing.Timer;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;

public class UI extends JFrame {

    public static void main(String[] args) {
        UI myProgram = new UI();
        myProgram.setVisible(true);
    }

    public UI() {
        this.setSize(new Dimension(500,300));
        this.setBackground(Color.WHITE);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        UserPanel p = new UserPanel(this);
    }

    public class UserPanel extends JPanel implements ActionListener {

        private Timer time = new Timer(1, this);
        private UI myFrame;

        public UserPanel(UI myFrame) {
            this.myFrame = myFrame;
            this.setSize(myFrame.getSize());

            time.start();
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            repaint();
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            System.out.println("painting");
            g.setColor(Color.BLACK);
            g.fillRect(this.getWidth()/2, this.getHeight()/2, 50,50);
        }
    }
}

UI Class (with inner class JPanel): UI类(具有内部类JPanel):

package Rain;

import javax.swing.JPanel;
import javax.swing.Timer;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JFrame;

public class UI extends JFrame {


    public UI() {
        this.setSize(new Dimension(500,300));
        this.setBackground(Color.WHITE);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        UserPanel p = new UserPanel(this);
    }


    private class UserPanel extends JPanel implements ActionListener {

        private Timer time = new Timer(1, this);
        private UI myFrame;
        private ArrayList<Raindrop> rain = new ArrayList<Raindrop>();
        private static final int AMOUNT = 50;
        private Random rand = new Random();

        public UserPanel(UI myFrame) {
            this.myFrame = myFrame;
            this.setSize(myFrame.getSize());

            for(int i = 0; i < AMOUNT; i++) {
                createRain();
            }
            new Painter(this);
            time.start();
        }

        public void createRain() {
            float distance = rand.nextFloat() * 90 + 10;
            int x = rand.nextInt(this.getWidth());
            int y = 100;

            rain.add(new Raindrop(distance,x,y));
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("tick");
            for(Raindrop r : rain) {
                r.fall();
            }
        }

        public void paintComponent(Graphics g) {
            System.out.println("painting");
            g.setColor(this.getBackground());
            g.fillRect(0,0,this.getWidth(),this.getHeight());
            for(Raindrop r : rain) {
                r.draw(g);
            }
        }

    }

}

Painter: 画家:

package Rain;

import javax.swing.JPanel;

public class Painter extends Thread {

    private JPanel p;

    public Painter(JPanel p) {
        this.p = p;
        this.start();
    }

    public void run() {
        while(true) {
            System.out.println("trying to paint..");
            p.repaint();
        }
    }

}

Console Output: 控制台输出:

trying to paint.. 试图画..

tick

trying to paint.. 试图画..

tick

... ...

Expected Output: 预期产量:

trying to paint.. 试图画..

painting 绘画

tick

trying to paint.. 试图画..

... ...

The thread does work but it never calls the paintComponent(Graphics g) function in the panel 该线程确实有效,但是它从未在面板中调用paintComponent(Graphics g)函数

All Swing applications must run on their own thread, called EDT . 所有Swing应用程序都必须在自己的称为EDT的线程上运行。 (Hopefully, you start your application by calling SwingUtilities#invokelater method). (希望您通过调用SwingUtilities#invokelater方法来启动应用程序)。 So, repainting a component outside of Event Dispatch Thread is really bad bad (bad) idea. 因此,在事件调度线程之外重新绘制组件确实是一个糟糕的想法。 Instead of creating new Thread , repaint the component inside javax.swing.Timer 's action listener since it will run in EDT. 不用创建new Thread ,而是在javax.swing.Timer的动作侦听器中重新绘制组件,因为它将在EDT中运行。

@Override
public void actionPerformed(ActionEvent e) {
    System.out.println("tick");
    for(Raindrop r : rain) {
        r.fall();
    }
    repaint(); //repaint in EDT
}

Also, when you @Override paintComponent method, always start by calling super.paintComponent(g) ; 另外,当您@Override paintComponent方法时,请始终通过调用super.paintComponent(g)

public void paintComponent(Graphics g) {
    super.paintComponent(g);//let component get painted normally
    System.out.println("painting");
    g.setColor(this.getBackground());
    g.fillRect(0,0,this.getWidth(),this.getHeight());
    for(Raindrop r : rain) {
        r.draw(g);
    }
}

UPDATE after your SSCCE 在SSCCE之后更新

In order a component to get painted, it must have a parent. 为了绘制组件,它必须有一个父对象。 You UserPanel p = new UserPanel(this); 您的UserPanel p = new UserPanel(this); but you never add it to the frame: 但您永远不会将其添加到框架中:

UserPanel p = new UserPanel(this);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(p);

The complete SSCCE: 完整的SSCCE:

public class UI extends JFrame {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> { //Run in EDT
            UI myProgram = new UI();
            myProgram.setVisible(true);
        });
    }

    public UI() {
        super("title");//call super for frame
        this.setSize(new Dimension(500, 300));
        this.setBackground(Color.WHITE);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        UserPanel p = new UserPanel(this);

        //Use border layout to make p fit the whole frame
        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(p, BorderLayout.CENTER);
    }

    public class UserPanel extends JPanel implements ActionListener {

        private Timer time = new Timer(1, this);
        private UI myFrame;

        public UserPanel(UI myFrame) {
            this.myFrame = myFrame;
            this.setSize(myFrame.getSize());

            time.start();
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            repaint();
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            System.out.println("painting");
            g.setColor(Color.BLACK);
            g.fillRect(this.getWidth() / 2, this.getHeight() / 2, 50, 50);
        }
    }
}

Don't ignore the SwingUtilities.invokeLater . 不要忽略SwingUtilities.invokeLater

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM