如果排序过程和程序执行能结合起来,那么这个过程会更加直观。
本文给出一个冒泡排序的可视化排序实现, 效果如下图所示:
界面组成
界面很简单就包括两个部分:界面左侧是可视化排序部分,右侧是冒泡排序的代码。
如何实现代码和排序的视觉同步?
关键点
如何在页面上表示出排序程序的运行过程。
如何将排序程序的运行过程和可视化排序结合起来,保持状态一致。
解决方法
在这个例子中,我才用了javax.swing.JList模拟程序的运行。
javax.swing.JList有一个setSelectedIndex的方法,能高亮显示指定的行。
通过改变selectedIndex的值,能够达到模拟冒泡排序程序执行的效果。在这个过程中,记录下两个循环的索引状态值,根据这些状态值去调整可视化排序。
页面展示
初始化页面
程序随机产生10个数字,然后展示在左侧的排序面板中,每个数都用绿色的矩形展示。
private List<NumberRectangle> initialNumberRectangles() {
List<NumberRectangle> list = new ArrayList<NumberRectangle>();
/**
* 随机产生10个数组
*/
Random random = new Random();
for (int i = 1; i <= 10; i++) {
list.add(new NumberRectangle(i, 1, random.nextInt(15) + 1,
Color.GREEN));
}
return list;
}
冒泡程序存放在左侧的javax.swing.JList中。
private static final String[] BUBBLE_SOURCE_CODE = {
"public void bubbleSort(int[] data) { ",
" for (int i = 0; i < data.length - 1; i++) {",
" for (int j = 0; j < data.length - i - 1; j++) {",
" if (data[j] > data[j + 1]) { ",
" int temp = data[j + 1]; ",
" data[j + 1] = data[j]; ",
" data[j] = temp; ",
" } ",
" } ",
" } ",
"} " };
private JList<String> codeList = new JList<String>(BUBBLE_SOURCE_CODE);
运行速度设置
点击菜单栏Set下的Speed可以设置程序执行的速度。
选择不同的速度项,其会修改Timer延迟的时间,从而达到程序速度改变的效果。
private class SpeedAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
Object speed = event.getSource();
if (speed == speedMI1) {
speedFlag = 1;
} else if (speed == speedMI2) {
speedFlag = 2;
} else if (speed == speedMI3) {
speedFlag = 3;
} else if (speed == speedMI4) {
speedFlag = 4;
} else if (speed == speedMI5) {
speedFlag = 5;
}
panel.timer.setDelay(1000 - 200 * (speedFlag - 1));
}
}
程序开始
点击菜单栏Set下的Start选项,程序就开始运行。
程序中间运行过程
![](http://static.oschina.net/uploads/space/2016/1024/205021_cZtT_2911530.png)
排序的主要逻辑主要写在TimeAction中,
该类主要通过java.swing.JList列表的选中的索引的改变,从而决定左侧排序面板的变化,设置不同的颜色。
private class TimerAction implements ActionListener, Serializable {
private static final long serialVersionUID = -8671813189049345697L;
public void actionPerformed(ActionEvent event) {
if (completed) {
return;
}
switch (selectedIndex) {
case 1:
if (outerLoop < 10) {
innerLoop = 0;
codeList.setSelectedIndex(selectedIndex++);
} else {
selectedIndex = 10;
}
break;
case 2:
if (innerLoop < 10 - outerLoop - 1) {
numbers.get(innerLoop).setColor(Color.RED);
numbers.get(innerLoop + 1).setColor(Color.BLUE);
codeList.setSelectedIndex(selectedIndex++);
} else {
outerLoop++;
selectedIndex = 1;
}
break;
case 3:
if (numbers.get(innerLoop).getValue() > numbers.get(
innerLoop + 1).getValue()) {
codeList.setSelectedIndex(selectedIndex++);
} else {
numbers.get(innerLoop + 1).setColor(Color.GREEN);
numbers.get(innerLoop).setColor(Color.GREEN);
innerLoop++;
selectedIndex = 2;
}
break;
case 4:
codeList.setSelectedIndex(selectedIndex++);
break;
case 5:
codeList.setSelectedIndex(selectedIndex++);
break;
case 6:
codeList.setSelectedIndex(selectedIndex);
int tempValue1 = numbers.get(innerLoop).getValue();
int tempValue2 = numbers.get(innerLoop + 1).getValue();
numbers.get(innerLoop + 1).setValue(tempValue1);
numbers.get(innerLoop).setValue(tempValue2);
numbers.get(innerLoop + 1).setColor(Color.GREEN);
numbers.get(innerLoop).setColor(Color.GREEN);
selectedIndex = 2;
innerLoop++;
break;
case 10:
if (selectedIndex == 10) {
completed = true;
codeList.setSelectedIndex(selectedIndex);
}
break;
default:
break;
}
repaint();
}
}
![](http://static.oschina.net/uploads/space/2016/1024/205136_0d5j_2911530.png)
排序完成页面
![](http://static.oschina.net/uploads/space/2016/1024/205327_gdMz_2911530.png)
实现代码
BubbleSortVisualizationFrame.java
package my.visualization.sort.bubble;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;
/**
*
* @author wangmengjun
*
*/
public class BubbleSortVisualizationFrame extends JFrame {
private static final long serialVersionUID = -6725108659717827278L;
private Container contentPane;
/**
* 设置三个Menu Item,分别用于开始程序,调整运行的速度以及退出程序
*
*/
private JMenuItem startMI = new JMenuItem("Start");
private JMenu speedMenu = new JMenu("Speed");
private JMenuItem exitMI = new JMenuItem("Exit");
/**
* 设定5个速度级别
*/
private JRadioButtonMenuItem speedMI1 = new JRadioButtonMenuItem("Speed1",
true);
private JRadioButtonMenuItem speedMI2 = new JRadioButtonMenuItem("Speed2",
false);
private JRadioButtonMenuItem speedMI3 = new JRadioButtonMenuItem("Speed3",
false);
private JRadioButtonMenuItem speedMI4 = new JRadioButtonMenuItem("Speed4",
false);
private JRadioButtonMenuItem speedMI5 = new JRadioButtonMenuItem("Speed5",
false);
public int speedFlag = 1;
/**
* 冒泡排序可视化的Panel
*/
private BubbleSortPanel panel;
public BubbleSortVisualizationFrame(){
setTitle("可视化排序之冒泡排序");
setSize(700, 400);
setResizable(false);
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu setMenu = new JMenu("Set");
setMenu.setMnemonic('s');
menuBar.add(setMenu);
setMenu.add(startMI);
setMenu.addSeparator();
setMenu.addSeparator();
setMenu.add(speedMenu);
setMenu.addSeparator();
setMenu.add(exitMI);
ButtonGroup group = new ButtonGroup();
group.add(speedMI1);
group.add(speedMI2);
group.add(speedMI3);
group.add(speedMI4);
group.add(speedMI5);
speedMenu.add(speedMI1);
speedMenu.add(speedMI2);
speedMenu.add(speedMI3);
speedMenu.add(speedMI4);
speedMenu.add(speedMI5);
startMI.addActionListener(new StartAction());
speedMI1.addActionListener(new SpeedAction());
speedMI2.addActionListener(new SpeedAction());
speedMI3.addActionListener(new SpeedAction());
speedMI4.addActionListener(new SpeedAction());
speedMI5.addActionListener(new SpeedAction());
exitMI.addActionListener(new ExitAction());
contentPane = getContentPane();
panel = new BubbleSortPanel(this);
contentPane.add(panel);
startMI.setEnabled(true);
}
private class StartAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
startMI.setEnabled(false);
panel.timer.start();
}
}
private class ExitAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
System.exit(0);
}
}
private class SpeedAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
Object speed = event.getSource();
if (speed == speedMI1) {
speedFlag = 1;
} else if (speed == speedMI2) {
speedFlag = 2;
} else if (speed == speedMI3) {
speedFlag = 3;
} else if (speed == speedMI4) {
speedFlag = 4;
} else if (speed == speedMI5) {
speedFlag = 5;
}
panel.timer.setDelay(1000 - 200 * (speedFlag - 1));
}
}
}
BubbleSortPanel.java
/**
*
*/
package my.visualization.sort.bubble;
/**
* @author wangmengjun
*
*/
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;
public class BubbleSortPanel extends JPanel {
private static final long serialVersionUID = -9149581857139587792L;
private static final String[] BUBBLE_SOURCE_CODE = {
"public void bubbleSort(int[] data) { ",
" for (int i = 0; i < data.length - 1; i++) {",
" for (int j = 0; j < data.length - i - 1; j++) {",
" if (data[j] > data[j + 1]) { ",
" int temp = data[j + 1]; ",
" data[j + 1] = data[j]; ",
" data[j] = temp; ",
" } ",
" } ",
" } ",
"} " };
private JList<String> codeList = new JList<String>(BUBBLE_SOURCE_CODE);
/**
* 初始化10个数据
*/
private List<NumberRectangle> numbers = initialNumberRectangles();
public TimerAction timerAction;
public Timer timer;
public BubbleSortVisualizationFrame frame;
public BubbleSortPanel(BubbleSortVisualizationFrame frame) {
timerAction = new TimerAction();
timer = new Timer(1000, timerAction);
codeList.setSelectedIndex(1);
JScrollPane scrollPane1 = new JScrollPane(codeList);
this.setLayout(new BorderLayout());
this.add(scrollPane1, BorderLayout.EAST);
this.frame = frame;
}
/**
* 判断排序是否已经结束
*/
private boolean completed = false;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
drawNumberRectangles(g2);
}
private void drawNumberRectangles(Graphics2D g2) {
for (NumberRectangle rectangle : numbers) {
rectangle.draw(g2);
}
}
int outerLoop = 0;
int innerLoop = 0;
int selectedIndex = 1;
private class TimerAction implements ActionListener, Serializable {
private static final long serialVersionUID = -8671813189049345697L;
public void actionPerformed(ActionEvent event) {
if (completed) {
return;
}
switch (selectedIndex) {
case 1:
if (outerLoop < 10) {
innerLoop = 0;
codeList.setSelectedIndex(selectedIndex++);
} else {
selectedIndex = 10;
}
break;
case 2:
if (innerLoop < 10 - outerLoop - 1) {
numbers.get(innerLoop).setColor(Color.RED);
numbers.get(innerLoop + 1).setColor(Color.BLUE);
codeList.setSelectedIndex(selectedIndex++);
} else {
outerLoop++;
selectedIndex = 1;
}
break;
case 3:
if (numbers.get(innerLoop).getValue() > numbers.get(
innerLoop + 1).getValue()) {
codeList.setSelectedIndex(selectedIndex++);
} else {
numbers.get(innerLoop + 1).setColor(Color.GREEN);
numbers.get(innerLoop).setColor(Color.GREEN);
innerLoop++;
selectedIndex = 2;
}
break;
case 4:
codeList.setSelectedIndex(selectedIndex++);
break;
case 5:
codeList.setSelectedIndex(selectedIndex++);
break;
case 6:
codeList.setSelectedIndex(selectedIndex);
int tempValue1 = numbers.get(innerLoop).getValue();
int tempValue2 = numbers.get(innerLoop + 1).getValue();
numbers.get(innerLoop + 1).setValue(tempValue1);
numbers.get(innerLoop).setValue(tempValue2);
numbers.get(innerLoop + 1).setColor(Color.GREEN);
numbers.get(innerLoop).setColor(Color.GREEN);
selectedIndex = 2;
innerLoop++;
break;
case 10:
if (selectedIndex == 10) {
completed = true;
codeList.setSelectedIndex(selectedIndex);
}
break;
default:
break;
}
repaint();
}
}
private List<NumberRectangle> initialNumberRectangles() {
List<NumberRectangle> list = new ArrayList<NumberRectangle>();
/**
* 随机产生10个数组
*/
Random random = new Random();
for (int i = 1; i <= 10; i++) {
list.add(new NumberRectangle(i, 1, random.nextInt(15) + 1,
Color.GREEN));
}
return list;
}
}
NumberRectangle.java
package my.visualization.sort.bubble;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
/**
*
* @author wangmengjun
*
*/
public class NumberRectangle {
private int x;
private int y;
private int value;
private Color color;
public NumberRectangle() {
}
public NumberRectangle(int x, int y, int value, Color color) {
this.x = x;
this.y = y;
this.color = color;
this.value = value;
}
public void draw(Graphics2D g2) {
int clientX = 30 + x * 30;
int clientY = 20 + y * 10;
Rectangle2D.Double rect = new Rectangle2D.Double(clientX, clientY, 20,
value * 20);
g2.setPaint(color);
g2.fill(rect);
g2.setPaint(Color.BLACK);
g2.draw(rect);
g2.drawString(String.valueOf(value), clientX, clientY - 10);
}
/**
* @return the color
*/
public Color getColor() {
return color;
}
/**
* @param color
* the color to set
*/
public void setColor(Color color) {
this.color = color;
}
/**
* @return the x
*/
public int getX() {
return x;
}
/**
* @param x
* the x to set
*/
public void setX(int x) {
this.x = x;
}
/**
* @return the y
*/
public int getY() {
return y;
}
/**
* @param y
* the y to set
*/
public void setY(int y) {
this.y = y;
}
/**
* @return the value
*/
public int getValue() {
return value;
}
/**
* @param value
* the value to set
*/
public void setValue(int value) {
this.value = value;
}
}
BubbleSortApplication.java
package my.visualization.sort.bubble;
import javax.swing.JFrame;
public class BubbleSortApplication {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
BubbleSortVisualizationFrame frame = new BubbleSortVisualizationFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}
某次运行效果
这样,一个简单地,模拟执行结合排序变化的例子就完成了。 :)