在JPanel中创建无数的对象并通过Java中的PaintComponent绘制它们

Gip*_*ing 4 java animation swing jpanel

我有一个难题,如何实现应用。我的 JPanel 宽度为 288,高度为 512,然后我创建了两个对象(图像)并使用坐标通过 PaintComponent 绘制它们

\n\n
 drawImage (Image1,288,128,this) ;\n drawImage (Image2, 288, 384, this);\n
Run Code Online (Sandbox Code Playgroud)\n\n

。它们在 X 轴上同时递减,当达到 x = 144 时,应在坐标 \xe2\x80\x98( x = 288 , y = (int)Math.random()* 512 处绘制新的(相同)图像)\xe2\x80\x99 并开始递减,并且第一个应该仍然递减。而这个过程应该是永无止境的。每个达到 x = 144 的新对象都应该构建新的对象。我尝试创建 ArrayList 并在其中添加坐标

\n\n
ArrayList arrayX = new ArrayList(); \narrayX.add(288)\narrayY.add((int) Math.random()* 512 )\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后通过提取值

\n\n
array.get()\n
Run Code Online (Sandbox Code Playgroud)\n\n

但那没有成功。\n我看到有人通过数组使用 JavaScript 完成此操作的视频

\n\n
var position = []\nposition = ({\nX : 288\nY : 256\n })\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后通过这样的循环实现

\n\n
 function draw() {\n\n for (int i = 0; i < position.length; i++ ){\n cvs.drawImage(Image1,position[i].x , position[i].y)\n cvs.drawImage(Image2,position[i].x , position[i].y + 50)\n\n position [i] .x - -;\n if(position[i].x == 128)\n position.push({\n X : 288\n Y : Math.floor(Math.random()*512 })\n })\n }\n }\n
Run Code Online (Sandbox Code Playgroud)\n\n

我不知道如何在 Java 中执行此操作。\n也许我也应该使用数组来保存带有坐标的变量,或者数组列表,但以不同的方式。请帮帮我 。\n提前致谢

\n

Mad*_*mer 5

从概念上讲,这个想法很简单,问题是,Swing 是信号线程,而不是线程安全的。

有关更多详细信息,请参阅Swing 中的并发性

这意味着您可以在事件调度线程内运行长时间运行或阻塞操作(如永无休止的循环),但您也不应该从 EDT 上下文外部更新 UI(或 UI 所依赖的属性)。

虽然该问题有多种可能的解决方案,但最简单的可能是使用 Swing Timer,它提供了一种安全地安排延迟的方法(不会阻止 EDT),并且会在EDT,允许您从其中更新 UI。

有关更多详细信息,请参阅如何使用 Swing 计时器

现在,因为您使用的是面向对象语言,所以您应该利用它提供的功能,对我来说,这意味着封装。

你有一个图像,你想在特定的位置绘制,但是它的位置会根据一些规则而改变,这只是简单的Java Old (POJO)

通常,我会从 a 开始interface描述基本属性和操作,但为了简洁起见,我直接跳到了一堂课......

public class Drawable {

    private int x, y;
    private Color color;

