Java Animate JLabel

所以我创建了一个基本的应用程序,我希望在屏幕底部有一个JLabel,从左下角开始,动画样式,在设定的时间内移动到右下角,在中心移动静态图像。 为此,我使用BorderLayout创建了一个带JPanel的JFrame。 有一个JLabel,其中ImageIcon添加到BorderLayout.CENTER,而一个JPanel添加到BorderLayout.SOUTH。 我的代码虽然草率而且非常漂亮,但是:

import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.BorderFactory; public class GameWindow extends JPanel{ private static JLabel mainWindow, arrowLabel, arrowBox; protected static JFrame frame; protected static JPanel arrows; public static int x = 600; public GameWindow(){ mainWindow = new JLabel("Center"); arrowLabel = new JLabel("Moving"); arrows = new JPanel(); arrows.setSize(600, 100); arrows.setLayout(null); arrowBox = new JLabel(""); arrowBox.setBounds(0, 0, 150, 100); arrowBox.setPreferredSize(new Dimension(150, 100)); arrowBox.setBorder(BorderFactory.createLineBorder(Color.black)); arrows.add(arrowBox); this.setSize(600,600); this.setLayout(new BorderLayout()); this.add(mainWindow, BorderLayout.CENTER); this.add(arrows, BorderLayout.SOUTH); } public static void main(String[] args) { GameWindow g = new GameWindow(); frame = new JFrame("Sword Sword Revolution"); frame.add(g); frame.setSize(600,600); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); Timer t = new Timer(1000, new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { arrows.add(arrowLabel); arrowLabel.setBounds(x, 100, 100, 100); x-=50; arrows.repaint(); frame.repaint(); } }); t.start(); } 

}

中心JLabel中的ImageIcon显示正常,带有边框的空JLabel出现在底部,但我无法获得第二个JLabel,箭头图像显示在屏幕上。 最终我将更改为scheduleAtFixedRate以连续移动JLabel,但是现在我甚至无法将图像显示在屏幕上。

我也明白,我很可能无法使用FlowLayout,因为我知道它不允许您设置组件的位置。 我尝试使用null布局,但是使用null布局时,不会出现带边框的空JLabel。 我几乎无法在框架的底部边缘找出边框的顶部,但即使使用setLocation,我也无法将其显示在我想要的位置。

显然,我的思维过程存在缺陷,所以任何帮助都会受到赞赏。

对于Swing应用程序,使用线程是完全错误的。 您不应该尝试在后台线程中添加或删除组件,而应该使用Swing Timer在Swing事件线程上执行此操作。

另外,你的意思是:

我想在屏幕底部有一个滚动的JLabel

请澄清你想要达到的效果。

关于,

我也明白,我很可能无法使用FlowLayout,因为我知道它不允许您设置组件的位置。 我尝试使用null布局,但是使用null布局时,不会出现带边框的空JLabel。 我几乎无法在框架的底部边缘找出边框的顶部,但即使使用setLocation,我也无法将其显示在我想要的位置。

不,不要在这种情况下使用null布局。 有更好的布局管理器可以帮助您以更清洁,更独立于平台的方式构建应用程序。

编辑3
关于:

为了澄清,在屏幕的底部我想要在最右角的JLabel,然后在摆动计时器中,JLabel将逐渐向左移动直到它离开屏幕。 如果我可以使setLocation工作,基本前提是将变量x设置为600,然后每秒递减x乘50,然后在屏幕上的新位置重绘JLabel。 基本动画。

我会为屏幕底部创建一个JPanel,用于保存JLabel或通过覆盖其paintComponent(...)方法显示没有JLabel的图像。 如果你将它用作容器,那么是的,它的布局应该为null,但GUI的其余部分不应该使用null布局。 Swing Timer只是改变JLabel的位置,然后在其JPanel /容器上调用repaint() 。 如果你选择后一种路线,你可以使用g.drawImage(myImage, x, y)在JPanel的paintComponent(...)方法中绘制图像,你的计时器会改变x和/或y并调用repaint() on绘图JPanel。

此外,您可能不希望在计时器中添加JLabel,而只需移动已在GUI中显示的JLabel。

此外,为避免焦点问题,请勿使用KeyListener捕获击键输入,而是使用键绑定。 Google会引导您找到有关此结构的精彩教程。

