简体   繁体   English

如何在每个并发线程中在 Java Swing 中绘画?

[英]How to paint in Java Swing in every concurrent thread?

The goal of my program is to draw squares from randomly generated points.我的程序的目标是从随机生成的点中绘制正方形。 I want to display a set of squares as soon as they are generated by each thread.我想在每个线程生成一组方块后立即显示它们。 However, only one set of squares displays, once all the threads are done running.但是,一旦所有线程都运行完毕,只会显示一组方块。 I have used swinginvoke, and am curious as if there is a problem with repaint() since all the threads reach repaint but don't paint until the final thread is done, and end up overlapping each other.我使用过swinginvoke,并且很好奇,好像repaint() 有问题,因为所有线程都达到了重绘但在最后一个线程完成之前不会绘制,并最终相互重叠。 I also don't want the "storedata" variable to be shared between each thread but every instance of it keeps the data in it.我也不希望在每个线程之间共享“storedata”变量,但它的每个实例都将数据保存在其中。 I tried to fix this by clearing it, but it hasn't worked.我试图通过清除它来解决这个问题,但它没有奏效。 The program consists of a custom thread class, a main class where the threads get started, a GUI class, and a custom jpanel where the squares are drawn.该程序由自定义线程类、线程启动的主类、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;
    }


}

In swing, painting should only be done in the Event Dispatching Thread .在 Swing 中,绘制只能在Event Dispatching Thread 中完成 The way to achieve what you want is when the thread has finished, add the shape (along with the color, possibly) to a collection in the JComponent that you want it in (you need to create a custom JComponent that has such a collection, and you should synchronize around it when adding shapes and in paintComponent when painting the shapes).实现您想要的方法是当线程完成时,将形状(可能连同颜色)添加到您想要它的 JComponent 集合中(您需要创建一个具有此类集合的自定义 JComponent,并且您应该在添加形状时和在绘制形状时在paintComponent 中同步它)。 Then in your non-EDT thread, call myJComponent.repaint().然后在您的非 EDT 线程中,调用 myJComponent.repaint()。 It is okay to call this in all of your non-EDT threads.可以在所有非 EDT 线程中调用它。

Example:例子:

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);
      }
    }
  }
}

Then in your threads:然后在你的线程中:

public void run() {
  ...
  Shape newShape = ...;
  synchronized (myPanel.myShapes) {
    //add newShape to the collection
  }
  myPanel.repaint();
}

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

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