12.6 使用JProgressBar、 ProgressMonitor和BoundedRangeModel创建进度条
进度条是图形界面中广泛使用的GUI组件,当复制一个较大的文件时,操作系统会显示一个进度条,用于标识复制操作完成的比例;当启动Eclipse等程序时,因为需要加载较多的资源,故而启动速度较慢,程序也会在启动过程中显示一个进度条,用以表示该软件启动完成的比例。
12.6.1 创建JProgressBar进度条
使用JProgressBar可以非常方便地创建进度条。
使用JProgressBar创建进度条的步骤
创建JProgressBard对象
创建一个JProgressBar对象,创建该对象时可以指定三个参数,用于设置进度条的排列方向(竖直和水平)、进度条的最大值和最小值。也可以在创建该对象时不传入任何参数,而是在后面程序中修改这三个属性。
例如,如下代码创建了JProgressBar对象
| 12
 
 | JProgressBar bar = new JProgressBar(JProgressBar.VERTICAL);
 
 | 
设置进度条的普通属性
调用该对象的常用方法设置进度条的普通属性。JProgressBar除提供设置排列方向、最大值、最小值的setter和getter方法之外,还提供了如下三个方法
| 方法 | 描述 | 
| void setBorderPainted(boolean b) | 设置该进度条是否使用边框 | 
| void setIndeterminate(boolean newValue) | 设置该进度条是否是进度不确定的进度条,如果指定一个进度条的进度不确定,将看到一个滑块在进度条中左右移动。 | 
| void setStringPainted(boolean b) | 设置是否在进度条中显示完成百分比。 | 
当然,JProgressBar也为上面三个属性提供了getter方法,但这三个getter方法通常没有太大作用。
当程序中工作进度改变时,调用JProgressBar对象的setValue()方法。当进度条的完成进度发生改变时,程序还可以调用进度条对象的如下两个方法。
| 方法 | 描述 | 
| double getPercentComplete() | 返回进度条的完成百分比 | 
| String getString() | 返回进度字符串的当前值 | 
程序 简单进度条
下面程序示范了使用进度条的简单例子
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 
 | import java.awt.*;import javax.swing.*;
 
 public class JProgressBarTest {
 JFrame frame = new JFrame("测试进度条");
 
 JProgressBar bar = new JProgressBar(JProgressBar.VERTICAL);
 JCheckBox indeterminate = new JCheckBox("不确定进度");
 JCheckBox noBorder = new JCheckBox("不绘制边框");
 
 public void init() {
 Box box = new Box(BoxLayout.Y_AXIS);
 box.add(indeterminate);
 box.add(noBorder);
 frame.setLayout(new FlowLayout());
 frame.add(box);
 
 frame.add(bar);
 
 bar.setMinimum(0);
 bar.setMaximum(100);
 
 bar.setStringPainted(true);
 
 noBorder.addActionListener(event -> bar.setBorderPainted(!noBorder.isSelected()));
 indeterminate.addActionListener(event -> {
 
 bar.setIndeterminate(indeterminate.isSelected());
 bar.setStringPainted(!indeterminate.isSelected());
 });
 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 frame.pack();
 frame.setVisible(true);
 
 for (int i = 0; i <= 100; i++) {
 
 bar.setValue(i);
 try {
 
 Thread.sleep(100);
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 }
 
 public static void main(String[] args) {
 new JProgressBarTest().init();
 }
 }
 
 | 
上面程序中创建了一个垂直进度条,并通过方法来设置进度条的外观形式:
并通过一个循环来不断改变进度条的value属性,该value将会自动转换成进度条的完成百分比
运行该程序,将看到如图12.29所示的效果
![这里有一张图片]()
在上面程序中,在主程序中使用循环来改变进度条的value属性,即修改进度条的完成百分比,这是没有任何意义的事情。
通常会希望用进度条去检测其他任务的完成情况,而不是在其他任务的执行过程中主动修改进度条的value属性,因为其他任务可能根本不知道进度条的存在。此时可以使用一个计时器来不断取得目标任务的完成情况,并根据其完成情况来修改进度条的value属性。
程序 进度条检测完成度
下面程序改写了上面程序,用一个SimulatedTarget来模拟一个耗时的任务。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 
 | import java.awt.*;import javax.swing.*;
 
 public class JProgressBarTest2 {
 JFrame frame = new JFrame("测试进度条");
 
 JProgressBar bar = new JProgressBar(JProgressBar.VERTICAL);
 JCheckBox indeterminate = new JCheckBox("不确定进度");
 JCheckBox noBorder = new JCheckBox("不绘制边框");
 
 public void init() {
 Box box = new Box(BoxLayout.Y_AXIS);
 box.add(indeterminate);
 box.add(noBorder);
 frame.setLayout(new FlowLayout());
 frame.add(box);
 
 frame.add(bar);
 
 bar.setStringPainted(true);
 
 noBorder.addActionListener(event -> bar.setBorderPainted(!noBorder.isSelected()));
 final SimulatedActivity target = new SimulatedActivity(1000);
 
 new Thread(target).start();
 
 bar.setMinimum(0);
 
 bar.setMaximum(target.getAmount());
 
 Timer timer = new Timer(300, e -> bar.setValue(target.getCurrent()));
 timer.start();
 indeterminate.addActionListener(event -> {
 
 bar.setIndeterminate(indeterminate.isSelected());
 bar.setStringPainted(!indeterminate.isSelected());
 });
 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 frame.pack();
 frame.setVisible(true);
 }
 
 public static void main(String[] args) {
 new JProgressBarTest2().init();
 }
 }
 
 
 class SimulatedActivity implements Runnable {
 
 private volatile int current;
 
 private int amount;
 
 public SimulatedActivity(int amount) {
 current = 0;
 this.amount = amount;
 }
 
 public int getAmount() {
 return amount;
 }
 
 public int getCurrent() {
 return current;
 }
 
 
 public void run() {
 while (current < amount) {
 try {
 Thread.sleep(50);
 } catch (InterruptedException e) {
 }
 current++;
 }
 }
 }
 
 | 
上面程序的运行效果与前一个程序的运行效果大致相同,但这个程序中的JProgressBars就实用多了,它可以检测并显示SimulatedTarget的完成进度。
SimulatedActivity类实现了Runnable接口,这是一个特殊的接口,实现该接口可以实现多线程功能。
BoundedRangeModel
Swing组件大都将外观显示和内部数据分离,JProgressBar也不例外,JProgressBar组件有一个用于保存其状态数据的Model对象,这个对象由BoundedRangeModel对象表示,程序调用JProgressBar对象的setValue方法时,实际上是设置BoundedRangeModel对象的value属性。
程序可以修改BoundedRangeModel对象的minimum属性和maximum属性,当该Model对象的这两个属性被修改后,它所对应的JProgressBar对象的这两个属性也会随之修改,因为JProgressBar对象的所有状态数据都是保存在该Model对象中的。
程序监听JProgressBar完成比例的变化,也是通过为BoundedRangeModel提供监听器来实现的BoundedRangeModel提供了如下一个方法来添加监听器。
| 方法 | 描述 | 
| void addChangeListener(ChangeListener x) | 用于监听 JProgressBar完成比例的变化,每当JProgressBar的value属性被改变时,系统都会触发ChangeListener监听器的stateChanged()方法。 | 
代码 进度条状态变化监听器
下面代码为进度条的状态变化添加了一个监听器
| 12
 3
 4
 
 | bar.getModel().addChangeListener(ce -> {
 
 });
 
 |