编辑4
例如:

 import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.event.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.EnumMap; import javax.imageio.ImageIO; import javax.swing.*; @SuppressWarnings("serial") public class AnimateExample extends JPanel { public static final String DUKE_IMG_PATH = "https://duke.kenai.com/iconSized/duke.gif"; private static final int PREF_W = 800; private static final int PREF_H = 800; private static final int TIMER_DELAY = 20; private static final String KEY_DOWN = "key down"; private static final String KEY_RELEASE = "key release"; public static final int TRANSLATE_SCALE = 3; private static final String BACKGROUND_STRING = "Use Arrow Keys to Move Image"; private static final Font BG_STRING_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 32); private EnumMap dirMap = new EnumMap(Direction.class); private BufferedImage image = null; private int imgX = 0; private int imgY = 0; private int bgStringX; private int bgStringY; public AnimateExample() { for (Direction dir : Direction.values()) { dirMap.put(dir, Boolean.FALSE); } try { URL imgUrl = new URL(DUKE_IMG_PATH); image = ImageIO.read(imgUrl); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } new Timer(TIMER_DELAY, new TimerListener()).start(); // here we set up our key bindings int condition = JComponent.WHEN_IN_FOCUSED_WINDOW; InputMap inputMap = getInputMap(condition); ActionMap actionMap = getActionMap(); for (final Direction dir : Direction.values()) { // for the key down key stroke KeyStroke keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, false); inputMap.put(keyStroke, dir.name() + KEY_DOWN); actionMap.put(dir.name() + KEY_DOWN, new AbstractAction() { @Override public void actionPerformed(ActionEvent arg0) { dirMap.put(dir, true); } }); // for the key release key stroke keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, true); inputMap.put(keyStroke, dir.name() + KEY_RELEASE); actionMap.put(dir.name() + KEY_RELEASE, new AbstractAction() { @Override public void actionPerformed(ActionEvent arg0) { dirMap.put(dir, false); } }); } FontMetrics fontMetrics = getFontMetrics(BG_STRING_FONT); int w = fontMetrics.stringWidth(BACKGROUND_STRING); int h = fontMetrics.getHeight(); bgStringX = (PREF_W - w) / 2; bgStringY = (PREF_H - h) / 2; } @Override public Dimension getPreferredSize() { return new Dimension(PREF_W, PREF_H); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; g.setFont(BG_STRING_FONT); g.setColor(Color.LIGHT_GRAY); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g.drawString(BACKGROUND_STRING, bgStringX, bgStringY); if (image != null) { g.drawImage(image, imgX, imgY, this); } } private class TimerListener implements ActionListener { public void actionPerformed(java.awt.event.ActionEvent e) { for (Direction dir : Direction.values()) { if (dirMap.get(dir)) { imgX += dir.getX() * TRANSLATE_SCALE; imgY += dir.getY() * TRANSLATE_SCALE; } } repaint(); }; } enum Direction { Up(KeyEvent.VK_UP, 0, -1), Down(KeyEvent.VK_DOWN, 0, 1), Left( KeyEvent.VK_LEFT, -1, 0), Right(KeyEvent.VK_RIGHT, 1, 0); private int keyCode; private int x; private int y; private Direction(int keyCode, int x, int y) { this.keyCode = keyCode; this.x = x; this.y = y; } public int getKeyCode() { return keyCode; } public int getX() { return x; } public int getY() { return y; } } private static void createAndShowGui() { AnimateExample mainPanel = new AnimateExample(); JFrame frame = new JFrame("Animate Example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(mainPanel); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } } 

这将创建此GUI:

在此处输入图像描述

三种可能性:

  • 您可以使用像SlidingLayout这样的库来创建具有极少数代码行的转换。 你将无法调整动画,但你的生活会更容易。

  • 您可以使用像Universal Tween Engine这样的动画引擎来手动配置所有内容并根据需要调整动画(第一个lib在引擎盖下使用这个动画)。 使用这样的引擎,你可以动画你想要的:位置,颜色,字体大小,……

  • 你可以手工编写所有东西并拥抱动漫世界的地狱:)

最后,你将能够快速创建这样的动画(这是我目前正在使用的工具,用于使用LibGDX游戏框架为android dev配置eclipse项目):
在此处输入图像描述在此处输入图像描述

我制作了这些库以减轻UI动画的痛苦(我喜欢UI设计:p)。 我发布了他们的开源(免费使用,许可apache-2),希望他们也可以帮助一些人。

如果您需要帮助,每个图书馆都有专门的帮助论坛。

很简单,看看这个:

 javax.swing.JLabel lb = new javax.swing.JLabel(); Image image = Toolkit.getDefaultToolkit().createImage("Your animated GIF"); ImageIcon xIcon = new ImageIcon(image); xIcon.setImageObserver(this); lb.setIcon(xIcon);