5

だから私は、左下隅から始まり、設定された時間内に右下隅に移動する画面の下部にJLabel、アニメーションスタイル、および中央に静止画像を配置したい基本的なアプリケーションを作成しています. これを行うために、BorderLayout を使用して JPanel で JFrame を作成しました。ImageIcon が BorderLayout.CENTER に追加された 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 が下部に表示されますが、矢印の画像を持つ 2 番目の JLabel が画面に表示されません。最終的には scheduleAtFixedRate に変更して JLabel を継続的に移動しますが、今のところ画像を画面に表示することさえできません。

また、コンポーネントの場所を設定できないことを理解しているため、これには FlowLayout を使用できない可能性が高いことも理解しています。null レイアウトを使用してみましたが、null レイアウトではボーダー付きの空の JLabel が表示されません。フレームの下端にある境界線の上部をかろうじて確認できますが、setLocation を使用しても、必要な場所に表示することはできません。

明らかに、私の思考プロセスには欠陥があるので、助けていただければ幸いです。

4

3 に答える 3

15

Swingアプリケーションでは、スレッドの使用はすべて間違っています。バックグラウンドスレッドでコンポーネントを追加または削除しようとしてはいけませんが、代わりにSwingタイマーを使用してSwingイベントスレッドでこれを行う必要があります。

また、どういう意味ですか:

画面の下部にスクロールするJLabelが必要です

達成しようとしている効果を明確にしてください。

また、に関して

また、コンポーネントの場所を設定できないことを理解しているため、これにFlowLayoutを使用できない可能性が高いことも理解しています。nullレイアウトを使用してみましたが、nullレイアウトでは、境界線のある空のJLabelが表示されません。フレームの下端の境界線の上部はほとんどわかりませんが、setLocationを使用しても、目的の場所に表示させることができません。

いいえ、この状況ではnullレイアウトを使用しないでください。よりクリーンでプラットフォームに依存しない方法でアプリケーションを構築するのに役立つ、はるかに優れたレイアウトマネージャーがあります。

編集3
に関して:

明確にするために、画面の下部の右端にJLabelが必要です。次に、スイングタイマーで、JLabelが画面を離れるまで徐々に左に移動します。setLocationを機能させることができれば、基本的な前提は、変数xを600に設定し、1秒ごとにxをたとえば50ずつデクリメントしてから、画面上の新しい場所にJLabelを再描画することです。基本的なアニメーション。

JLabelを保持するか、メソッドをオーバーライドしてJLabelなしで画像を表示するために、画面の下部にJPanelを作成しますpaintComponent(...)。コンテナとして使用する場合は、はい、そのレイアウトはnullである必要がありますが、GUIの残りの部分ではnullレイアウトを使用しないでください。Swing Timerは、JLabelの場所を変更してrepaint()から、JPanel/containerを呼び出すだけです。後者のルートを使用する場合は、paintComponent(...)を使用してJPanelの方法で画像を描画しg.drawImage(myImage, x, y)、タイマーがxまたはy、あるいはその両方を変更repaint()して、描画JPanelを呼び出します。

また、タイマーにJLabelを追加し続けるのではなく、GUIにすでに表示されているJLabelを移動するだけの場合もあります。

また、フォーカスの問題を回避するために、KeyListenerを使用してキーストローク入力をキャプチャするのではなく、KeyBindingsを使用してください。Googleは、この構成に関する優れたチュートリアルを紹介します。

編集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

3 つの可能性:

  • SlidingLayoutのようなライブラリを使用して、非常に少ないコード行でこのようなトランジションを作成できます。アニメーションを微調整することはできませんが、生活は楽になります。

  • Universal Tween Engineのようなアニメーション エンジンを使用して、すべてを手動で構成し、必要に応じてアニメーションを微調整できます (最初のライブラリは内部でこれを使用しています)。このようなエンジンを使用すると、位置、色、フォント サイズなど、必要なものをアニメーション化できます。

  • すべてを手作業でコーディングして、アニメーションの世界である地獄を受け入れることができます :)

最終的に、次のようなアニメーションをすばやく作成できるようになります (これは私が現在取り組んでいるツールであり、LibGDX ゲーム フレームワークを使用して Android 開発用の Eclipse プロジェクトを構成するために使用されます)。
ここに画像の説明を入力 ここに画像の説明を入力

これらのライブラリを作成したのは、UI アニメーションの苦痛を軽減するためです (私は UI デザインが大好きです :p)。一部の人々にも役立つことを期待して、私はそれらをオープンソース (無料で使用でき、ライセンスは apache-2) でリリースしました。

ヘルプが必要な場合は、各ライブラリ専用のヘルプ フォーラムがあります。

于 2012-09-22T17:11:29.610 に答える