使用计时器对JPanel(滑入)进行animation处理

我正在尝试使用我创build的这个类从侧面创build一个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(); } } 

那么,我不知道问题是什么。 我已经尝试了很多不同的东西,但是我似乎并没有把它做好。

定时器应该改变每个滴答的位置,直到它到位,而是在每个滴答,你正在运行一个for-next循环,这是阻止EDT直到循环结束,阻止更新UI

以示例更新

例如…

 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; } } 

更新

我应该在昨晚join这个(不想上床的一年,有两个做过的父母,不要再说了)

animation是一个复杂的话题,尤其是当你开始考虑变速时(这个例子是静态的)。

而不是重新发明轮子,你应该认真考虑看看…

  • 时间框架 – 这是基本的animation框架,不会假设您可能会喜欢使用它。
  • 三叉戟 – 类似于时序框架,但也支持基于Swing的组件(通过reflection)内置
  • 通用吐温引擎

这个很好的例子容易承认下面的变化。 它利用JLayeredPane封闭的面板的首选大小。

TestPane

 /** * @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); } } 

OP代码中有许多问题。 正如MadProrammer所指出的那样,每个计时器滴答只能移动一步。 这里是一个简单的,经过testing的校正OPs代码,每次移动JPanel一个像素,每秒25次。 注意评论:

 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(); } 

例如滑动任何东西


  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); } } } } }