如何在每个并发线程中在 Java Swing 中绘画? [英] How to paint in Java Swing in every concurrent thread?

查看:28
本文介绍了如何在每个并发线程中在 Java Swing 中绘画?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的程序的目标是从随机生成的点中绘制正方形.我想在每个线程生成一组方块后立即显示它们.但是,一旦所有线程都运行完毕,只会显示一组方块.我已经使用了swinginvoke,并且很好奇 repaint() 是否存在问题,因为所有线程都达到了重绘但在最后一个线程完成之前不会绘制,并且最终彼此重叠.我也不想要storedata"每个线程之间共享的变量,但它的每个实例都将数据保存在其中.我试图通过清除它来解决这个问题,但它没有奏效.该程序由一个自定义线程类、一个启动线程的主类、一个 GUI 类和一个绘制正方形的自定义 jpanel 组成.

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. I also don't want the "storedata" variable to be shared between each thread but every instance of it keeps the data in it. 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.

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 线程中调用它.

In swing, painting should only be done in the 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). Then in your non-EDT thread, call myJComponent.repaint(). It is okay to call this in all of your non-EDT threads.

示例:

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

这篇关于如何在每个并发线程中在 Java Swing 中绘画?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