Java Animate JLabel

Então estou criando um aplicativo básico que eu quero ter um JLabel na parte inferior da canvas que começa no canto inferior esquerdo e se move, estilo de animação, para o canto inferior direito em um tempo definido e uma imagem estática no centro . Para fazer isso, criei um JFrame com um JPanel usando BorderLayout. Há um JLabel com um ImageIcon adicionado ao BorderLayout.CENTER e um JPanel no BorderLayout.SOUTH. Meu código, embora escrito apressadamente e longe de ser bonito, é:

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

}

O ImageIcon no centro JLabel aparece bem, e o JLabel vazio com uma borda aparece na parte inferior, mas não consigo obter o segundo JLabel com a imagem da seta para aparecer na canvas. Eventualmente eu vou mudar para scheduleAtFixedRate para mover continuamente o JLabel, mas agora eu não posso nem conseguir que a imagem apareça na canvas.

Eu também entendo que provavelmente não usarei o FlowLayout para isso, pois entendo que ele não permite que você defina a localização dos seus componentes. Eu tentei usar o layout nulo, mas com layout nulo o JLabel vazio com uma borda não aparece. Eu mal posso distinguir o topo da borda na borda inferior do quadro, mas mesmo com setLocation não consigo fazer com que apareça onde quero.

Obviamente, meu processo de pensamento é falho, então qualquer ajuda seria apreciada.

Seu uso de threading está todo errado para aplicativos Swing. Você não deve tentar adicionar ou remover componentes em um thread de segundo plano, mas deve usar um Timer de Swing para fazer isso no encadeamento de events do Swing.

Além disso, o que você quer dizer com:

Eu quero ter um JLabel de rolagem na parte inferior da canvas

Por favor, esclareça o efeito que você está tentando alcançar.

Também a respeito,

Eu também entendo que provavelmente não usarei o FlowLayout para isso, pois entendo que ele não permite que você defina a localização dos seus componentes. Eu tentei usar o layout nulo, mas com layout nulo o JLabel vazio com uma borda não aparece. Eu mal posso distinguir o topo da borda na borda inferior do quadro, mas mesmo com setLocation não consigo fazer com que apareça onde quero.

Não, não use layout nulo para essa situação. Existem gerenciadores de layout muito melhores que podem ajudá-lo a criar seu aplicativo de uma maneira mais limpa e independente de plataforma.

Editar 3
A respeito de:

Para esclarecer, na parte inferior da canvas eu quero um JLabel no canto mais à direita e, em seguida, no timer de giro, o JLabel se moverá gradualmente para a esquerda até sair da canvas. Se eu conseguisse colocar o setLocation em funcionamento, a premissa básica seria ter uma variável x definida para 600 e, em seguida, cada segundo decrementar x por exemplo 50 e, em seguida, redesenhar o JLabel no novo local na canvas. Animação básica.

Eu criaria um JPanel para a parte inferior da canvas com o propósito de segurar seu JLabel ou exibir a imagem sem um JLabel, substituindo o método paintComponent(...) . Se você usá-lo como um contêiner, então sim, seu layout deve ser nulo, mas o resto da GUI não deve estar usando o layout nulo. O Swing Timer simplesmente mudaria a localização do JLabel e então chamaria repaint() em seu JPanel / container. Se você seguir a última rota, desenhe a imagem no método paintComponent(...) do JPanel usando g.drawImage(myImage, x, y) , e seu timer mudará x e / ou y e chamar repaint() em o desenho JPanel.

Além disso, você provavelmente não deseja continuar adicionando um JLabel ao cronômetro, mas simplesmente movendo o JLabel que já está exibido na GUI.

Além disso, para evitar problemas de foco, não use um KeyListener para capturar a input de pressionamento de tecla, mas use as Ligações de teclas. O Google direcionará você para um ótimo tutorial sobre essa construção.

Editar 4
Por exemplo:

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

Qual criará esta GUI:

insira a descrição da imagem aqui

Três possibilidades:

  • Você pode usar uma biblioteca como o SlidingLayout para criar essa transição com poucas linhas de código. Você não será capaz de ajustar a animação, mas sua vida será mais fácil.

  • Você pode usar um mecanismo de animação como o Universal Tween Engine para configurar tudo manualmente e ajustar a animação tanto quanto quiser (a primeira lib usa essa sob a capa). Usando esse mecanismo, você pode animar o que quiser: posições, colors, tamanho da fonte, …

  • Você pode codificar tudo à mão e abraçar o inferno que é o mundo da animação 🙂

No final, você poderá criar rapidamente animações como essas (é uma ferramenta que estou trabalhando atualmente, usada para configurar projetos de eclipse para o Android dev usando a estrutura de jogo LibGDX):
insira a descrição da imagem aquiinsira a descrição da imagem aqui

Eu fiz essas bibliotecas para aliviar a dor que é a animação da interface do usuário (eu amo design de interface do usuário: p). Eu os liberei open-source (livre para usar, licença apache-2), esperando que eles possam ajudar algumas pessoas também.

Se você precisar de ajuda, há um fórum de ajuda dedicado para cada biblioteca.

Muito simples, olhe para isto:

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