java - 如何在每个并发线程中用 Java Swing 进行绘制?
问题描述
我的程序的目标是从随机生成的点中绘制正方形。我想在每个线程生成一组正方形后立即显示它们。但是,一旦所有线程都运行完毕,只会显示一组方块。我使用了swinginvoke,并且很好奇repaint() 是否存在问题,因为所有线程都达到了repaint,但直到最后一个线程完成后才进行绘制,并最终相互重叠。我也不希望在每个线程之间共享“storedata”变量,但它的每个实例都将数据保存在其中。我试图通过清除它来解决这个问题,但它没有奏效。该程序由一个自定义线程类、一个启动线程的主类、一个 GUI 类和一个绘制正方形的自定义 jpanel 组成。
import java.awt.*;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
class Action extends Thread {
private Random rand = new Random();
private static CopyOnWriteArrayList<Point> storedata = new CopyOnWriteArrayList<>();
static volatile CopyOnWriteArrayList<CopyOnWriteArrayList<Point>> finallist = new CopyOnWriteArrayList<>();
private GUI g;
Action(GUI g) {
this.g = g;
}
private void generatePoint() {
int x = rand.nextInt(500);
int y = rand.nextInt(500);
Point p = new Point(x,y);
storedata.add(p);
}
public void run() {
for (int i = 0; i < 5; i++) {
generatePoint();
}
CopyOnWriteArrayList<Point> copy = new CopyOnWriteArrayList<>(storedata);
finallist.add(copy);
SwingUtilities.invokeLater(() -> {
try {
Thread.sleep(500);
System.out.println("ARRAY = " + copy.toString());
g.setSolution(copy);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
storedata.clear();
}
}
public class Main {
public static void main(String[] args) {
GUI g = new GUI();
g.setVisible(true);
Thread[] threads = new Thread[5];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Action(g);
threads[i].start();
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.CopyOnWriteArrayList;
public class GUI extends JFrame implements ActionListener {
private CustomPanel c;
GUI() {
initialize();
}
private void initialize() {
this.setLayout(new FlowLayout());
c = new CustomPanel(500,500);
this.setSize(1000,1000);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(c);
this.setTitle("Seat Placement Program");
}
@Override
public void actionPerformed(ActionEvent e) {
}
public void setSolution(CopyOnWriteArrayList<Point> room) {
c.setRoom(room);
c.setPaint(true);
c.repaint();
}
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.CopyOnWriteArrayList;
public class CustomPanel extends JPanel implements ActionListener {
private Border blackline = BorderFactory.createLineBorder(Color.black);
private boolean paint;
private CopyOnWriteArrayList<Point> room;
public CustomPanel(int h, int w) {
room = new CopyOnWriteArrayList<>();
paint = false;
this.setPreferredSize(new Dimension(w,h));
this.setBorder(blackline);
}
@Override
public void actionPerformed (ActionEvent e) {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if(paint) {
for(Point p : room) {
g.drawRect((int) p.getX(),(int) p.getY(),20,20);
}
}
}
void setPaint(boolean b) {
paint = b;
}
public void setRoom(CopyOnWriteArrayList<Point> room) {
this.room = room;
}
}
解决方案
在 Swing 中,绘画只能在Event Dispatching Thread中完成。实现你想要的方法是当线程完成时,将形状(可能连同颜色)添加到你想要它的 JComponent 中的集合中(你需要创建一个具有这样一个集合的自定义 JComponent,并且在添加形状时应该在它周围同步,在绘制形状时应该在paintComponent中同步)。然后在您的非 EDT 线程中,调用 myJComponent.repaint()。可以在所有非 EDT 线程中调用它。
例子:
public class MyPanel extends JPanel {
Collection<Shape> myShapes = ...;
public void paintComponent(Graphics g) {
... // do any other non-shape related painting
Graphics2D g2 = (Graphics2D) g;
synchronized (myShapes) {
for (Shape s : myShapes) {
//TODO: do you want to set a color?
g.draw(s);
//TODO: do you want to fill the shapes?
g.fill(s);
}
}
}
}
然后在你的线程中:
public void run() {
...
Shape newShape = ...;
synchronized (myPanel.myShapes) {
//add newShape to the collection
}
myPanel.repaint();
}
推荐阅读
- javascript - 如何检查对象数组中键的所有值是否为0?
- jquery - nanoGallery 显示错误
- android - 我如何重构导航抽屉
- python - 如何拆分列表元素并根据空格分隔符替换它们?
- html - 调整窗口大小时如何阻止右侧浮动导航内容溢出
- python - tkinter 组合框,从同一文件运行时,条目正确显示。但是当从另一个模块调用相同的值时不显示正确的值
- flutter - 条件“满足”后未返回小部件 (!snapshot.hasData)
- java - 无法使用带有 Spring Batch + Spring Boot 的 XStreamMarshaller 解析 xml
- android - 如何在android中添加21天的日期选择器值
- github - 如何在不共享 URL 的情况下将来自 Github 的提交的 git diff 发送给其他人?