使用计时器为Java Line绘图设置动画

时间:2015-08-05 15:48:07

标签: java swing animation timer

我试图在面板上绘制两个圆圈,并在按下一个按钮后连接它们。到目前为止(除了调整线的位置)这是好的。但是,我想使用计时器为其设置动画。应该出现第一个圆圈,然后逐渐显示该线条,最后是第二个圆圈。

我看过许多计时器的例子,但我似乎无法让它为我工作。我一定是在误解。

这是球类(每个圆圈):

package twoBalls;

import java.awt.Color;
import java.awt.Point;

public class Ball {

    private int x;
    private int y;
    private int r;
    private Color color;
    private Point location;
    private Ball parent;

    public Ball(int x, int y, int r) {
        this.x = x;
        this.y = y;
        this.r = r;
        Point p = new Point(x, y);
        setLocation(p);
    }

    public void setParent(Ball b) {
        parent = b;
    }

    public Ball getParent() {
        return parent;
    }

    public void setx(int x) {
        this.x = x;
    }

    public void sety(int y) {
        this.y = y;
    }

    public int getx() {
        return x;
    }

    public int gety() {
        return y;
    }

    public int getr() {
        return r;
    }

    public void setPreferedSize() {

    }

    public void setLocation(Point p) {
        setx(p.x);
        sety(p.y);
        location = p;
    }

    public Point getLocation() {
        return location;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public Color getColor() {
        return color;
    }

}

然后将类存储在arrayList中的类。而且我认为这是实际绘图应该与定时器一起发生的地方。 我试图将行的起点和终点设置为相同,并使用计时器将终点增加到它应该的位置。我可能是追踪的方式,但那是意图!

我已更改此类,现在可以输入while循环中的if语句,因为我现在正在比较不同的点。但这条线仍然没有被绘制出来。

package twoBalls;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;

import javax.swing.JPanel;
import javax.swing.Timer;

public class BallsArray extends JPanel implements ActionListener {

    private ArrayList<Ball> balls;
    private Timer timer;
    private final int DELAY = 25;
    private int xDest;
    private int yDest;
    private Point dest;
    private Point starts;
    private int xStart;
    private int yStart;

    public BallsArray() {
        balls = new ArrayList<Ball>();
        timer = new Timer(DELAY, this);
        yDest = 0;
        xDest = 0;
        dest = new Point(xDest, yDest);
        starts = new Point(xStart, yStart);

    }

    public void setDestXY(int x, int y) {
        xDest = x;
        yDest = y;
        dest = new Point(xDest, yDest);
        setDest(dest);
    }

    public void setDest(Point p) {
        dest = p;

    }

    public Point getDest() {
        return dest;
    }

    public void setStartsXY(int x, int y) {
        xStart = x;
        yStart = y;
        starts = new Point(xStart, yStart);
        setStarts(starts);
    }

    public void setStarts(Point p) {
        starts = p;
    }

    public Point getStarts() {
        return starts;
    }

    public void addBall(Ball b) {
        balls.add(b);
    }

    public void addBall(int x, int y, int r) {
        balls.add(new Ball(x, y, r));

    }

    public void paintComponent(Graphics g) {

        Graphics2D g2 = (Graphics2D) g;
        for (int i = 0; i < balls.size(); i++) {
            if (i == 0) {
                paintBall(balls.get(0), g2);
            }
            if (i != 0) {
                int j = i - 1;
                Ball bp = balls.get(j);
                Ball bc = balls.get(i);
                bc.setParent(bp);
                paintLine(bc, g2);
                paintBall(bc, g2);
            }

        }
    }

    public void paintBall(Ball b, Graphics2D g2d) {
        Ellipse2D circ = new Ellipse2D.Float(b.getx(), b.gety(), b.getr(),
                b.getr());
        g2d.draw(circ);
    }

