java - 重绘线程不会重绘内部类 JPanel
问题描述
我想在摇摆中制作一个小雨程序,但由于某种原因,我无法重新绘制另一个班级的面板。这次我尝试为面板使用内部类,但它似乎不适用于从另一个类/线程重新绘制它。有人知道为什么吗?
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 类(带有内部类 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);
}
}
}
}
画家:
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();
}
}
}
控制台输出:
想画。。
打钩
想画。。
打钩
...
预期输出:
想画。。
绘画
打钩
想画。。
...
该线程确实有效,但它从不调用面板中的 paintComponent(Graphics g) 函数
解决方案
所有 Swing 应用程序都必须在它们自己的线程上运行,称为EDT。(希望您通过调用SwingUtilities#invokelater方法来启动您的应用程序)。因此,在 Event Dispatch Thread 之外重新绘制组件确实是个坏主意。不要创建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
}
此外,当您使用@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);
}
}
SSCCE 后更新
为了绘制组件,它必须有一个父级。您UserPanel p = new UserPanel(this);
,但您从未将其添加到框架中:
UserPanel p = new UserPanel(this);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(p);
完整的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);
}
}
}
不要忽视SwingUtilities.invokeLater
.
推荐阅读
- html - 存储在“/images”目录中的静态内容不被提供,因为 URL 变得相对于“/css”目录
- c# - .NET Core 中的用户配置设置
- bokeh - 在 JupyterLab 的单元格中未呈现 Holoviews 图
- javascript - 将 JSON 对象从 Flask 传递到 JavaScript,但特殊字符未编码
- python - 在 spyder 中存储已经生成的数据
- jsp - Highlighting selected tab in nav header, JSP
- python - 无参数实例化
- ios - http 204 响应在 iOS 中返回一个空白页面,有没有办法阻止这种情况?
- mysql - sql UPDATE 几行
- reactjs - 使用 jest/enzyme 测试调用 setState 的异步 componentDidMount