2012-09-22 33 views
5

Vì vậy, tôi đang tạo một ứng dụng cơ bản mà tôi muốn có một JLabel ở dưới cùng của màn hình bắt đầu ở góc dưới bên trái và di chuyển, phong cách hoạt hình, góc dưới cùng bên phải trong một thời gian nhất định, và một hình ảnh tĩnh ở trung tâm. Để làm điều này, tôi đã tạo một JFrame với một JPanel bằng cách sử dụng BorderLayout. Có một JLabel với một ImageIcon được thêm vào BorderLayout.CENTER và một JPanel tại BorderLayout.SOUTH. Mã của tôi, trong khi vội vàng viết và xa khá, là: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(); 
} 

}

biểu tượng IMAGEtrong ở trung tâm JLabel vẻ tốt, và JLabel trống với một biên giới xuất hiện ở phía dưới, nhưng tôi không thể có được JLabel thứ hai có hình mũi tên để hiển thị trên màn hình. Cuối cùng, tôi sẽ thay đổi để scheduleAtFixedRate liên tục di chuyển JLabel, nhưng ngay bây giờ tôi thậm chí không thể có được hình ảnh xuất hiện trên màn hình.

Tôi cũng hiểu rằng tôi rất có thể sẽ không thể sử dụng FlowLayout cho điều này, vì tôi hiểu nó không cho phép bạn đặt vị trí của các thành phần của bạn. Tôi đã thử sử dụng cách bố trí rỗng, nhưng với cách bố trí rỗng, nhãn JL trống rỗng có viền không xuất hiện. Tôi hầu như không thể làm nổi bật phần đầu của đường viền ở cạnh dưới của khung hình, nhưng ngay cả với setLocation tôi không thể làm cho nó xuất hiện ở nơi tôi muốn nó.

Rõ ràng, quá trình suy nghĩ của tôi là thiếu sót, vì vậy mọi trợ giúp sẽ được đánh giá cao.

Trả lời

15

Việc sử dụng luồng của bạn là sai đối với các ứng dụng Swing. Bạn không nên cố gắng thêm hoặc loại bỏ các thành phần trong một chủ đề nền mà thay vào đó nên sử dụng một Swing Timer để làm điều này trên thread sự kiện Swing.

Ngoài ra, những gì bạn có nghĩa là bởi:

Tôi muốn có một JLabel di chuyển ở dưới cùng của màn hình

Xin làm rõ hiệu quả mà bạn đang cố gắng để đạt được.

Cũng liên quan,

Tôi cũng hiểu rằng tôi sẽ hầu như không thể sử dụng FlowLayout cho điều này, như tôi hiểu nó không cho phép bạn thiết lập vị trí của các thành phần của bạn. Tôi đã thử sử dụng cách bố trí rỗng, nhưng với cách bố trí rỗng, nhãn JL trống rỗng có viền không xuất hiện. Tôi hầu như không thể làm nổi bật phần đầu của đường viền ở cạnh dưới của khung hình, nhưng ngay cả với setLocation tôi không thể làm cho nó xuất hiện ở nơi tôi muốn nó.

Không, không sử dụng bố cục trống cho trường hợp này. Có nhiều trình quản lý bố cục tốt hơn có thể giúp bạn xây dựng ứng dụng của mình theo cách độc lập hơn về nền tảng.

Sửa 3
Về:

Để làm rõ, ở dưới cùng của màn hình tôi muốn có một JLabel ở góc xa bên phải, sau đó trong timer swing, các JLabel sẽ dần dần di chuyển đến còn lại cho đến khi nó rời khỏi màn hình. Nếu tôi có thể đặt setLocation để làm việc, tiền đề cơ bản sẽ là có một biến x đặt thành 600, và sau đó mỗi lần giảm thứ hai x bằng cách nói 50 và sau đó vẽ lại JLabel tại vị trí mới trên màn hình. Hoạt ảnh cơ bản.

