12.6 创建进度条 12.6.1 创建JProgressBar进度条

12.6 使用JProgressBar、 ProgressMonitor和BoundedRangeModel创建进度条

进度条是图形界面中广泛使用的GUI组件,当复制一个较大的文件时,操作系统会显示一个进度条,用于标识复制操作完成的比例;当启动Eclipse等程序时,因为需要加载较多的资源,故而启动速度较慢,程序也会在启动过程中显示一个进度条,用以表示该软件启动完成的比例。

12.6.1 创建JProgressBar进度条

使用JProgressBar可以非常方便地创建进度条。

使用JProgressBar创建进度条的步骤

创建JProgressBard对象

创建一个JProgressBar对象,创建该对象时可以指定三个参数,用于设置进度条的排列方向(竖直和水平)、进度条的最大值最小值。也可以在创建该对象时不传入任何参数,而是在后面程序中修改这三个属性。

例如,如下代码创建了JProgressBar对象

1
2
//创建一条垂直进度条
JProgressBar bar = new JProgressBar(JProgressBar.VERTICAL);

设置进度条的普通属性

调用该对象的常用方法设置进度条的普通属性JProgressBar除提供设置排列方向、最大值、最小值的settergetter方法之外,还提供了如下三个方法

方法 描述
void setBorderPainted(boolean b) 设置该进度条是否使用边框
void setIndeterminate(boolean newValue) 设置该进度条是否是进度不确定的进度条,如果指定一个进度条的进度不确定,将看到一个滑块在进度条中左右移动。
void setStringPainted(boolean b) 设置是否在进度条中显示完成百分比。

当然,JProgressBar也为上面三个属性提供了getter方法,但这三个getter方法通常没有太大作用。

当程序中工作进度改变时,调用JProgressBar对象的setValue()方法。当进度条的完成进度发生改变时,程序还可以调用进度条对象的如下两个方法。

方法 描述
double getPercentComplete() 返回进度条的完成百分比
String getString() 返回进度字符串的当前值

程序 简单进度条

下面程序示范了使用进度条的简单例子

1
2
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);
// 把进度条添加到JFrame窗口中
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 {
// 程序暂停0.1秒
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来模拟一个耗时的任务。

1
2
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);
// 把进度条添加到JFrame窗口中
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());
// 以任务的当前完成量设置进度条的value
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;
}

// run方法代表不断完成任务的过程
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完成比例的变化,每当JProgressBarvalue属性被改变时,系统都会触发ChangeListener监听器的stateChanged()方法。

代码 进度条状态变化监听器

下面代码为进度条的状态变化添加了一个监听器

1
2
3
4
// JProgressBar的完成比例发生变化时会触发该方法
bar.getModel().addChangeListener(ce -> {
//对进度变化进行合适处理
});