Java 在 Netbeans 上使用带有 Thread(Runnable) 的 awt,JFrame 绘制一个圆 (Mac)

Java drawing a circle using awt,JFrame with Thread(Runnable) on Netbeans (Mac)

https://github.com/terryaa/KOSTA_MAC/tree/master/Java/NetBeans/day13_01_15/src/ex1

我想做的是画圆,但一次在 canvas 上画一个圆,然后继续使用 Runnable join 绘制下一个圆。它应该使用 .start() 绘制一个圆圈,而另一个 .start() 不应该在正式的 .start() 绘制圆圈完成之前开始。

在链接页面的包中,Ex3_Canvas1 class 有 main 并使用 Runnable MyThread0 class 使用基本的 .start() 和 .join() 画一个圆,它做得很好我想要的是。

我创建了 NetBean 的自动 JFrame class Ex2_CanvasDemo 并尝试执行相同的操作但失败了。 JFrame window 绘制一个完整的圆后弹出,然后显示创建下一个圆。我想要的是 window 应该首先出现,它显示了两个圆圈的创建,不是同时而是依次出现,例如 Ex3_Canvas1。

我猜这是因为主线程等待 th(Ex2_CanvasDemo) 完成,所以 window 不申请更改。但是 Ex1_Canvas1 不应该这样做吗?这种差异是由于 netbeans 自动生成的代码造成的吗?我怎样才能像 Ex2_CanvasDemo 中的 Ex1_Canvas1 那样做呢?

我尝试制作一个 Runnable class 并在 Ex2_CanvasDemo 中使用但也失败了..

有什么帮助吗? 我在 mac.

上使用 jdk 8 和 netbeans8

--线程部分Ex2_CanvasDemo--

public Ex2_CanvasDemo() {
                initComponents();
                Thread th=new Thread(new Runnable() {

                @Override
                public void run() {
                    for(int i=0;i<370;i+=10){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException ex) {
                            Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
                        }
                        arcNUm=i;
                        System.out.println("circle"+arcNUm);
                        canvas1.repaint();
                    }
                }
            });
            th.start();
        try {
            th.join();
        } catch (InterruptedException ex) {
            Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
        }
            th=new Thread(new Runnable() {

                @Override
                public void run() {
                    for(int i=0;i<370;i+=10){
                        System.out.println("circle"+i);
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException ex) {
                            Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
                        }
                        arcNum2=i;
                        canvas2.repaint();
                    }

                }
            });
            th.start();
//        try {
//            th.join();
//        } catch (InterruptedException ex) {
//            Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
//        }


    }

警告

动画很难,好的动画真的很难。你需要对自己重复一遍,因为正确制作动画真的很难。

你需要知道的...

动画基本上是随时间变化的幻觉。你很少想执行线性动画,动画通常是在一段时间内完成的,因为它允许在平台上消除性能差异,这对用户来说并不那么苛刻。

Swing 是单线程的,不是线程安全的。这意味着您不应阻止事件调度线程,并且您只能从事件调度线程的上下文中更新 UI。

有关详细信息,请参阅 Concurrency in Swing

这让生活有点困难,因为你不能简单地 运行 EDT 中的线性循环,因为这会阻塞 UI 并且很难从 Thread 因为这是一团乱七八糟的同步。

最简单的解决方案之一是利用可用的 API 并使用 Swing Timer,它充当伪循环,在回调之间放置一个小的延迟,您可以在其中可以执行一些操作

示例...

虽然您可以通过多种方式解决此问题,但我设置了一个简单的 List,其中包含一堆 Animatable,其中 "do stuff" 然后只是 运行 一个接着一个.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                TestPane testPane = new TestPane();
                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(testPane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                EventQueue.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        testPane.play();
                    }
                });
            }
        });
    }

    public class TestPane extends JPanel {

        private List<Animatable> animations;
        private Animatable animation;

        private Timer timer;

        public TestPane() {
            animations = new ArrayList<>(25);
            animations.add(new CircleAnimation(Color.RED));
            animations.add(new CircleAnimation(Color.BLUE));

            timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (animation == null) {
                        animation = animations.remove(0);
                    }
                    if (animation.update(getBounds())) {
                        if (animations.isEmpty()) {
                            ((Timer)e.getSource()).stop();
                        } else {
                            animation = animations.remove(0);
                        }
                    }
                    repaint();
                }
            });
        }

        public void play() {
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (animation != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                animation.paint(g2d);
                g2d.dispose();
            }
        }

    }

    public interface Animatable {
        public boolean update(Rectangle bounds);
        public void paint(Graphics2D g2d);
    }

    public class CircleAnimation implements Animatable {
        private Color color;
        private Ellipse2D circle;
        private double delta = -1;

        public CircleAnimation(Color color) {
            this.color = color;
        }

        @Override
        public boolean update(Rectangle bounds) {
            if (circle == null) {
                circle = new Ellipse2D.Double(bounds.width, (bounds.height / 2) - 10, 20, 20);
            }
            Rectangle rect = circle.getBounds();
            rect.x += delta;
            circle.setFrame(rect);
            return rect.x + 20 < bounds.x;
        }

        @Override
        public void paint(Graphics2D g2d) {
            if (circle == null) {
                return;
            }
            g2d.setColor(color);
            g2d.fill(circle);
        }
    }

}