【问题标题】:Moving Black and White Balls移动黑白球
【发布时间】:2014-11-28 16:57:16
【问题描述】:

我是 Java 和 OO 编程的新手,这里是移动黑白球问题的代码。首先让我解释一下我想要输出的程序:窗口上有 n 个球(例如 6 个球),一个黑色和一个白色,在每次移动中我们只允许移动一个球并且 这个动作应该显示在屏幕上,最后所有的白球都应该在一边,所有的黑球都应该在另一边。以下是六个球的示例:

我已经编写了程序,它看起来运行良好,算法没有缺陷,但我的问题是我无法显示球运动的动画,在每个运动中,一个球应该与它的邻居交换位置球,但我得到的只是球的最终安排。请有人帮我处理动画部分。我会非常感谢。

代码:

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

【问题讨论】:

  • 您每次都在调用arrayFill(circles),所以您总是从头开始。

标签: java swing


【解决方案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
  • paintComponent 是一个 protected 方法,除非有理由将其提升为 public,否则它应该保持不变。

(我删除了您的 cmets 并更改了支撑样式,只是为了浓缩我的答案的代码。)

附带说明,您应该阅读教程Initial Threads。您没有在 Swing 事件线程上创建 GUI。基本上你需要在main 调用invokeLater 中包装你的代码:

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

【讨论】:

    【解决方案2】:

    基本问题在于您的actionPerformed 方法。您的两个for 循环正在快速将阵列重新排列为其最终排列。每次循环迭代将需要几纳秒到几毫秒才能完成(这取决于 repaint() 方法的工作方式)。整个过程在不到 50 毫秒左右的时间内完成。这对你的眼睛来说太快了。

    基本上,repaint() 方法是有效的,但它的工作速度太快,人眼跟不上。

    如果您将 for 循环分解为每次调用它时执行算法的一个步骤的东西,您可以从计时器触发它并以人类可检测的速度查看动画。

    【讨论】:

      【解决方案3】:

      添加油漆线。它应该总是像调用 repaint() 一样,

      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并保持布尔变量,如existAnimation

      然后在paintComponent上,增加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();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-11
        • 1970-01-01
        • 2013-07-18
        • 1970-01-01
        • 2016-02-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-11
        相关资源
        最近更新 更多