2017-11-18 4 views
0

これは、フレームの左側に所定の数のアイコンが始まり、画面を横切って右側に移動するアニメーションです。各アイコンは、コンテナの単一列のGridLayout内の行を塗りつぶす独自のJPanelに描画され、各JPanelは独自のスレッドでレースします。JPanelに線が描画されていない

フィニッシュラインはコンテナにペイントされているはずですが、表示されません。これは、スイングタイマーの方が良い方法です。私はRacerのJPanel不透明度をfalseに設定しようとしましたが、これはうまくいきません。フィニッシュラインのペイントを実行するには、どうすれば他のスレッドを得ることができますか?

動作していないコード:

gui = new JPanel() { 
    @Override 
    public void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     g.drawLine(finishLineXPos, 0, finishLineXPos, windowHeight); 
    } 
}; 

フルコード:各アイコンは、独自のJPanelに引き出され

import javax.swing.*; 
import java.awt.*; 
import java.util.ArrayList; 

public class Races2 { 

    private JFrame frame; 
    private JPanel gui; // to hold all components 
    private int finishLineXPos; // x-coordinate of finish line 
    private Icon racerImg; 
    private int racerImgWidth; 
    private int racerImgHeight; 
    private int numOfRacers; 
    private ArrayList<Racer> racers; 
    private Racer winner; 
    private int windowHeight; 
    private int windowWidth; 

    public Races(int num) { 
     numOfRacers = num; 
     racerImg = new ImageIcon("races.png"); 
     racerImgWidth = racerImg.getIconWidth(); 
     racerImgHeight = racerImg.getIconHeight(); 
     windowHeight = racerImgHeight * numOfRacers; 
     windowWidth = racerImgWidth * 20; 
     finishLineXPos = racerImgWidth * 18; // two icon widths from the right 

     frame = new JFrame("Off to the Races - by Brienna Herold"); 
     frame.setResizable(false); // prevents window resizing which affects painting 
     gui = new JPanel() { 
      @Override 
      public void paintComponent(Graphics g) { 
       super.paintComponent(g); 
       g.drawLine(finishLineXPos, 0, finishLineXPos, windowHeight); 
      } 
     }; 
     gui.setLayout(new GridLayout(numOfRacers,1)); 
     gui.setPreferredSize(new Dimension(windowWidth, windowHeight)); 

     // Create and add racers to gui panel 
     racers = new ArrayList<Racer>(); 
     for (int i = 0; i < numOfRacers; i++) { 
      Racer racer = new Racer(); 
      gui.add(racer); 
      racers.add(racer); 
     } 

     // Start racers 
     for (Racer racer : racers) { 
      Thread racerThread = new Thread(racer); 
      racerThread.start(); 
     } 

     frame.add(gui); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

    protected class Racer extends JPanel implements Runnable { 
     private int lastPosX; 
     private int posX; 

     public Racer() { 
      posX = 0; 
     } 

     @Override 
     public void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      racerImg.paintIcon(this, g, posX, 0); 
      posX += Math.random() * 20; 
     } 

     @Override 
     public void run() { 
      // While the race has not been won yet, proceed with race 
      while (winner == null) { 
       repaint(); 
       try { 
        Thread.sleep(100); // slows down racing a bit 
       } catch (InterruptedException ex) { 
        ex.printStackTrace(); 
       } 

       // If racer passes specified x-coordinate, set it as winner 
       if (posX >= finishLineXPos) { 
        System.out.println("Winner: " + this.getName()); 
        winner = this; 
       } 
      } 
     } 
    } 
} 
+1

RacerクラスはJPanelまたはSwingコンポーネントクラスではなく、その位置を知っている論理クラスである必要があります。論理クラスは、描画または描画メソッドを呼び出して描画することもできます。これらのRacerオブジェクトのメインのグラフィックスJPanelインスタンスを与え、メインのJPanelに描画させます。スレッドの束ではなく、単一のSwing Timerでアニメーションを動かす。 –

+0

