swing的JProgressBar进度条_BoundedRangeModel
由于Swing中很多组件的界面与数据都采用了MVC的设计思想:
M指的是: Model即数据模型、封装数据 V指的是: View即视图,用于展示数据的组件 C指的是: Controller即控制器,用于把Model中的数据交给View展示
使用MVC的优点:可以把我们的代码进行分类、分层。我们正在学的JProgressBar对象的内部其实内置了BoundedRangeModel模型对象,由于这个对象是JProgressBar内置的,所以JProgressBar可以直接使用BoundedRangeModel模型对象进度百分比的设置的底层就是由BoundedRangeModel完成的,其实最终还是需要BoundedRangeModel
Swing组件大都将外观显示和内部数据分离,JProgressBar也不例外,JProgressBar组件有一个内置的用于保存其状态数据的Model对象,这个对象由BoundedRangeModel对象表示,程序调用JProgressBar对象的方法完成进度百分比的设置,监听进度条的数据变化,其实都是通过它内置的BoundedRangeModel对象完成的。下面的代码是对之前代码的改进,通过BoundedRangeModel完成数据的设置,获取与监听
swing的JProgressBar进度条_BoundedRangeModel的练习
package ch30;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class a_18_0swing_进度条_JProgressBar_BoundedRangeModel {
//创建窗口
JFrame jf = new JFrame("测试进度条");
//窗口里面的左侧的复选框
JCheckBox indeterminate = new JCheckBox("不确定进度");
JCheckBox noBorder = new JCheckBox("不绘制边框");
//JProgressBar创建一个进度条,JProgressBar参数:HORIZONTAL即水平进度条、最小值、最大值
JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL,0,100);
//获取进度条内置的数据模型对象
BoundedRangeModel model = bar.getModel();//即进度条的所有数据都在这个model里面
//组装视图
public void init() {
//设置不确定进度条
indeterminate.addActionListener(new ActionListener() {
//点击复选框后的事件
public void actionPerformed(ActionEvent e) {
//获取一下indeterminate,即"不确定进度"复选框有没有被选中
boolean selected = indeterminate.isSelected();
//如果返回true,则当前进度条为不确定进度
bar.setIndeterminate(selected);
//绘制进度条
bar.setStringPainted(!selected);
}
});
//设置绘制进度条的边框
noBorder.addActionListener(new ActionListener() {
//点击复选框后的事件
public void actionPerformed(ActionEvent e) {
//获取一下noBorder,即"不绘制边框"复选框有没有被选中
boolean flag = noBorder.isSelected();
//如果上面那行的复选框被选中,即上面那行就是true,我们就不需要绘制进度条。原因:我们的进度条默认是有边框的
bar.setBorderPainted(!flag);
}
});
//垂直方向的BOX容器,用来存放两个复选框
Box box = new Box(BoxLayout.Y_AXIS);
box.add(indeterminate);
box.add(noBorder);
//设置进度条中绘制完成百分比
bar.setStringPainted(true);
//设置为默认是有边框的
bar.setBorderPainted(true);
//FlowLayout流式布局,优点:添加组件会变成从左往右添加,即就是我们要的水平布局。具体操作:把当前窗口jf的布局方式修改为FlowLayout
jf.setLayout(new FlowLayout());
jf.add(box);//即96行我们组装好的容器,里面是两个垂直排列的复选框
jf.add(bar);//把我们的进度条也放进来。注意:这行的bar和上面那行的box会在jf窗口里面水平排列,或理解为左右排列
//设置进度条的最大值和最小值
bar.setMinimum(0);
bar.setMaximum(100);
//设置窗口的最佳大小,设置窗口可见,设置点击叉号就退出程序
jf.pack();
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//开启子线程,模拟耗时操作
a_18_0swing_进度条_JProgressBar_BoundedRangeModel.SimulatedActivity simulatedActivity = new a_18_0swing_进度条_JProgressBar_BoundedRangeModel.SimulatedActivity(bar.getMaximum());
//new一个线程任务,即new一个子线程并开启
new Thread(simulatedActivity).start();//这个子线程任务开启后,最下面的run方法就会开始执行,即current就会被不断修改
//设置定时任务(注意这里是父线程)
Timer timer = new Timer(100, new ActionListener() {
public void actionPerformed(ActionEvent e) {
//读取线程任务对象的当前完成量,设置给进度条
int current = simulatedActivity.getCurrent();//父线程要通过getCurrent拿子线程的current值,就涉及下面那行的内存可见问题
//使用我们获取到的model来实现
model.setValue(current);
}
});
//开启定时任务
timer.start();
//思考:上面那行的定时任务的定时器需要一直执行下去吗,显然不能,解决如下
//监听进度条的变化,如果进度完成为100%,那么停止定时器
model.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
//当子线程的进度条为100%就停止定时器,如何知道子线程的进度条为100%呢,如下
//解决:把bar里面的进度条的最大百分比跟子线程的进度百分比做比较,如果相同,就说明达到100%,停止定时器
int value = model.getValue();
if (value == simulatedActivity.getAmount()) {
timer.stop();
}
}
});
}
public static void main(String[] args) {
new a_18_0swing_进度条_JProgressBar_BoundedRangeModel().init();
}
//自定义一个线程任务类,模拟耗时操作,其实这里就是子线程
private class SimulatedActivity implements Runnable {
//记录任务总量
private int amount;
//记录当前任务的完成量current。注意由于主线程和子线程不是同一线程,所以current变量前面需要加volatile关键字,意思是内存可见
private volatile int current = 0;//初始值是0
//volatile关键字可以让其他线程能拿到这个线程的current值
//由于任务总量是主线程开启时传进来的,我们需要写一个带参构造方法接收一下这个任务总量
public SimulatedActivity(int amount) {
this.amount = amount;
}
//get和set方法
public int getCurrent() {
return current;
}
public void setCurrent(int current) {
this.current = current;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public void run() {
//通过循环不断的修改current的值来实现模拟任务完成量的耗时操作。这个就是子线程的任务
while (current < amount) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
current++;
}
}
}
}
swing的JProgressBar进度条__ProgressMonitor
创建进度对话框 ProgressMonitor的用法与JProgressBar的用法基本相似,只是ProgressMonitor可以直接创建一个进度对话框,它提供了下面的构造器
完成对话框的创建:
xxxxxxxxxx
public ProgressMonitor(Component parentComponent,Object message,String note, int min,int max);
上面那个构造方法的参数如下:
1、parentComponent:对话框的父组件(由于对话框可以独立存在,所以即使不指定父组件也没事,不传的话就写null) 2、message:对话框的描述信息 3、note:对话框的提示信息 4、min:进度条的最小值 5、max:进度条的最大值
使用ProgressMonitor创建的对话框里包含的进度条是非常固定的,程序甚至不能设置该进度条是否包含边框(总是包含边框),不能设置进度,不能改变进度条的方向(总是水平方向)
swing的JProgressBar进度条__ProgressMonitor的练习
xxxxxxxxxx
package ch30;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
//注意这个是不显示具体百分比的
public class a_19_0swing_进度条_JProgressBar_ProgressMonitor {
//放到外面,不然下面的if语句里面的timer会报红线
Timer timer;
public void init() {
//由于ProgressMonitor进度对话框可以独立存在,所以我们就不需要创建Frame窗口啦
//创建进度对话框,ProgressMonitor参数:父组件、对话框的描述信息、对话框的提示信息、进度条的最小值、进度条的最大值
ProgressMonitor monitor = new ProgressMonitor(null, "等待任务完成", "已完成", 0, 100);
//创建线程任务对象,即创建子线程,总量是100,即总进度是100%
final SimulatedActivity simulatedActivity = new SimulatedActivity(100);
//new一个Thread对象,把上面那行的线程任务对象放进来
final Thread targetThread = new Thread(simulatedActivity);
//开启子线程
targetThread.start();
//在主线程里面设置定时任务,每隔200毫秒就去同步一次子线程里面的进度,保证主线程的进度条显示正常
timer = new Timer(200, new ActionListener() {
public void actionPerformed(ActionEvent e) {
//读取子线程的当前任务量
int current = simulatedActivity.getCurrent();
//修改自己主线程的进度
monitor.setProgress(current);
//判断用户是否点击了取消按钮,如果点击了,就停止定时任务,且关闭对话框(由于我们程序的窗口只添加了对话框,所以就相当于退出程序)
if (monitor.isCanceled()) {
//停止定时任务
timer.stop();
//关闭对话框
monitor.close();
//退出程序
System.exit(0);
}
}
});
//开启定时任务,即开启这个主线程
timer.start();
}
public static void main(String[] args) {
new a_19_0swing_进度条_JProgressBar_ProgressMonitor().init();
}
//定义一个线程任务,模拟耗时操作。不懂可去前几节课复习一下,这里就不写太详细啦
private class SimulatedActivity implements Runnable {
//内存可见
private volatile int current = 0;
private int amount;
//由于任务总量是主线程开启时传进来的,我们需要写一个带参构造方法接收一下这个任务总量
public SimulatedActivity(int amount) {
this.amount = amount;
}
//get和set方法
public int getCurrent() {
return current;
}
public void setCurrent(int current) {
this.current = current;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public void run() {
//通过循环,不断的修改current的值,模拟任务完成量
while (current < amount) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
current++;
}
}
}
}