Tôi sẽ tạo JPanel ở cuối màn hình với mục đích giữ JLabel hoặc hiển thị hình ảnh mà không có JLabel bằng cách ghi đè phương pháp paintComponent(...) của nó. Nếu bạn sử dụng nó như một thùng chứa, thì có, bố cục của nó phải là rỗng, nhưng phần còn lại của GUI không được sử dụng bố cục rỗng. The Swing Timer chỉ đơn giản là sẽ thay đổi vị trí của JLabel và sau đó gọi repaint() trên JPanel/container của nó. Nếu bạn đi tuyến đường thứ hai, bạn sẽ vẽ hình ảnh theo phương pháp paintComponent(...) của JPanel bằng cách sử dụng g.drawImage(myImage, x, y) và bộ hẹn giờ của bạn sẽ thay đổi x và/hoặc y và gọi repaint() trên bản vẽ JPanel.

Ngoài ra, bạn có thể không muốn tiếp tục thêm JLabel vào bộ hẹn giờ mà chỉ cần di chuyển nhãn JLabel đã được hiển thị trong GUI.

Ngoài ra, để tránh các vấn đề trọng tâm, không sử dụng KeyListener để nắm bắt đầu vào gõ phím mà thay vào đó sử dụng Khóa Ràng buộc. Google sẽ hướng dẫn bạn đến một hướng dẫn tuyệt vời về cấu trúc này.

Sửa 4
Ví dụ:

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

Mà sẽ tạo GUI này:

enter image description here

+1

+1. Và thiết lập vị trí trong khi sử dụng một LayoutManager không có hiệu lực. Nó là LayoutManager xác định vị trí – Robin

+0

Tôi thực sự đã có bộ đếm thời gian trước đó, nhưng ai đó đã nói với tôi nó là tốt hơn để sử dụng ScheduledExecutorService.Đặt nó trở lại sử dụng bộ hẹn giờ Swing là không có vấn đề, tôi sẽ chỉnh sửa câu hỏi – awestover89

+0

Nếu vẫn không hoạt động, sau đó tạo một chương trình đơn giản và có thể chạy được, thể hiện vấn đề của bạn và sử dụng hình ảnh trực tuyến có sẵn cho tất cả, [sscce] (http://sscce.org). –

8

Ba khả năng:

  • Bạn có thể sử dụng thư viện như SlidingLayout để tạo chuyển đổi như vậy với rất ít dòng mã. Bạn sẽ không thể tinh chỉnh hoạt ảnh nhưng cuộc sống của bạn sẽ dễ dàng hơn.

  • Bạn có thể sử dụng công cụ hoạt ảnh như Universal Tween Engine để định cấu hình mọi thứ bằng tay và tinh chỉnh hoạt ảnh nhiều như bạn muốn (lib đầu tiên sử dụng hình này dưới mui xe). Sử dụng công cụ này, bạn có thể làm động những gì bạn muốn: vị trí, màu sắc, kích thước font chữ, ...

  • Bạn có thể mã hóa tất cả mọi thứ bằng tay và nắm lấy địa ngục đó là phim hoạt hình thế giới :)

cuối cùng, bạn sẽ có thể nhanh chóng tạo ra hình ảnh động như thế này (đó là một công cụ tôi hiện đang làm việc trên, được sử dụng để cấu hình các dự án thực cho dev android sử dụng khuôn khổ trò chơi LibGDX):
enter image description hereenter image description here

tôi làm cho các thư viện này giảm bớt nỗi đau là giao diện người dùng hoạt hình (Tôi yêu thiết kế giao diện người dùng: p). Tôi phát hành chúng mã nguồn mở (miễn phí để sử dụng, giấy phép apache-2), hy vọng họ có thể giúp một số người quá.

Nếu bạn cần trợ giúp, có một diễn đàn trợ giúp dành riêng cho mỗi thư viện.

+1

wow. khá trơn! 1+ –

+3

Tôi sẽ xem xét điều đó. Trở lại khi tôi còn là sinh viên, tôi ghét ý tưởng sử dụng các thư viện bên ngoài. Một cái gì đó để làm với niềm tự hào và muốn nói rằng tôi đã làm tất cả bản thân mình. Tuy nhiên, tôi đã học được cách ôm lấy các thư viện hiện có trong sự nghiệp chuyên nghiệp của tôi, toàn bộ việc không tái tạo lại thứ bánh xe. Tôi sẽ cho bạn biết, cảm ơn – awestover89

+0

Bạn đã thực hiện một ngày của tôi. – Rempelos

1

Rất đơn giản, xem xét điều này:

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