gui JPanelのグラフィックは、追加している他のコンポーネントによって隠されていることに注意してください。だからあなたはそれが何かを描くのを見ていないのです。 –

+0

@DontKnowMuchButGettingBetter、各Racerの背景が透明であれば、guiのラインはまだ表示されないはずですか? – briennakh

答えて

2

コンテナの単一列内の行を埋めGridLayout

まず、カスタムペイントの仕方を学ぶ必要があります。まず、SwingチュートリアルのCustom Paintingのセクションを読んで、いくつかの基本を理解してください。キーポイントのカップル:レイアウトマネージャは、その仕事を行うことができますので、あなたがコンポーネントにのgetPreferredSize()メソッドをオーバーライドする必要が

  1. は推奨サイズを持っています。推奨サイズを指定しなかった場合、サイズはゼロになる可能性がありますので、ペイントするものはありません。

  2. 塗装方法は塗装のみです。スウィングがコンポーネントを再描画するタイミングを制御することはできないため、描画メソッドではコンポーネントの状態を変更しないでください。したがって、塗りつぶす画像の位置を制御するには、setPositionX(...)のようなメソッドが必要です。スレッド(またはタイマー)では、このメソッドを呼び出して場所を変更し、コンポーネントのrepaint()を呼び出します。

フィニッシュラインは、容器の上に塗装を取得することになったが、それは

を表示されないさてあなたはそれらのように、レースパネルの上部に、すべてのレーサーのコンポーネントを追加していますコンポーネントはパネル上に描かれた線をカバーします。

racer.setOpaque(false)を使用する方法が1つあります。

もう1つの方法は、paint()メソッドをオーバーライドすることです。 (これは、paintComponent()メソッドでカスタムペインティングを行う一般的なルールの例外です)。チュートリアルのリンクを読んだ場合、このアプローチを使用すると、すべてのRacerコンポーネントがペイントされた後にレースパネルのペインティングが行われることがわかります。

frame.setResizable(false); // prevents window resizing which affects painting 

これは必須ではありません。塗装に影響する唯一の理由は、塗装方法でコンポーネントの状態を変更しているためです。上記の私のコメントを参照してください。

racerImg.paintIcon(this, g, posX, 0); 

一般的に、人は画像を描くためにdrawImage(...)メソッドを使用します。イメージをペイントするだけのアイコンを作成する理由はありません。

racerImg = new ImageIcon("races.png"); 

ファイルを読み取るためにImageIconを使用しないでください。 ImageIO.read(...)を使用して画像を読み取ります。次に、上記のように画像をペイントします。

それぞれのJPanelレースは独自のスレッドで動作します。

これは、レースにさまざまな種類のランダム性をもたらすため、愚かな要求のようです。あなたはすでに画像を動かすためのランダムな距離を生成するロジックを持っているので、なぜこれのために別のスレッドが必要ですか?代わりに、ただ1つのスレッドを持って、すべてのRacesを反復して、上で提案したsetPositionX()メソッドを呼び出す必要があります。それから、すべてのレースが同じ時間に再ペイントされ、ランダムに距離が変更されます。

編集:

がすべてのコードの上に述べた単一の変更で私のために正常に動作します:あなたはそれのコンストラクタでレーサーを透明にする必要が

posX = 0; 
setOpaque(false); 

これは学校の割り当てと思われるので、ルールに従わなければなりませんが、割り当ての問題を本当に理解する必要があります。

この投稿の他の人にも示唆しているように、私はまだもっと良いアプローチは、あなたがペイントできるArrayListオブジェクトまたはRacerオブジェクト(コンポーネントではない)を持つことだと思います。このトピックに関する最後の質問でBallオブジェクトをペイントする実例を与えました。

+0

ありがとうございました。あなたの答えは本当に役に立ちます。私のアニメーションは現在動作していますが、もっと重要なのはなぜそれが以前にはうまくいかなかったのか理解しています。はい、それは学校の割り当てのためです。私の教授はJavaの古いバージョンの習慣に従っているようですので、課題を完了してベストプラクティスを見つけようとすると面白い時が来ました。 – briennakh

関連する問題