【问题标题】:animate JPanel (slide in) with timer使用计时器为 JPanel(滑入)设置动画
【发布时间】:2013-04-25 07:58:01
【问题描述】:

我正在尝试使用我制作的此类从侧面滑入 JPanel:

public class AnimationClass {

    private int i;
    private int y;
    private JPanel panel;
    private int xTo;
    private Timer timer;
    private int xFrom;

    synchronized void slidePanelInFromRight(JPanel panelInput, int xFromInput, int xToInput, int yInput, int width, int height) {
        this.panel = panelInput;
        this.xFrom = xFromInput;
        this.xTo = xToInput;
        this.y = yInput;

            panel.setSize(width, height);

            timer = new Timer(0, new ActionListener() {
                public void actionPerformed(ActionEvent ae) {

                    for (int i = xFrom; i > xTo; i--) {
                        panel.setLocation(i, y);
                        panel.repaint();
                        i--;

                        timer.stop();
                        timer.setDelay(100);

                        if (i >= xTo) {
                            timer.stop();
                        }
                    }
                    timer.stop();

                }
            });
            timer.start();

    }

}

嗯,我不知道问题是什么。我尝试了很多不同的东西,但我似乎无法让它发挥作用。

【问题讨论】:

  • 您遇到的具体问题是什么,目前表现如何?

标签: java swing animation timer jpanel


【解决方案1】:

OP 的代码中存在许多问题。正如 MadProrammer 指出的那样,每个计时器滴答声应该只移动一步。这是一个简单的、经过测试的对 OPs 代码的更正,它一次移动一个像素,每秒 25 次。注意 cmets:

synchronized void slidePanelInFromRight(JPanel panelInput, int xFromInput, int xToInput, int yInput, int width, int height) {
this.panel = panelInput;
this.xFrom = xFromInput;
this.xTo = xToInput;
this.y = yInput;

    panel.setSize(width, height);

    // timer runs 25 times per second
    timer = new Timer(40, new ActionListener() {
        public void actionPerformed(ActionEvent ae) {

            // Must 'remember' where we have slid panel to by using instance variable rather than automatic variable
            // Only move one step at a time.
            // No need to restart timer, it continues to run until stopped
            if (xFrom > xTo){
                xFrom = xFrom - 1;
                panel.setLocation(xFrom, y);
                panel.repaint();
            } else {
                timer.stop();
           }

            panel.setLocation(xFrom, y);
            panel.repaint();

        }
    });
    timer.start();

}

