最近我注意到有关使用循环算法的代码动画的问题。例如:
现有代码有效,但仅显示最终结果。因此,人们希望为算法的每个步骤设置动画。问一个问题是因为:
在两种情况下,建议均使用Swing Timer,并且该问题被重复进行。但这容易吗?
下面的代码演示了一种简单的冒泡排序算法。排序逻辑很简单,并且只包含在一个方法中,可以指示动画应在何处发生。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;
public class BubbleSort extends JPanel
{
private final static int BAR_WIDTH = 30;
private final static int BAR_HEIGHT_MAX = 400;
private int[]items;
public BubbleSort(int[] items)
{
this.items = items;
}
public void setItems(int[] items)
{
this.items = items;
repaint();
}
public void sort()
{
int n = items.length;
int temp = 0;
for (int i = 0; i < n; i++)
{
for (int j = 1; j < (n - i); j++)
{
if (items[j-1] > items[j])
{
temp = items[j - 1];
items[j - 1] = items[j];
items[j] = temp;
// paint current state for animation
repaint();
try { Thread.sleep(100); } catch (Exception e) {}
}
}
}
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
for (int i = 0; i < items.length; i++)
{
int x = i * BAR_WIDTH;
int y = getHeight() - items[i];
g.setColor( Color.RED );
g.fillRect(x, y, BAR_WIDTH, items[i]);
g.setColor( Color.BLUE );
g.drawString("" + items[i], x, y);
}
}
@Override
public Dimension getPreferredSize()
{
return new Dimension(items.length * BAR_WIDTH, BAR_HEIGHT_MAX + 20);
}
public static int[]generateRandomNumbers()
{
int[] items = new int[10];
for(int i = 0; i < items.length; i++)
{
items[i] = (int)(Math.random() * BubbleSort.BAR_HEIGHT_MAX);
}
return items;
}
private static void createAndShowGUI()
{
BubbleSort bubbleSort = new BubbleSort( BubbleSort.generateRandomNumbers() );
JButton generate = new JButton("Generate Data");
generate.addActionListener((e) -> bubbleSort.setItems( BubbleSort.generateRandomNumbers() ) );
JButton sort = new JButton("Sort Data");
sort.addActionListener((e) -> bubbleSort.sort());
JPanel bottom = new JPanel();
bottom.add( generate );
bottom.add( sort );
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(bubbleSort, BorderLayout.CENTER);
frame.add(bottom, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater( () -> createAndShowGUI() );
}
}
问题是:
答案 0 :(得分:0)
Swing Timer对事件驱动的代码起作用。因此,迭代代码必须重构为事件驱动代码。
在此过程中需要考虑的一些事情:
状态必须添加到算法中。这意味着必须将所有用于控制循环算法的局部变量转换为该类的实例变量。
循环算法必须分为两种方法,一种是设置算法的初始状态并启动计时器,另一种是更新状态并停止计时器。
使用以上建议,代码重构可能类似于:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BubbleSortTimer extends JPanel
{
private final static int BAR_WIDTH = 30;
private final static int BAR_HEIGHT_MAX = 400;
private int[]items;
private int i, j, n;
private Timer timer;
public BubbleSortTimer(int[] items)
{
this.items = items;
timer = new Timer(100, (e) -> nextIteration());
}
public void setItems(int[] items)
{
this.items = items;
repaint();
}
public void sort()
{
i = 0;
j = 1;
n = items.length;
timer.start();
}
public void nextIteration()
{
if (items[j - 1] > items[j])
{
int temp = items[j - 1];
items[j - 1] = items[j];
items[j] = temp;
repaint();
}
j++;
if (j >= n - i)
{
j = 1;
i++;
}
if (i == n)
timer.stop();
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
for (int i = 0; i < items.length; i++)
{
int x = i * BAR_WIDTH;
int y = getHeight() - items[i];
g.setColor( Color.RED );
g.fillRect(x, y, BAR_WIDTH, items[i]);
g.setColor( Color.BLUE );
g.drawString("" + items[i], x, y);
}
}
@Override
public Dimension getPreferredSize()
{
return new Dimension(items.length * BAR_WIDTH, BAR_HEIGHT_MAX + 20);
}
public static int[]generateRandomNumbers()
{
int[] items = new int[10];
for(int i = 0; i < items.length; i++)
{
items[i] = (int)(Math.random() * BubbleSortTimer.BAR_HEIGHT_MAX);
}
return items;
}
private static void createAndShowGUI()
{
BubbleSortTimer bubbleSort = new BubbleSortTimer( BubbleSortTimer.generateRandomNumbers() );
JButton generate = new JButton("Generate Data");
generate.addActionListener((e) -> bubbleSort.setItems( BubbleSortTimer.generateRandomNumbers() ) );
JButton sort = new JButton("Sort Data");
sort.addActionListener((e) -> bubbleSort.sort());
JPanel bottom = new JPanel();
bottom.add( generate );
bottom.add( sort );
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(bubbleSort, BorderLayout.CENTER);
frame.add(bottom, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater( () -> createAndShowGUI() );
}
}
答案 1 :(得分:0)
另一种方法可能是使用SwingWorker。
SwingWorker在其自己的线程中创建运行,并允许您“发布”要绘制的间歇结果。
以下方法的关键是将数据副本传递到SwingWorker。这样一来,工作人员可以在重新绘制数据时对数据进行排序,因此您不必担心数组中的数据处于不一致状态。
每次循环代码迭代后,都会更新数组的新状态,以便可以对其进行绘制。
这使您可以轻松地将排序逻辑移至SwingWorker中,而无需重构。
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import java.util.List;
import javax.swing.*;
import javax.swing.Timer;
public class BubbleSortWorker extends JPanel
{
private final static int BAR_WIDTH = 30;
private final static int BAR_HEIGHT_MAX = 400;
private int[]items;
public BubbleSortWorker(int[] items)
{
this.items = items;
}
public void setItems(int[] items)
{
this.items = items;
repaint();
}
public void sort()
{
new SortWorker(items).execute();
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
for (int i = 0; i < items.length; i++)
{
int x = i * BAR_WIDTH;
int y = getHeight() - items[i];
g.setColor( Color.RED );
g.fillRect(x, y, BAR_WIDTH, items[i]);
g.setColor( Color.BLUE );
g.drawString("" + items[i], x, y);
}
}
@Override
public Dimension getPreferredSize()
{
return new Dimension(items.length * BAR_WIDTH, BAR_HEIGHT_MAX + 20);
}
class SortWorker extends SwingWorker<Void, int[]>
{
private int[] items;
public SortWorker(int[] unsortedItems)
{
items = Arrays.copyOf(unsortedItems, unsortedItems.length);
}
@Override
protected Void doInBackground()
{
int n = items.length;
int temp = 0;
for (int i = 0; i < n; i++)
{
for (int j = 1; j < (n - i); j++)
{
if (items[j-1] > items[j])
{
temp = items[j - 1];
items[j - 1] = items[j];
items[j] = temp;
//repaint();
publish( Arrays.copyOf(items, items.length) );
try { Thread.sleep(100); } catch (Exception e) {}
}
}
}
return null;
}
@Override
protected void process(List<int[]> list)
{
int[] items = list.get(list.size() - 1);
setItems( items );
}
@Override
protected void done() {}
}
public static int[]generateRandomNumbers()
{
int[] items = new int[10];
for(int i = 0; i < items.length; i++)
{
items[i] = (int)(Math.random() * BubbleSortWorker.BAR_HEIGHT_MAX);
}
return items;
}
private static void createAndShowGUI()
{
BubbleSortWorker bubbleSort = new BubbleSortWorker( BubbleSortWorker.generateRandomNumbers() );
JButton generate = new JButton("Generate Data");
generate.addActionListener((e) -> bubbleSort.setItems( BubbleSortWorker.generateRandomNumbers() ) );
JButton sort = new JButton("Sort Data");
sort.addActionListener((e) -> bubbleSort.sort());
JPanel bottom = new JPanel();
bottom.add( generate );
bottom.add( sort );
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(bubbleSort, BorderLayout.CENTER);
frame.add(bottom, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater( () -> createAndShowGUI() );
}
}