如何生成多个圆形并使这些形状沿着框架向下移动

How to generate multiple circle shapes and animate those shapes going down the frame

单击框架的面板时,我无法生成多个椭圆形。我想要的是它会生成许多椭圆形并且这些形状会向下移动。要求之一是使用两个多线程。然而,在我的例子中,我创建的程序是,它只会生成一个椭圆形,并且位置是随机变化的。谁能帮我一个这个。

package ovalrandomcolors;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.List;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class OvalRandomColors extends JPanel{

    private int ovalX = 50;
    private int ovalY =50;
    private int ovalPositionX = 250;
    private int ovalPositionY = 250;
    private Color color = Color.YELLOW;

    public OvalRandomColors(){
        setBackground(Color.DARK_GRAY);
    }
    @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);


        g.setColor(color);
        g.fillOval(ovalPositionX, ovalPositionY, ovalX, ovalY);

        g.setColor(color);
        g.fillOval(ovalPositionX, ovalPositionY, ovalX, ovalY);

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable(){

            @Override
            public void run() {
               JFrame frame = new JFrame();
               final OvalRandomColors oval = new OvalRandomColors();
               oval.addMouseListener(new MouseAdapter(){
                   @Override
                   public void mouseClicked(MouseEvent e){
                       OvalWithThreading firstThread = new OvalWithThreading(oval);
                       OvalWithThreading secondThread = new OvalWithThreading(oval);

                       Thread first = new Thread(firstThread);
                       Thread second = new Thread(secondThread);
                       second.start();
                       first.start();
                   }
               });

               frame.add(oval);
               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               frame.setSize(500,700);
               frame.setVisible(true);
            }  
        });      
    }


    public void updateOval(){

        int r = (int)(Math.random() * 255);
        int g = (int) (Math.random() * 255);
        int b = (int)(Math.random() * 255);

        color = new Color(r,g,b);

        ovalPositionX = (int)(Math.random() * 78);
        ovalPositionY = (int) (Math.random() * 245);

        animateOval();
        repaint();
    }
    public void animateOval(){
           // ovalPositionX += 30;
            ovalPositionY += 30;
    }

    public static class OvalWithThreading implements Runnable{

        private final OvalRandomColors ovalShape;
        public OvalWithThreading(OvalRandomColors oS){
            this.ovalShape = oS;
        }
        @Override
        public void run() {
            for(;;){
                    ovalShape.updateOval();
                try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                Logger.getLogger(OvalRandomColors.class.getName()).log(Level.SEVERE, null, ex);
                }
            }           
        }       
    }   
}

让我们开始吧,Swing 不是 Thread 安全的,因此拥有另一个 Thread 来更新 UI 依赖于呈现的对象的状态需要一些认真的考虑。通常,我建议使用 Swing TimerSwingWorker 来完成此操作,但这些不是 "requirements"

为了渲染多个对象,您需要一些方法来存储它们,以便您可以更新它们的状态并渲染它们。最简单的解决方案是 List,您可以查看 Collections Trail 了解更多详细信息。

现在,如果您还需要管理颜色,您可以看看我是如何管理每个形状的增量的,应该会给您一个这样做的想法

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JFrame;
import javax.swing.JPanel;
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();
                }

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

    public class TestPane extends JPanel {

        private ReentrantLock shapesLock = new ReentrantLock();
        private List<Ellipse2D> shapes;

        public TestPane() {
            shapes = new ArrayList<>(25);
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    shapesLock.lock();
                    try {
                        shapes.add(new Ellipse2D.Double(e.getX() - 5, e.getY() - 5, 10, 10));
                    } finally {
                        shapesLock.unlock();
                    }
                }
            });

            Thread t = new Thread(new Runnable() {
                private Map<Shape, Double> deltas = new HashMap<>();

                @Override
                public void run() {
                    while (true) {
                        try {
                            shapesLock.lock();
                            try {
                                Rectangle containerBounds = getBounds();
                                containerBounds.setLocation(0, 0);
                                Iterator<Ellipse2D> it = shapes.iterator();
                                while (it.hasNext()) {
                                    Ellipse2D shape = it.next();
                                    Rectangle2D bounds = shape.getBounds2D();
                                    double y = bounds.getY();
                                    Double delta = deltas.get(shape);
                                    if (delta == null) {
                                        delta = 0d;
                                    }
                                    y += delta;
                                    shape.setFrame(bounds.getX(), y, bounds.getWidth(), bounds.getHeight());
                                    if (containerBounds.contains(shape.getBounds())) {
                                        delta = Math.min(delta + 0.25, 6d);
                                        deltas.put(shape, delta);
                                    } else {
                                        it.remove();
                                    }
                                }
                            } finally {
                                shapesLock.unlock();
                            }
                            repaint();
                            Thread.sleep(40);
                        }   catch (InterruptedException ex) {
                        }
                    }
                }
            });
            t.setDaemon(false);
            t.start();
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            shapesLock.lock();
            try {
                for (Ellipse2D shape : shapes) {
                    g2d.fill(shape);
                }
            } finally {
                shapesLock.unlock();
            }
            g2d.dispose();
        }

    }

}