Java Animate JLabel

Así que estoy creando una aplicación básica que quiero tener un JLabel en la parte inferior de la pantalla que comienza en la esquina inferior izquierda y se mueve, estilo de animación, a la esquina inferior derecha en un tiempo establecido, y una imagen estática en el centro . Para hacer esto, creé un JFrame con un JPanel usando BorderLayout. Hay un JLabel con un ImageIcon agregado a BorderLayout.CENTER y un JPanel en BorderLayout.SOUTH. Mi código, aunque escrito apresuradamente y lejos de ser bonito, es:

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

}

El ImageIcon en el centro JLabel parece estar bien, y el JLabel vacío con un borde aparece en la parte inferior, pero no puedo obtener el segundo JLabel con la imagen de la flecha para mostrar en la pantalla. Eventualmente cambiaré a scheduleAtFixedRate para mover continuamente el JLabel, pero en este momento no puedo ni siquiera hacer que la imagen aparezca en la pantalla.

También entiendo que probablemente no pueda usar FlowLayout para esto, ya que entiendo que no le permite establecer la ubicación de sus componentes. Intenté usar el diseño nulo, pero con el diseño nulo, el JLabel vacío con un borde no aparece. Apenas puedo distinguir la parte superior del borde en el borde inferior del marco, pero incluso con setLocation no puedo hacer que aparezca donde quiero.

Obviamente, mi proceso de pensamiento es defectuoso, por lo que cualquier ayuda sería apreciada.

Su uso del enhebrado es incorrecto para las aplicaciones Swing. No debería tratar de agregar o quitar componentes en un hilo de fondo, sino que debe usar un temporizador de swing para hacer esto en el hilo del evento Swing.

Además, ¿a qué te refieres con:

Quiero tener un JLabel desplazable en la parte inferior de la pantalla

Por favor, aclara el efecto que intentas lograr.

También con respecto a,

También entiendo que probablemente no pueda usar FlowLayout para esto, ya que entiendo que no le permite establecer la ubicación de sus componentes. Intenté usar el diseño nulo, pero con el diseño nulo, el JLabel vacío con un borde no aparece. Apenas puedo distinguir la parte superior del borde en el borde inferior del marco, pero incluso con setLocation no puedo hacer que aparezca donde quiero.

No, no use el diseño nulo para esta situación. Hay administradores de disposición mucho mejores que pueden ayudarlo a construir su aplicación de una manera más limpia e independiente de la plataforma.

Editar 3
Respecto a:

Para aclarar, en la parte inferior de la pantalla quiero un JLabel en la esquina derecha, luego en el temporizador de swing, JLabel se moverá gradualmente hacia la izquierda hasta que salga de la pantalla. Si pudiera hacer que setLocation funcione, la premisa básica sería tener una variable x establecida en 600, y luego cada segundo disminuir x por ejemplo 50 y luego volver a dibujar JLabel en la nueva ubicación en la pantalla. Animación básica

Crearía un JPanel para la parte inferior de la pantalla con el fin de mantener su JLabel o mostrar la imagen sin un JLabel anulando su paintComponent(...) . Si lo usa como un contenedor, entonces sí, su diseño debe ser nulo, pero el rest de la GUI no debe usar un diseño nulo. El Swing Timer simplemente cambiaría la ubicación de JLabel y luego llamaría a repaint() en su JPanel / contenedor. Si realiza la última ruta, dibujaría la imagen en el método paintComponent(...) g.drawImage(myImage, x, y) usando g.drawImage(myImage, x, y) , y su temporizador cambiaría x y / o y llamaría a repaint() en el dibujo JPanel.

Además, es probable que no desee seguir agregando un JLabel en su temporizador, sino simplemente mover el JLabel que ya se muestra en la GUI.

Además, para evitar problemas de enfoque, no use un KeyListener para capturar la entrada del teclado sino más bien use Key Bindings. Google lo dirigirá a un gran tutorial sobre este constructo.

Editar 4
Por ejemplo:

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

Que creará esta GUI:

enter image description here

Tres posibilidades:

  • Puede usar una biblioteca como SlidingLayout para crear dicha transición con muy pocas líneas de código. No podrás modificar la animación, pero tu vida será más fácil.

  • Puede utilizar un motor de animación como Universal Tween Engine para configurar todo a mano y ajustar la animación tanto como desee (la primera lib usa esta debajo del capó). Usando tal motor, puedes animar lo que quieras: posiciones, colores, tamaño de letra, …

  • Puedes codificar todo a mano y abrazar el infierno que es el mundo de la animación 🙂

Al final, podrás crear rápidamente animaciones como estas (es una herramienta en la que estoy trabajando actualmente, que se utiliza para configurar proyectos de eclipse para Android dev usando el marco del juego LibGDX):
enter image description hereenter image description here

Hice estas bibliotecas para aliviar el dolor que es la animación UI (me encanta el diseño de UI: p). Los liberé de código abierto (de uso gratuito, licencia de apache-2), con la esperanza de que también puedan ayudar a algunas personas.

Si necesita ayuda, hay un foro de ayuda dedicado para cada biblioteca.

Muy simple, mira esto:

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