【讨论】:

    【解决方案2】:

    滑动任何东西的示例


        package TestingPackage;
    
    import java.awt.Color;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    
    public class ToggleBtn extends JPanel {
    
        JFrame frame;
        JPanel panelOut;
        JLabel labelOn;
        JLabel labelOff;
        JButton btn;
        int count = 1;
    
        public ToggleBtn() {
            frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setBounds(500, 300, 300, 300);
            frame.setLayout(null);
    
            panelOut = new JPanel(null);
            panelOut.setBounds(50, 100, 120, 30);
            panelOut.setBackground(Color.gray);
            frame.add(panelOut);
    
            btn = new JButton("::");
            btn.setBounds(0, 0, 60, 30);
            panelOut.add(btn);
            btn.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    startThread();
                }
            });
            labelOn = new JLabel("ON");
            labelOn.setBounds(0, 0, 60, 30);
            panelOut.add(labelOn);
    
            labelOff = new JLabel("OFF");
            labelOff.setBounds(60, 0, 60, 30);
            panelOut.add(labelOff);
    
            frame.setVisible(true);
        }
    
        public void startThread() {
            count++;
            new Move().start();
        }
    
        public static void main(String[] args) {
            new ToggleBtn();
        }
    
        class Move extends Thread {
    
            @Override
            public void run() {
                if (count % 2 == 0) {
                    System.out.println("if");
                    for (int i = 0; i <= 60; i++) {
                        try {
                            Thread.sleep(3);
                        } catch (InterruptedException ex) {
                            Logger.getLogger(ToggleBtn.class.getName()).log(Level.SEVERE, null, ex);
                        }
                        btn.setBounds(i, 0, 60, 30);
                    }
    
                } else {
                    System.out.println("else");
                    for (int i = 60; i >= 0; i--) {
                        try {
                            Thread.sleep(3);
                        } catch (InterruptedException ex) {
                            Logger.getLogger(ToggleBtn.class.getName()).log(Level.SEVERE, null, ex);
                        }
                        btn.setBounds(i, 0, 60, 30);
                    }
                }
            }
        }
    }
    

    【讨论】:

    • 这种方法不能正确同步对btn的访问。
    【解决方案3】:

    计时器应该在每个滴答声上更改位置,直到它到位,相反,在每个滴答声上,您都在运行一个 for-next 循环,该循环会阻塞 EDT,直到循环完成,防止更新用户界面

    更新示例

    例如...

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Point;
    import java.awt.Rectangle;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.Action;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class TestAnimatedPane {
    
        public static void main(String[] args) {
            new TestAnimatedPane();
        }
    
        public TestAnimatedPane() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame("Test");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private JPanel panel;
    
            public TestPane() {
                setLayout(null);
                panel = new JPanel();
                panel.setBackground(Color.RED);
                add(panel);
                Dimension size = getPreferredSize();
    
                Rectangle from = new Rectangle(size.width, (size.height - 50) / 2, 50, 50);
                Rectangle to = new Rectangle((size.width - 50) / 2, (size.height - 50) / 2, 50, 50);
    
                Animate animate = new Animate(panel, from, to);
                animate.start();
    
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
        }
    
        public static class Animate {
    
            public static final int RUN_TIME = 2000;
    
            private JPanel panel;
            private Rectangle from;
            private Rectangle to;
    
            private long startTime;
    
            public Animate(JPanel panel, Rectangle from, Rectangle to) {
                this.panel = panel;
                this.from = from;
                this.to = to;
            }
    
            public void start() {
                Timer timer = new Timer(40, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        long duration = System.currentTimeMillis() - startTime;
                        double progress = (double)duration / (double)RUN_TIME;
                        if (progress > 1f) {
                            progress = 1f;
                            ((Timer)e.getSource()).stop();
                        }
                        Rectangle target = calculateProgress(from, to, progress);
                        panel.setBounds(target);
                    }
                });
                timer.setRepeats(true);
                timer.setCoalesce(true);
                timer.setInitialDelay(0);
                startTime = System.currentTimeMillis();
                timer.start();
            }
    
        }
    
        public static Rectangle calculateProgress(Rectangle startBounds, Rectangle targetBounds, double progress) {
    
            Rectangle bounds = new Rectangle();
    
            if (startBounds != null && targetBounds != null) {
    
                bounds.setLocation(calculateProgress(startBounds.getLocation(), targetBounds.getLocation(), progress));
                bounds.setSize(calculateProgress(startBounds.getSize(), targetBounds.getSize(), progress));
    
            }
    
            return bounds;
    
        }
    
        public static Point calculateProgress(Point startPoint, Point targetPoint, double progress) {
    
            Point point = new Point();
    
            if (startPoint != null && targetPoint != null) {
    
                point.x = calculateProgress(startPoint.x, targetPoint.x, progress);
                point.y = calculateProgress(startPoint.y, targetPoint.y, progress);
    
            }
    
            return point;
    
        }
    
        public static int calculateProgress(int startValue, int endValue, double fraction) {
    
            int value = 0;
            int distance = endValue - startValue;
            value = (int)Math.round((double)distance * fraction);
            value += startValue;
    
            return value;
    
        }
    
        public static Dimension calculateProgress(Dimension startSize, Dimension targetSize, double progress) {
    
            Dimension size = new Dimension();
    
            if (startSize != null && targetSize != null) {
    
                size.width = calculateProgress(startSize.width, targetSize.width, progress);
                size.height = calculateProgress(startSize.height, targetSize.height, progress);
    
            }
    
            return size;
    
        }
    }
    

    更新

    我应该在昨晚添加这个(1 年不想上床睡觉,2 位父母想睡觉,不要再说了......)

    动画是一个复杂的话题,尤其是当您开始研究变速时(示例是静态的)。

    与其重新发明轮子,不如认真考虑看看……

    【讨论】:

    • 非常感谢。它有效,但我仍然想知道运动的速度。如何设置计时器中每个“循环”之间的等待时间?目前,它的运行速度非常慢(一次 1px),但如果我可以减少每个 px 移动之间的等待,那将是完美的。虽然想不通
    • 你有点不喜欢。您有可以影响速度的数字变量。第一个是实际 Timer 的滴答延迟,我已将其设置为 40,这应该会给您大约 25fps。 RUN_TIME 参数决定动画应该播放多长时间,设置为 2 秒。我会玩弄 RUN_TIME。动画示例允许每个周期之间的可变延迟,这将为您提供更流畅的动画
    • +1 表示精心设计;我无法抗拒这个variation
    【解决方案4】:

    这个精心设计的example 很容易承认下面的变化。它利用JLayeredPane 中封闭面板的首选大小。

    /**
     * @see https://stackoverflow.com/a/16322007/230513
     * @see https://stackoverflow.com/a/16316345/230513
     */
    public class TestPane extends JLayeredPane {
    
        private static final int WIDE = 200;
        private static final int HIGH = 5 * WIDE / 8; // ~1/phi
        private JPanel panel;
    
        public TestPane() {
            panel = new JPanel();
            panel.setBackground(Color.RED);
            panel.add(new JButton("Test"));
            add(panel);
    
            Dimension size = panel.getPreferredSize();
            int half = HIGH / 2 - size.height / 2;
            Rectangle from = new Rectangle(size);
            from.translate(WIDE, half);
            Rectangle to = new Rectangle(size);
            to.translate(0, half);
            panel.setBounds(from);
    
            Animate animate = new Animate(panel, from, to);
            animate.start();
        }
    
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(WIDE, HIGH);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 2020-12-29
      • 2023-03-22
      • 1970-01-01
      • 2012-12-03
      • 1970-01-01
      相关资源
      最近更新 更多