2014-11-28 3 views
1

저는 Java와 OO 프로그래밍에 익숙하지 않습니다. 여기에 검은 색과 흰색 공이 움직이는 문제가 있습니다. 먼저 출력에서 ​​원하는 프로그램을 설명하겠습니다 : 윈도우에 n 개의 볼 (예 : 6 개의 볼)이 있습니다. 하나는 검정색이고 하나는 흰색이고 각 이동마다 하나의 볼만 움직일 수 있습니다. 이 움직임 화면에으로 표시되어야하며 마지막에는 모든 흰색 공이 한쪽에 있어야하고 모든 검은 색 공이 다른쪽에 있어야합니다. 여기에 여섯 공의 예입니다 : 내가 프로그램을 작성했습니다검은 색과 흰색 공 이동

image

는 그것은 좋은 작업 보인다 알고리즘에는 결함,하지만 내 문제는 내가의 움직임의 애니메이션을 보여주지 수 있다는 것입니다 공, 각 운동에서 하나의 공이 그 이웃 공과 그 자리를 바꾸어야하지만, 나는 공의 최종 배열을 얻는다. 누군가 애니메이션 부분을 도와주세요. 나는 정말로 감사 할 것입니다.

코드 :

import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.geom.Ellipse2D; 
import javax.swing.*; 

public class DrawPanel extends JPanel implements ActionListener 
{ 
Timer myTimer = new Timer(2000, this); 
public static final int NUMBER_OF_CIRCLES = 10; //number of circles which are to moved 
static int[] circles = new int[NUMBER_OF_CIRCLES]; 

public void paintComponent(Graphics g) 
{ 
    int x = 0; //start point of circles; 
    int length = 40; //diagonal of the circles 

    super.paintComponent(g); 
    Graphics2D g2 = (Graphics2D) g; 
    Ellipse2D circle; 

    //painting n circles based on the array 
    for(int index = 0; index<10; index++) 
    { 
     if(circles[index] == 0){ //if the element of the arrayy is 0 then draw a void circle 

      circle = new Ellipse2D.Double(x, 120, length, length); 
      g2.draw(circle); 
     } 
     else if(circles[index] == 1){ //if the element of the array is 1 them draw a filled circle 
      circle = new Ellipse2D.Double(x, 120, length, length); 
      g2.fill(circle); 
     } 
     x += 45; //increas start pont of the next circle 45 pixles 
    } 
    myTimer.start(); 
} 

public void actionPerformed(ActionEvent e) 
{ 
    int tmp; //template for swaping elements 
    int condition; //condition of the forS 

    arrayFill(circles); //fills the array based on the writen method, one 1 and one 0 like: 0 1 0 1 0 1 0 1 

    //here is the part which works good, it changes palces of an elemen at time. 
    //at the end of this part the array would be like: 1 1 1 1 0 0 0 0 
    if(NUMBER_OF_CIRCLES % 2 == 0) 
     condition = circles.length/2 -1; 
    else 
     condition = circles.length/2; 
    for(int i = circles.length-1, k = 1; i>condition; i--, k++) 
    { 
     for(int j = i - k; j<i ;j++) 
     { 
      tmp = circles[j]; 
      circles[j] = circles[j+1]; 
      circles[j+1] = tmp; 
      //if we call arrayPrint method it will print the array but I don't know why repaint is not working here 
      //arrayPrint(circles); 
      repaint(); 
     } 
    } 
} 

//fills the array, one 1 and one 0. Example: 0 1 0 1 0 1 0 1 0 1 
public static void arrayFill(int[] array) 
{ 
    for(int i = 0; i<array.length; i++) 
    { 
     if(i%2 == 0) 
      array[i] = 0; 
     else 
      array[i] = 1; 
    } 
} 

}//end of class 

그리고 주요 클래스 :

import javax.swing.JFrame; 
public class BlackAndWhiteBallsMoving { 

public static void main(String[] args) 
{ 
    DrawPanel myPanel = new DrawPanel(); 
    JFrame myFrame = new JFrame(); 

    myFrame.add(myPanel); 
    myFrame.setSize(600, 500); 
    myFrame.setTitle("Black And White Balls Moving"); 
    myFrame.setVisible(true); 
    myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
} 

}//end of class 
+0

당신은'arrayFill (원)를 호출하고'각 틱, 그래서 당신은 항상 처음부터 다시하고 있습니다. – trashgod

답변

1

Timer에 의해 트리거되는 이벤트는 다시 칠하기와 동일한 이벤트 스레드에서 수행됩니다. repaint을 호출하면 페인트 이벤트가 실행되지 않고 나중에 이벤트 이벤트가 대기합니다. 타이머 이벤트 내에서 다시 칠하는 경우 타이머 이벤트가 완료된 후에 만 ​​다시 실행됩니다.

타이머를 트리거 할 때마다 하나의 스왑 만 수행되도록 루프를 리팩토링하면됩니다.

또한

public class DrawPanel extends JPanel implements ActionListener { 
    public static final int NUMBER_OF_CIRCLES = 10; 

    Timer myTimer = new Timer(500, this); 
    int[] circles = new int[NUMBER_OF_CIRCLES]; 

    public DrawPanel() { 
     arrayFill(circles); 

     if(NUMBER_OF_CIRCLES % 2 == 0) { 
      condition = circles.length/2 -1; 
     } else { 
      condition = circles.length/2; 
     } 

     i = circles.length - 1; 
     k = 1; 

     myTimer.start(); 
    } 

