2012-09-22 10 views
5

Tworzę więc podstawową aplikację, która ma mieć JLabel u dołu ekranu, który zaczyna się w lewym dolnym rogu i przesuwa się, styl animacji, do prawego dolnego rogu w określonym czasie, oraz nieruchomy obraz w środku. Aby to zrobić, stworzyłem JFrame z JPanel używając BorderLayout. Istnieje JLabel z ImageIcon dodanym do BorderLayout.CENTER i JPanel na BorderLayout.SOUTH. Mój kod, podczas pośpiesznie napisane i daleko od dość, to:Java Animate JLabel

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

}

ImageIcon w centrum JLabel pojawia się w porządku, i pusty JLabel z granicy pojawia się na dole, ale nie mogę dostać drugi JLabel z obrazem strzałki, aby pokazać się na ekranie. W końcu zmienię się na scheduleAtFixedRate, aby ciągle przesuwać JLabel, ale w tej chwili nie mogę nawet wyświetlić obrazu na ekranie.

Rozumiem również, że najprawdopodobniej nie będę w stanie korzystać z FlowLayout, ponieważ rozumiem, że nie pozwala to na ustawienie lokalizacji komponentów. Próbowałem użyć pustego układu, ale z pustym układem pusty JLabel z ramką nie pojawia się. Ledwo widzę górną krawędź w dolnej krawędzi ramy, ale nawet przy ustawieniu setLocation nie mogę sprawić, że pojawi się tam, gdzie chcę.

Oczywiście, mój proces myślenia jest wadliwy, więc każda pomoc będzie doceniona.

Odpowiedz

15

Używanie wątków jest niewłaściwe w przypadku aplikacji Swing. Nie powinieneś próbować dodawać ani usuwać komponentów w wątku tła, ale zamiast tego należy użyć Swing Timer, aby to zrobić w wątku zdarzenia Swing.

Ponadto, co masz na myśli przez:

chcę mieć przewijanie JLabel na dole ekranu

Proszę wyjaśnić efekt próbujesz osiągnąć.

dotyczące Również

Rozumiem też, że będę najprawdopodobniej nie będzie w stanie wykorzystać FlowLayout na to, jak rozumiem, że nie można ustawić lokalizację poszczególnych elementów zestawu. Próbowałem użyć pustego układu, ale z pustym układem pusty JLabel z ramką nie pojawia się. Ledwo widzę górną krawędź w dolnej krawędzi ramy, ale nawet przy ustawieniu setLocation nie mogę sprawić, że pojawi się tam, gdzie chcę.

Nie, nie należy używać układu pustego dla tej sytuacji. Istnieje znacznie lepszych menedżerów układów, które mogą pomóc w budowaniu aplikacji w sposób bardziej niezależny od platformy.

Edycja 3
odniesieniu do:

celu wyjaśnienia, w dolnej części ekranu, chcę JLabel na prawym rogu, a następnie w zegarze wachlowania, JLabel będzie stopniowo przejść do lewo, aż opuści ekran. Gdybym mógł ustawić właściwość setLocation, podstawową przesłanką byłoby ustawienie zmiennej x na 600, a następnie co sekundę x przez powiedzmy 50, a następnie ponowne rysowanie JLabel w nowej lokalizacji na ekranie. Podstawowa animacja.

Chciałbym stworzyć JPanel na dole ekranu dla celów albo trzymając JLabel lub wyświetlanie obrazu bez JLabel nadrzędnymi swoją metodę paintComponent(...). Jeśli używasz go jako kontenera, to tak, jego układ powinien być pusty, ale reszta GUI nie powinna używać układu null. Swing Timer po prostu zmienił lokalizację JLabel, a następnie zadzwonił pod numer repaint() na swoim JPanel/container. Jeśli pójdziesz tą drugą trasą, narysujesz obraz w metodzie JPanel paintComponent(...) używając g.drawImage(myImage, x, y), a twój zegar zmieni x i/lub y i zadzwoni na repaint() na rysunku JPanel.

Ponadto, prawdopodobnie nie chcesz dodawać JLabel do licznika czasu, ale po prostu przesuwać JLabel, który jest już wyświetlany w GUI.

Ponadto, aby uniknąć problemów z ostrością, nie używaj KeyListener do przechwytywania danych za naciśnięciem klawisza, ale raczej użyj Wiązania kluczy. Google skieruje Cię do świetnego samouczka na ten temat.

Edycja 4
Na przykład:

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

co stworzy to GUI:

enter image description here

+1

+1. Ustawienie lokalizacji podczas korzystania z Menedżera układu nie ma żadnego efektu. Jest to LayoutManager, który określa lokalizację – Robin

+0

faktycznie miałem Timer wcześniej, ale ktoś powiedział mi, że lepiej jest użyć ScheduledExecutorService.Powrót do korzystania z timera Swing nie jest problemem, będę edytować pytanie – awestover89

+0

Jeśli nadal nie działa, a następnie stwórz prosty program, który będzie kompilować i działać, który demonstruje twój problem i który używa obrazów online dostępnych dla wszystkich, [sscce] (http://sscce.org). –

8

trzy możliwości:

  • Możesz użyć biblioteki takiej jak SlidingLayout, aby utworzyć takie przejście z bardzo małą liczbą wierszy kodu. Nie będziesz w stanie poprawić animacji, ale twoje życie będzie łatwiejsze.

  • Możesz użyć silnika animacji, takiego jak Universal Tween Engine, aby skonfigurować wszystko ręcznie i dostosować animację tak, jak chcesz (pierwsza lib używa tej pod maską). Stosując taki silnik, można animować, co chcesz: pozycji, kolorów, rozmiaru czcionki, ...

  • można zakodować wszystko ręką i przyjąć, że piekło jest świat animacji :)

w końcu będziesz w stanie szybko tworzyć animacje jak te (jest to narzędzie obecnie jestem w pracy, służy do konfigurowania projektów eclipse android dev przy użyciu ramy grę libgdx):
enter image description hereenter image description here

I stworzyłem te biblioteki, aby złagodzić ból to animacja interfejsu użytkownika (kocham projekt interfejsu użytkownika: p). Wydałem je open-source (darmowy do użytku, licencja apache-2), mając nadzieję, że mogą pomóc niektórym osobom.

Jeśli potrzebujesz pomocy, istnieje dedykowane forum pomocy dla każdej biblioteki.

+1

wow. całkiem śliczne! 1+ –

+3

Zajrzę do tego. Kiedy byłem studentem, nienawidziłem idei korzystania z zewnętrznych bibliotek. Coś związanego z dumą i chęć powiedzenia, że ​​zrobiłem to wszystko sam. Nauczyłem się jednak korzystać z istniejących już bibliotek w mojej karierze zawodowej, a wszystko to nie wymyślając niczego nowego. Dam ci znać, dzięki – awestover89

+0

Zrobiłeś mój dzień. – Rempelos

1

Bardzo proste, spójrz na to:

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