5

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

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,因为我知道它不允许您设置组件的位置。我尝试使用空布局,但使用空布局时,不会出现带有边框的空 JLabel。我几乎无法辨认出框架底部边缘的边框顶部,但即使使用 setLocation 我也无法让它出现在我想要的位置。

显然,我的思维过程是有缺陷的,所以任何帮助将不胜感激。

4

3 回答 3

15

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

另外,你的意思是:

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

请澄清您要达到的效果。

还有关于,

我也知道我很可能无法为此使用 FlowLayout,因为我知道它不允许您设置组件的位置。我尝试使用空布局,但使用空布局时,不会出现带有边框的空 JLabel。我几乎无法辨认出框架底部边缘的边框顶部,但即使使用 setLocation 我也无法让它出现在我想要的位置。

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

编辑3
关于:

澄清一下,在屏幕底部,我希望在最右边的角落有一个 JLabel,然后在摇摆计时器中,JLabel 将逐渐向左移动,直到它离开屏幕。如果我能让 setLocation 工作,基本前提是将变量 x 设置为 600,然后每隔一秒将 x 递减 50,然后在屏幕上的新位置重新绘制 JLabel。基础动画。

paintComponent(...)我会为屏幕底部创建一个 JPanel,以便通过覆盖其方法来保存您的 JLabel 或显示没有 JLabel 的图像。如果您将它用作容器,那么是的,它的布局应该为空,但 GUI 的其余部分不应使用空布局。Swing Timer 将简单地更改 JLabel 的位置,然后调用repaint()其 JPanel/容器。如果您走后一条路线,您将使用 JPanel 的paintComponent(...)方法绘制图像g.drawImage(myImage, x, y),并且您的计时器将更改 x 和/或 y 并调用repaint()绘图 JPanel。

此外,您可能不想继续在计时器中添加 JLabel,而只是移动已显示在 GUI 中的 JLabel。

此外,为了避免焦点问题,不要使用 KeyListener 来捕获击键输入,而是使用键绑定。谷歌会引导你到一个关于这个结构的很棒的教程。

编辑 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<Direction, Boolean> dirMap = 
         new EnumMap<AnimateExample.Direction, Boolean>(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:

在此处输入图像描述

于 2012-09-22T16:46:03.873 回答
8

三种可能:

  • 您可以使用像SlidingLayout这样的库,用很少的代码行来创建这样的过渡。您将无法调整动画,但您的生活会更轻松。

  • 您可以使用Universal Tween Engine之类的动画引擎手动配置所有内容并根据需要调整动画(第一个库在后台使用这个)。使用这样的引擎,您可以制作您想要的动画:位置、颜色、字体大小……

  • 您可以手动编写所有代码并拥抱动画世界 :)

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

我制作这些库是为了缓解 UI 动画带来的痛苦(我喜欢 UI 设计:p)。我将它们开源(免费使用,许可 apache-2),希望它们也能帮助一些人。

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

于 2012-09-22T17:11:29.610 回答
1

很简单,看这个:

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);
于 2013-09-26T17:43:08.393 回答