    public Drawable(int x, int y, Color color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public Color getColor() {
        return color;
    }

    public void update() {
        x--;
        if (x <= 144) {
            reset();
        }
    }

    protected void reset() {
        x = 288;
        y = (int) (Math.random() * (512 - 20));
    }

    public void paint(Graphics2D g2d) {
        Graphics2D copy = (Graphics2D) g2d.create();
        copy.translate(getX(), getY());
        copy.setColor(getColor());
        copy.drawOval(0, 0, 20, 20);
        copy.dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是等等,你说,它正在使用Color而不是图像!?是的,我手头没有任何小图片,此外,我需要给你留点事做;)

现在,动画是重复更新和绘制的序列,直到达到所需状态。

在这种情况下,您不太关心最终状态,因此您可以让它继续运行。

“更新”周期由 Swing 处理Timer,它循环遍历List对象Drawable,调用它们的update方法,然后调度 a repaint,它触发对象被绘制的JPanels ,简单......paintComponentDrawable

public class TestPane extends JPanel {

    private List<Drawable> drawables;

    public TestPane() {
        drawables = new ArrayList<>(2);
        drawables.add(new Drawable(288, 128, Color.RED));
        drawables.add(new Drawable(288, 384, Color.RED));

        Timer timer = new Timer(5, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                for (Drawable drawable : drawables) {
                    drawable.update();
                }
                repaint();
            }
        });
        timer.start();
    }

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

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (Drawable drawable : drawables) {
            Graphics2D g2d = (Graphics2D) g.create();
            drawable.paint(g2d);
            g2d.dispose();
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

总而言之 - 可运行的示例......

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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();
                }

                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 Drawable {

        private int x, y;
        private Color color;

        public Drawable(int x, int y, Color color) {
            this.x = x;
            this.y = y;
            this.color = color;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        public Color getColor() {
            return color;
        }

        public void update() {
            x--;
            if (x <= 144) {
                reset();
            }
        }

        protected void reset() {
            x = 288;
            y = (int) (Math.random() * (512 - 20));
        }

        public void paint(Graphics2D g2d) {
            Graphics2D copy = (Graphics2D) g2d.create();
            copy.translate(getX(), getY());
            copy.setColor(getColor());
            copy.drawOval(0, 0, 20, 20);
            copy.dispose();
        }
    }

    public class TestPane extends JPanel {

        private List<Drawable> drawables;

        public TestPane() {
            drawables = new ArrayList<>(2);
            drawables.add(new Drawable(288, 128, Color.RED));
            drawables.add(new Drawable(288, 384, Color.RED));

            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    for (Drawable drawable : drawables) {
                        drawable.update();
                    }
                    repaint();
                }
            });
            timer.start();
        }

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

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            for (Drawable drawable : drawables) {
                Graphics2D g2d = (Graphics2D) g.create();
                drawable.paint(g2d);
                g2d.dispose();
            }
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

“有没有更简单的解决方案”?是的,当然,我总是先用最难的方法来解决问题。首先,好的动画很难。严重地。我已经研究这个东西近20年了,制作一个优秀的动画引擎,能够灵活地满足它可能遇到的所有可能的需求,这几乎是不可能的任务,特别是在一个没有真正设计的框架中为了它。

如果你不相信我,你可以看看

这只是动画有多复杂的几个例子

抱歉,当谈到动画时,你会惊讶地发现我经常被问到“可以更简单吗”;)

“每个达到 x = 144 的新物体都应该构建新的物体

所以,显然我可能对这一点感到困惑。如果这意味着“达到 144 后添加新对象”,那么这会引发一些新问题。主要问题是 GC 开销,这会导致动画速度减慢。当然,我们只处理大约 4-6 个对象,但如果你不小心,它就是其中之一。

所以我就拿上面的例子,对更新周期做了一些修改。这增加了reusePool旧对象的放置位置并可以重复使用,从而减少了重复创建和销毁短命对象的 GC 开销。

只是decaying List确保一旦对象通过了swanPoint,就不会考虑重新生成新对象。当然,您可以在 POJO 本身上放置一个标志,但我不认为这是 POJO 责任的一部分