    int i, j, k; 
    int condition; 
    boolean outer = true; 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     if(outer) { 
      if(i > condition) { 
       j = i - k;  // set j 
       outer = false; // and move to the inner loop swap 
      } else { 
       myTimer.stop(); // the outer loop is done so stop the timer 
      } 
     } 
     if(!outer) { 
      int tmp = circles[j]; 
      circles[j] = circles[j+1]; 
      circles[j+1] = tmp; 
      repaint(); 

      j++; 
      if(j >= i) { 
       i--; 
       k++; 
       outer = true; // move to the outer condition 
      }     // next time the timer triggers 
     } 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     int x = 0; 
     int length = 40; 

     super.paintComponent(g); 
     Graphics2D g2 = (Graphics2D) g; 
     Ellipse2D circle; 
     for(int index = 0; index<10; index++) { 
      if(circles[index] == 0){ 
       circle = new Ellipse2D.Double(x, 120, length, length); 
       g2.draw(circle); 
      } else if(circles[index] == 1){ 
       circle = new Ellipse2D.Double(x, 120, length, length); 
       g2.fill(circle); 
      } 
      x += 45; 
     } 
     //myTimer.start(); 
    } 

    public static void arrayFill(int[] array) { 
     for(int i = 0; i<array.length; i++) { 
      if(i%2 == 0) { 
       array[i] = 0; 
      } else { 
       array[i] = 1; 
      } 
     } 
    } 
} 

(나는 그것을 다른 방법을 고려 할 수 있습니다 확신 해요.) : 나는 예를 들어 당신이 한 적이

  • 나는 @Override 주석을 추가하는 당신이해야 용도. 그렇게하면 실수를했을 때 경고합니다. (메서드 이름의 철자를 잘못 쓰거나 서명을 잘못 선언하는 것과 같습니다.)
  • circles은 정적이어야하는 이유가 없기 때문에 인스턴스 변수로 이동했습니다. 이것은 DrawPanel 인스턴스의 상태의 일부입니다.
  • circles과 같은 변수를 초기화하는 생성자를 만들었습니다.
  • paintComponentprotected이며,이를 승격 할 이유가없는 한 그대로 유지해야합니다 (public).

는 튜토리얼 Initial Threads을 읽어야한다, (나는 당신의 의견을 제거하고, 버팀대 스타일의 단지 내 대답에 대한 코드를 응축. 변경) 보조 노트로

합니다. Swing 이벤트 스레드에 GUI를 만들지 않습니다. 기본적으로 당신은 invokeLater에 전화 내부 main에 코드를 포장해야합니다

public static void main(String[] args) { 
    SwingUtilities.invokeLater(new Runnable() { 
     @Override 
     public void run() { 
      // create and show your GUI 
     } 
    }); 
} 
0

기본에 문제가 actionPerformed 방법입니다. 귀하의 두 개의 for 루프는 어레이를 최종 배열로 신속하게 재배치합니다. 각 루프 반복은 완료 할 때까지 나노초에서 밀리 초가 소요됩니다 (repaint() 메서드의 작동 방식에 따라 다름). 전체 프로세스는 50 밀리 초 미만으로 완료됩니다. 너무 빨라 눈을 뗄 수 없습니다.

기본적으로 repaint() 메소드가 작동하지만 인간의 눈이 너무 빨라서 계속 작동하지 않습니다.

for 루프를 호출 할 때마다 알고리즘의 한 단계를 수행하는 루프를 분해하면 타이머에서 트리거하고 사람이 감지 할 수있는 속도로 애니메이션을 볼 수 있습니다.

0

페인트 스레드를 추가하십시오. 항상,

new Thread(){ // this can be started on main or constructor of object 
public void run(){ 
    while(true){ 
     repaint(); 
     try { 
     Thread.sleep(50); 
     } catch(Exception e){ }  
    } 
  } 
    }.start(); 

을 칠하고() 등을 호출하고 작업이 수행에 다음, 마크 movingObjects 같은 객체를 이동하는 animate_x = 0을 유지하고, paintComponent에에 다음

existAnimation

같은 부울 변수를 유지해야한다 증가 animate_x

animate_x = animate_x + 1; 
if (animate_x >= MAX_WIDTH_OF_ANIMATION){ 
    existAnimation = false; 
} 

이 existAnimation, animate_x 및 movingObjects 같은

를 사용

public void paintComponent(Graphics g) 
{ 
    int x = 0; //start point of circles; 
    int length = 40; //diagonal of the circles 

    super.paintComponent(g); 
    Graphics2D g2 = (Graphics2D) g; 
    Ellipse2D circle; 

    //painting n circles based on the array 
    for(int index = 0; index<10; index++) 
    { 

     int paint_x = x; 
     if (movingObjects.has(circles[index])){ 
      paint_x += animate_x; 
     } 
     if(circles[index] == 0){ //if the element of the arrayy is 0 then draw a void circle 

      circle = new Ellipse2D.Double(paint_x, 120, length, length); 
      g2.draw(circle); 
     } 
     else if(circles[index] == 1){ //if the element of the array is 1 them draw a filled circle 
      circle = new Ellipse2D.Double(paint_x, 120, length, length); 
      g2.fill(circle); 
     } 
     x += 45; //increas start pont of the next circle 45 pixles 
    } 
    myTimer.start(); 
} 
관련 문제