2017-11-18 19 views
0

주어진 수의 아이콘이 프레임의 왼쪽에서 시작하여 화면에서 오른쪽으로 경주되는 애니메이션입니다. 각 아이콘은 컨테이너의 단일 열 GridLayout 내의 행을 채우는 자체 JPanel에 그려지고 각 JPanel은 자체 스레드에서 레이스를합니다. Swing Timer가 더 나은 접근 방법 일지라도 쓰레드를 사용해야합니다.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로 그려야합니다. 일련의 스레드가 아닌 단일 스윙 타이머로 애니메이션을 구동하십시오. –

+0

gui JPanel의 그래픽은 추가하고있는 다른 구성 요소로 덮여 있습니다. 그것이 당신이 무언가를 끌지 않는 이유입니다. –

+0

@DontKnowMuchButGettingBetter, 각 Racer의 배경이 투명하면 gui의 줄이 계속 표시되지 않아야합니까? – briennakh

답변

2

컨테이너의 단일 열에서 행을 채 웁니다 GridLayout

먼저 맞춤형 페인팅 방법을 배워야합니다. Custom Painting에있는 Swing 튜토리얼의 섹션을 읽어서 시작하십시오. 두 가지 핵심 포인트 :

  1. 당신은 레이아웃 매니저가 일을 할 수 있도록 구성 요소로 getPreferredSize() 메소드가 선호하는 크기가 오버라이드 (override) 할 필요가있다. 원하는 크기를 지정하지 않으면 크기가 0 일 수 있으므로 페인트 할 것이 없습니다.

  2. 도장 방법은 도장 용입니다. Swing가 컴퍼넌트를 칠하는 시점을 제어 할 수 없기 때문에, 페인트 메서드로 컴퍼넌트의 상태를 변경하지 말아주세요. 따라서 그림을 그릴 위치를 제어하려면 setPositionX(...)과 같은 메서드가 필요합니다. 그런 다음 Thread (또는 Timer)에서이 메서드를 호출하여 위치를 변경하고 구성 요소에서 repaint()을 호출합니다.

는 결승선는 컨테이너에 그린 취득하도록되어 있지만,

그럼 당신은 인종 패널의 상단에 그렇게 사람들을 모든 레이서 구성 요소를 추가

표시되지 않을 구성 요소는 패널에 그려진 선을 덮을 것입니다.

racer.setOpaque(false)을 사용하는 방법이 하나 있습니다.

또 다른 방법은 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은 자체 스레드로 레이스합니다.

그것은 경주에 다른 종류의 무작위성을 가져 오기 때문에 어리석은 요구 사항처럼 보입니다. 이미 이미지를 움직일 수있는 랜덤 거리를 생성하는 로직을 가지고 있습니다. 왜 그런 이유로 별도의 스레드가 필요한가요? 대신 하나의 스레드를 가지고 모든 레이스를 반복하고 위에 제안 된 setPositionX() 메서드를 호출해야합니다. 그런 다음 모든 인종은 무작위 거리 변경을 통해 동시에 다시 칠합니다.

편집 :

말했다 데 모든 하나의 변화와 나를 위해 잘 작동 코드 위 : 당신은 단지 그것의 생성자에서 레이서를 투명하게 만들 필요가

posX = 0; 
setOpaque(false); 

.

이것이 학교 과제로 보이는 것처럼 나는 규칙을 따라야 만하지만 실제로 할당 문제를 이해해야합니다.

이 게시물의 다른 사람들이 제안했듯이, 더 나은 접근 방법은 페인트 할 수있는 ArrayList 또는 Racer 개체 (구성 요소 아님)를 사용하는 것입니다. 이 주제에 대한 마지막 질문에서 Ball 객체를 그리는 작업 예제를 보냈습니다.

+0

정말 고마워. 귀하의 대답은 정말 도움이됩니다. 내 애니메이션은 현재 작동하며 더 중요한 것은 이전에 작동하지 않는 이유를 이해합니다. 네, 학교 임용입니다. 저의 교수는 자바의 오래된 버전에 대한 습관을 따르는 것으로 보입니다. 따라서 과제를 완료하고 모범 사례를 찾아내는 데 흥미로운 시간이었습니다. – briennakh