public TestPane() {
    drawables = new ArrayList<>(2);
    reusePool = new ArrayList<>(2);
    decaying = new ArrayList<>(2);

    timer = new Timer(5, new ActionListener() {
        private List<Drawable> spawned = new ArrayList<>(5);

        @Override
        public void actionPerformed(ActionEvent e) {
            spawned.clear();
            Iterator<Drawable> it = drawables.iterator();
            int swapnPoint = getWidth() / 2;
            while (it.hasNext()) {
                Drawable drawable = it.next();
                drawable.update();

                if (drawable.getX() > 0 && drawable.getX() < swapnPoint) {
                    if (!decaying.contains(drawable)) {
                        decaying.add(drawable);
                        Drawable newDrawable = null;
                        if (reusePool.isEmpty()) {
                            newDrawable = new Drawable(
                                            getWidth() - 20,
                                            randomVerticalPosition(),
                                            randomColor());
                        } else {
                            newDrawable = reusePool.remove(0);
                            newDrawable.reset(getWidth() - 20,
                                            randomVerticalPosition(),
                                            randomColor());
                        }
                        spawned.add(newDrawable);
                    }
                } else if (drawable.getX() <= -20) {
                    System.out.println("Pop");
                    it.remove();
                    decaying.remove(drawable);
                    reusePool.add(drawable);
                }
            }
            drawables.addAll(spawned);

            repaint();
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

现在,这将允许对象在整个宽度上行进,并在经过中点时生成新对象。一旦它们超出视图的可视范围,它们将被放置到视图中,reuse List以便在需要新对象时可以再次重用它们。

可运行的例子...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
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.start();
                    }
                });
            }
        });
    }

    public class Drawable {

        private int x, y;
        private Color color;

        public Drawable(int x, int y, Color color) {
            this.x = x;
            this.y = y;
            this.color = color;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        public Color getColor() {
            return color;
        }

        public void update() {
            x--;
        }

        protected void reset(int x, int y, Color color) {
            this.x = x;
            this.y = y;
            this.color = color;
        }

        public void paint(Graphics2D g2d) {
            Graphics2D copy = (Graphics2D) g2d.create();
            copy.translate(getX(), getY());
            copy.setColor(getColor());
            copy.fillOval(0, 0, 20, 20);
            copy.setColor(Color.BLACK);
            copy.drawOval(0, 0, 20, 20);
            copy.dispose();
        }
    }

    public class TestPane extends JPanel {

        private List<Drawable> drawables;
        private List<Drawable> decaying;
        private List<Drawable> reusePool;

        private Color[] colors = new Color[]{Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GREEN, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.YELLOW};
        private Random rnd = new Random();

        private Timer timer;

        public TestPane() {
            drawables = new ArrayList<>(2);
            reusePool = new ArrayList<>(2);
            decaying = new ArrayList<>(2);

            timer = new Timer(40, new ActionListener() {
                private List<Drawable> spawned = new ArrayList<>(5);

                @Override
                public void actionPerformed(ActionEvent e) {
                    spawned.clear();
                    Iterator<Drawable> it = drawables.iterator();
                    int swapnPoint = getWidth() / 2;
                    while (it.hasNext()) {
                        Drawable drawable = it.next();
                        drawable.update();

                        if (drawable.getX() > 0 && drawable.getX() < swapnPoint) {
                            if (!decaying.contains(drawable)) {
                                decaying.add(drawable);
                                Drawable newDrawable = null;
                                if (reusePool.isEmpty()) {
                                    System.out.println("New");
                                    newDrawable = new Drawable(
                                                    getWidth() - 20,
                                                    randomVerticalPosition(),
                                                    randomColor());
                                } else {
                                    System.out.println("Reuse");
                                    newDrawable = reusePool.remove(0);
                                    newDrawable.reset(getWidth() - 20,
                                                    randomVerticalPosition(),
                                                    randomColor());
                                }
                                spawned.add(newDrawable);
                            }
                        } else if (drawable.getX() <= -20) {
                            System.out.println("Pop");
                            it.remove();
                            decaying.remove(drawable);
                            reusePool.add(drawable);
                        }
                    }
                    drawables.addAll(spawned);

                    repaint();
                }
            });
        }

        public void start() {
            drawables.add(new Drawable(getWidth(), 128, randomColor()));
            drawables.add(new Drawable(getWidth(), 384, randomColor()));
            timer.start();
        }

        protected int randomVerticalPosition() {
            return rnd.nextInt(getHeight() - 20);
        }

        protected Color randomColor() {
            return colors[rnd.nextInt(colors.length - 1)];
        }

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

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            for (Drawable drawable : drawables) {
                Graphics2D g2d = (Graphics2D) g.create();
                drawable.paint(g2d);
                g2d.dispose();
            }
        }

    }

}
Run Code Online (Sandbox Code Playgroud)