    public void paintLine(Ball b, Graphics2D g2d) {
        timer.start();
        if (b != null && b.getLocation() != null) {
            Ball parent = b.getParent();
            if (parent != null) {
                g2d.setColor(Color.GRAY);
                if (parent.getLocation() != null && b.getLocation() != null) {
                    setDest(parent.getLocation());
                    setStarts(parent.getLocation());
                    g2d.draw(new Line2D.Float(starts, dest));
                }
            }
        }

    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // Not sure what I need to do here
        // increment second location somehow
        // Point s = getStarts();
        Point p = getDest();
        Point t = this.getLocation();
        while (p != t) {

            if (p.x != t.x && p.y != t.y) {
                System.out.println("hello");
                int x = dest.x;
                int y = dest.y;
                x++;
                y++;
                setDestXY(x, y);
                p = getDest();
                repaint();
            } else if (p.x == t.x && p.y != t.y) {
                System.out.println("part 2");
                int y = dest.y;
                y++;
                setDestXY(dest.x, y);
                p = getDest();
                repaint();
            } else if (p.x != t.x && p.y == t.y) {
                System.out.println("part 3");
                int x = dest.x;
                x++;
                setDestXY(x, dest.y);
                p = getDest();
                repaint();
            }
            repaint();
        }
    }
}

我在网上获得了很多帮助,我担心我现在已经超出了我的深度!我不确定下面的EventQueue / run部分。以下是全部设置的类:

package twoBalls;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;

public class Display implements ActionListener {

    private JFrame frame;
    private JButton button;
    private BallsArray b;

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                Display ex = new Display();

            }
        });

    }

    public Display() {
        b = new BallsArray();
        frame = new JFrame();
        frame.setSize(800, 500);
        frame.setTitle("Show balls");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        button = new JButton("New Ball");
        frame.add(button, BorderLayout.SOUTH);
        frame.setVisible(true);
        button.addActionListener(this);

    }

    @Override
    public void actionPerformed(ActionEvent e) {

        Ball ball1 = new Ball(100, 100, 50);
        b.addBall(ball1);
        b.addBall(200, 200, 50);
        frame.add(b, BorderLayout.CENTER);
        frame.revalidate();
        frame.repaint();

    }
}

此刻它绘制了两个圆圈,但根本没有绘制线条。

1 个答案:

答案 0 :(得分:2)

制作动画时,使用model / view / controller pattern

会有所帮助

这是我根据您的代码创建的GUI。

Show Balls Animation

我简化了你的Ball课程。这就是你定义一个球所需的一切。

package twoBalls;

import java.awt.Color;
import java.awt.Point;

public class Ball {

    private final int radius;

    private final Color color;

    private final Point center;

    public Ball(int x, int y, int radius, Color color) {
        this(new Point(x, y), radius, color);
    }

    public Ball(Point center, int radius, Color color) {
        this.center = center;
        this.radius = radius;
        this.color = color;
    }

    public int getRadius() {
        return radius;
    }

    public Color getColor() {
        return color;
    }

    public Point getCenter() {
        return center;
    }

}

我创建了GUIModel类来保存GUI所需的所有信息。这将模型与视图分开。

package twoBalls;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

public class GUIModel {

    private double direction;
    private double distance;

    private List<Ball> balls;

    private Point lineStartPoint;
    private Point lineEndPoint;

    public GUIModel() {
        this.balls = new ArrayList<>();
    }

    public void addBall(Ball ball) {
        this.balls.add(ball);
    }

    public List<Ball> getBalls() {
        return balls;
    }

    public void calculatePoints() {
        this.lineStartPoint = balls.get(0).getCenter();
        this.lineEndPoint = balls.get(1).getCenter();

        this.distance = Point.distance(lineStartPoint.x, lineStartPoint.y,
                lineEndPoint.x, lineEndPoint.y);
        this.direction = Math.atan2(lineEndPoint.y - lineStartPoint.y,
                lineEndPoint.x - lineStartPoint.x);
    }

    public Point getCurrentPoint(int pos, int total) {
        double increment = distance / total;
        double length = increment * pos;

        double x = lineStartPoint.x + Math.cos(direction) * length;
        double y = lineStartPoint.y - Math.sin(direction) * length;

        x = Math.round(x);
        y = Math.round(y);

        return new Point((int) x, (int) y);
    }

    public Point getLineStartPoint() {
        return lineStartPoint;
    }

}

此类包含两个Ball实例,并计算线的长度和方向,分为总增量。

现在我们已经定义了模型类,让我们看一下视图类。第一个是你的Display类。

