9.2 ProgressMonitor类
ProgressMonitor类用来报告需要一段时间完成的任务的状态。这个类是一个特殊的Swing类,他并不是一个GUI组件,也不是一个选项面板或是JavaBean组件。相反,当任务的每一部分完成时,我们通知ProgressMonitor。如果任务需要一段相当长的时间来完成,ProgressMonitor会显示一个类似图9-15所示的弹出窗口。
在ProgressMonitor显示弹出窗口以后,用户可以做下列两件事情。用户可以监视ProgressMontior显示来确认任务已经完成了多少;当任务完成时,ProgressMonitor显示会自动消失。或者,如果用户选择了关闭按钮,这会通知ProgressMonitor任务需要被结束。要检测关闭,任务需要定时查看ProgressMonitor来确认用户是否关闭了任务操作。否则,任务会继续。
ProgressMonitor类显示的弹出窗口是一个maxCharacterPerLineCount属性设置为60的JOptionPane,允许选项面板自动回行所显示的消息。选项面板会嵌入在一个其标题为“Progress...”的非模态JDialog中。为JDialog是非模态的,用户仍然可以与主程序进行交互。ProgressMonitor的JOptionPane总是可以在其图标区域显示一个信息图标。
另外,选项面板的消息区域由下面三个对象组件:
- 在消息区域的顶部是在整个JOptionPane生命周期中保持不变的固定消息。与JOptionPane的message属性类似,这个消息可以是一个文本字符串,或者是一个对象数组。
- 在消息区域的中部是会随着任务过程而变化的注释或是变化消息。
- 在消息区域的底部是一个由已完成任务的增加百分比填充的过程栏(JProgressBar组件)。
选项面板的按钮区域显示一个关闭按钮。
9.2.1 创建ProgressMonitor
当我们创建一个ProgressMonitor时,其构造函数有五个参数:
public ProgressMonitor(Component parentComponent, Object message, String note, int minimum, int maximum)
第一个参数表示当ProgressMonitor需要显示时JOptionPane的父组件。父组件是弹出窗口显示在其上的绷脸的,并且其作用类似于JOptionPane的createDialog()方法中的parentComponent组件。然后我们为JOptionPane的消息区域提供静态或是变化的消息部分。这些消息部分的每一个可以为null,尽管null意味着消息区域的这一部分不会显示。最后,我们需要为过程栏提供minimum与maximum值作为其范围。这两个值之间的区别表示要执行的期望操作数目,例如要载入的文件数或是要读取的文件尺寸。通常,最小设置为零,但是并不做要求。完成操作数决定了过程栏要移动多远。
初始时,弹出窗口并不显示。默认情况下,过程监视器每半分钟(500毫秒)检测一次来确认正在进行的任务是否会在两秒内结束。如果任务已经显示了某些进程,并且他不会在两秒内结束,那么弹出窗口就会显示。结束时间可以通过修改ProgressMonitor的millisToDecideToPopup与millisToPopup属性来配置。
下面的代码演示了一个具有200步操作的ProgressMonitor的创建。应该保存到ProgressMonitor的引用,从而他可以在任务过程中得到通知。
ProgressMonitor monitor = new ProgressMonitor( parent, "Loading Progress", "Getting Started...", 0, 200);
9.2.2 使用PropressMonitor
一旦我们创建了ProgressMonitor,我们需要启动其过程已经被监视的任务。当任务完成了一步或是多步时,ProgressMonitor需要得到任务进程的通知。通知是通过public void setProgress(int newValue)方法调用来实现的,其中参数表示现在已经完成的进程,而newValue需要位于初始指定的minimum...maximum范围之间。这个进程值需要在ProgressMonitor之外进行维护,因为我们不能向监视器询问进程已经完成了多少(ProgressMonitor并没有public int getProgress()方法)。如果进程值在一个名为progress的变化中进行维护,下面两行代码可以更新进程并且通知ProgressMonitor。
progress += 5; monitor.setProgress(progress);
progress设置表示到目前为止已经载入的文件数,或者是由文本读取的字节数。除了更新计数,我们还应该更新note来反映进程。如果ProgressMonitor构造函数之间中所用的minimum与maximum参数之间的差值为100,那么当前的进程可以被看作是当前任务的百分比。否则,progress属性仅表示到目前为止已经完成的进程。
monitor.setNote("Loaded " + progress + " files");
执行任务代码要负责检测用户是否按下了ProgressMonitor对话框中的Cancel按钮。如果任务被关闭,ProgressMonitor会自动关闭对话框,但是任务必须在代码中的合适位置添加一个简单的检测来主动检测变化:
if (monitor.isCanceled()) { // Task canceled - cleanup ... } else { // Continue doing task ... }
大多数的任务要求ProgressMonitor使用单独的线程来实现,从而避免阻塞主程序的响应。
列表9-5显示了一个创建ProgressMonitor并且允许我们手动或自动增加其progress属性的程序。这些任务是由屏幕上的按钮来处理的(如图9-16)。选择Start按钮创建ProgressMonitor。选择Manual Increase按钮可以使得进程增加5.选择Automatic Increase按钮可以使得进程每250毫秒增加5.在自动增加的过程中按下弹出窗口的Cancel按钮演示了操作被关闭时发生的情况;计时器停止发送更新。
列表9-5中开始处的ProgressMonitorHandler内联类对于保证仅由事件线程访问ProgressMonitor是必须。否则,在某些随机的线程内,访问将不是线程安全的。
package swingstudy.ch09; import java.awt.Component; import java.awt.EventQueue; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.ProgressMonitor; import javax.swing.Timer; public class SampleProgress { static ProgressMonitor monitor; static int progress; static Timer timer; static class ProgressMonitorHandler implements ActionListener { // Called by Timer public void actionPerformed(ActionEvent event) { if(monitor == null) { return ; } if(monitor.isCanceled()) { System.out.println("Monitor canceled"); timer.stop(); } else { progress += 3; monitor.setProgress(progress); monitor.setNote("Load "+progress+" files"); } } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("ProgressMonitor Sample"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new GridLayout(0,1)); // define start button JButton startButton = new JButton("Start"); ActionListener startActionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { Component parent = (Component)event.getSource(); monitor = new ProgressMonitor(parent, "Loading Progress", "Getting Started...", 0, 200); progress = 0; } }; startButton.addActionListener(startActionListener); frame.add(startButton); // define manual increase button // pressing this button increases progress by 5 JButton increaseButton = new JButton("Manual Increase"); ActionListener increaseActionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { if(monitor == null) return ; if(monitor.isCanceled()) { System.out.println("Monitor cancled"); } else { progress += 5; monitor.setProgress(progress); monitor.setNote("Loaded "+progress+" files"); } } }; increaseButton.addActionListener(increaseActionListener); frame.add(increaseButton); // define automatic increase button // start timer to increase progress by 3 every 250 ms JButton autoIncreaseButton = new JButton("Automatic Increase"); ActionListener autoIncreaseActionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { if(monitor != null) { if(timer == null) { timer = new Timer(250, new ProgressMonitorHandler()); } timer.start(); } } }; autoIncreaseButton.addActionListener(autoIncreaseActionListener); frame.add(autoIncreaseButton); frame.setSize(300, 200); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }
9.2.3 ProgressMonitor属性
表9-6显示了ProgressMonitor的八个属性。
millisToDecideToPoppup属性表示监视器在决定是否需要显示弹出窗口前要等待的毫秒数。如果progress属性还没有变化,则监视器会在再一次检测之前等待另一个时间间隔。当ProgressMonitor检测并且发现progress属性已经变化时,他会估计任务是否会在millisToPopup属性的毫秒数之内完成。如果ProgressMonitor认为所的任务会及时完成,则不会显示弹出窗口。否则,弹出窗口会在任务开始时刻的millisToPopup毫秒之后显示。
9.2.4 自定义ProgressMonitor观感
修改ProgressMonitor的外观需要修改JProgressBar以及JLabel的外观,以及ProgressMonitor所用的JOptionPane。
ProgressMonitor只有一个UIResource相关的属性:
- String类型的ProgressMonitor.progressText