package twoBalls;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Display implements Runnable {

    private GUIModel guiModel;

    private JFrame frame;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Display());
    }

    public Display() {
        this.guiModel = new GUIModel();
        Ball ball1 = new Ball(150, 200, 50, Color.BLUE);
        Ball ball2 = new Ball(450, 200, 50, Color.GREEN);
        guiModel.addBall(ball1);
        guiModel.addBall(ball2);
        guiModel.calculatePoints();
    }

    @Override
    public void run() {
        frame = new JFrame();
        frame.setTitle("Show Balls Animation");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        DrawingPanel drawingPanel = new DrawingPanel(guiModel);
        panel.add(drawingPanel, BorderLayout.CENTER);

        panel.add(createButtonPanel(drawingPanel), BorderLayout.SOUTH);

        frame.add(panel);

        frame.pack();
        frame.setVisible(true);
    }

    private JPanel createButtonPanel(DrawingPanel drawingPanel) {
        JPanel panel = new JPanel();

        JButton startButton = new JButton("Start Animation");
        startButton.addActionListener(new StartAnimation(drawingPanel));
        panel.add(startButton);

        return panel;
    }

    public class StartAnimation implements ActionListener {

        private DrawingPanel drawingPanel;

        public StartAnimation(DrawingPanel drawingPanel) {
            this.drawingPanel = drawingPanel;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            LineRunnable runnable = new LineRunnable(drawingPanel);
            new Thread(runnable).start();
        }

    }

}

Display类的构造函数设置GUI模型。

Display类的run方法构造GUI,并启动动画。

了解我如何分离模型和视图。

StartAnimation类是您的控制器。当你左键单击JButton时它会启动动画。我稍后会讨论LineRunnable类。

接下来,让我们看一下DrawingPanel类。

package twoBalls;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;

import javax.swing.JPanel;

public class DrawingPanel extends JPanel {

    private static final long serialVersionUID = -3709678584255542338L;

    private boolean drawLine;

    private int pos;
    private int total;

    private GUIModel guiModel;

    public DrawingPanel(GUIModel guiModel) {
        this.guiModel = guiModel;
        this.drawLine = false;
        this.setPreferredSize(new Dimension(600, 400));
    }

    public boolean isDrawLine() {
        return drawLine;
    }

    public void setDrawLine(boolean drawLine) {
        this.drawLine = drawLine;
    }

    public void setPos(int pos) {
        this.pos = pos;
        repaint();
    }

    public void setTotal(int total) {
        this.total = total;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g;

        for (Ball ball : guiModel.getBalls()) {
            g2d.setColor(ball.getColor());
            Point center = ball.getCenter();
            int radius = ball.getRadius();
            g2d.fillOval(center.x - radius, center.y - radius, radius + radius,
                    radius + radius);
        }

        if (isDrawLine()) {
            g2d.setColor(Color.BLACK);
            g2d.setStroke(new BasicStroke(5.0F));
            Point a = guiModel.getLineStartPoint();
            Point b = guiModel.getCurrentPoint(pos, total);
            g2d.drawLine(a.x, a.y, b.x, b.y);
        }
    }

}

这个视图类唯一能做的就是绘制球和线。计算线条长度的责任属于模型。

我在这里设置了首选大小,并使用Display类中的pack方法来获取JFrame的大小。您通常想知道绘图区域的尺寸,而不是整个窗口。

最后,让我们看一下LineRunnable类。这是控制动画的类。

package twoBalls;

import java.awt.EventQueue;

public class LineRunnable implements Runnable {

    private int total;

    private DrawingPanel drawingPanel;

    public LineRunnable(DrawingPanel drawingPanel) {
        this.drawingPanel = drawingPanel;
        this.total = 240;
    }

    @Override
    public void run() {
        setDrawLine();
        for (int pos = 0; pos <= total; pos++) {
            setPos(pos);
            sleep(50L);
        }
    }

    private void setDrawLine() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                drawingPanel.setDrawLine(true);
                drawingPanel.setTotal(total);
            }
        });
    }

    private void setPos(final int pos) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                drawingPanel.setPos(pos);
            }
        });
    }

    private void sleep(long delay) {
        try {
            Thread.sleep(delay);
        } catch (InterruptedException e) {

        }
    }

}

在run方法中,我们将线分为240个段,每50毫秒绘制一个段。 GUI需要12秒才能绘制线条。如果您愿意,可以使用这些数字。

for循环是一个经典的动画循环。首先,您更新模型,我正在通过绘图面板进行更新。然后你就睡了。

此动画循环在与GUI线程不同的线程上运行。这使GUI保持响应。由于循环在不同的线程上运行,我们必须使用invokeLater方法在Event Dispatch线程上绘制。

我希望这对你有所帮助。分而治之。不要让一个班级做不止一件事。