/*Explorer*/
import javax.swing.SwingUtilities;
public class Explorer {//资源管理器
public static void main(String args[]){
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new MainFrame();//创建主机
}
});//等SwingUtilities.invokeLater()图形用户接口全部实现后,才能调用匿名内部类的线程,从而实现异步执行
}
}
/*MainFrame*/
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class MainFrame extends JFrame {//MainFrame继承JFrame窗体
private MainPanel panel;//创建框架中的组件对象
public MainFrame() {
super("Explorer");//调用父类JFrame中的JFrame(String title)构造器,设置窗体框架的标题
create();//创建窗体的布局和结构内容
}
private void create() {
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e){//窗口的关闭按钮被点击时调用的方法
panel.onFinished();//调用MainPanel的onFinished()方法
System.exit(0);//退出系统s
}
});//在窗口添加一个Windows事件消息,目的是我们关闭窗口的时候可以正常的退出,即当你的框架关闭时窗口能关闭。
this.panel = new MainPanel();//创建一个面板对象,无参构造中create()方法实现面板的组件创造
this.getContentPane().add(this.panel,BorderLayout.CENTER);//把刚刚创建的面板添加到主窗体的内容窗格中央
//getContentPane():返回此框架的 contentPane对象。用getContentPane()方法获得JFrame的内容面板,再对其加入组件
this.setLocationRelativeTo(null);//可写在this.pack()的下一行
//设置窗口相对于指定组件的位置。如果组件当前未显示或者 c 为 null,则此窗口将置于屏幕的中央。
this.setPreferredSize(new Dimension(800,600));//设置参照的窗体首选大小,自动调节比例大小*会考*
this.pack();//告诉布局管理器,按照窗体首选大小进行内部布局调整
this.setVisible(true);//让窗体显示
}
}
/*MainPanel*/
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.io.File;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class MainPanel extends JPanel{
private final FileQueue queue;
private Thread scanner;//扫描线程
private Thread render;//处理线程
private JButton start;//启动按钮
private JButton stop;//停止按钮(未完善)
private JTextField where;//文本字段,单行
private JTextArea logger;//文本区域。多行
public MainPanel() {
super();//一定要调用这个super()方法
create();
queue = new FileQueue();
}
public void create() {
//框体网格布局设置
this.setLayout(new GridBagLayout());//设置网格包布局管理器
//设置单行文本字段
where = new JTextField();
GridBagConstraints gbc = new GridBagConstraints();//如何放置,是否扩展,即定制某些组件的相关约束对象,可重复使用
gbc.gridx = 0;//网格第一行
gbc.gridy = 0;//网格第一列
gbc.weightx = 1;//权重是1,可随意设置,十分灵活
gbc.fill = GridBagConstraints.HORIZONTAL;//将第一行第一列水平拉伸,往右挤。组件横向充满显示区域,但是不改变组件高度
this.add(where,gbc);//按照指定布局限制添加组件。
//设置文件目录浏览按钮
JButton browse = new JButton("...");
browse.addActionListener(new ActionListener() {//实现addActionListener里的参数接口ActionListener,重写ActionListener接口的actionPerformed方法
//如果发生了点击事件,系统将会回调actionPerformed方法
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();//创建文件选择器
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);//设置选择模式,只能选择文件夹
if(fc.showOpenDialog(browse) == JFileChooser.APPROVE_OPTION) {//弹出的文件选择框尽量靠近按钮,且当点击了确定或保存时
File file = fc.getSelectedFile();//得到选择的目标文件信息
where.setText(file.getAbsolutePath());//让文本目录的绝对路径显示在单行文本字段
}
}
});
gbc.gridx = 1;
gbc.weightx = 0;//不再拉伸
gbc.fill = GridBagConstraints.NONE;//默认值
this.add(browse,gbc);//把按钮添加到面板中
//设置Start按钮
start = new JButton("Start");
start.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
logger.selectAll();//选择文本区域的所有行
logger.replaceSelection("");//清空文本区域
render = new Thread(new Render(queue,MainPanel.this));
render.start();
scanner = new Thread(new Scanner(queue,new File(where.getText())));
scanner.start();
}
});
gbc.gridx = 0;
gbc.gridy = 1;
gbc.anchor = GridBagConstraints.WEST;锚点,放置在最左边
this.add(start,gbc);
//设置Stop按钮
stop = new JButton("Stop");
gbc.gridx = 1;
this.add(stop,gbc);
//设置logger文本框
logger = new JTextArea(10,20);
logger.setEditable(false);//禁止编辑文本区域
gbc.gridx = 0;
gbc.gridy = 2;
gbc.gridwidth = 2;//横跨2列
gbc.weightx = 1;//水平拉伸
gbc.weighty = 1;//垂直拉伸
gbc.fill = GridBagConstraints.BOTH;//充满当前窗体剩下的所有区域
this.add(new JScrollPane(logger),gbc);//使文本框可滚动浏览
}
public void onFilePair(FilePair pair) {//记录文本框要输出的父子对文件目录信息
logger.append(pair.getParent().getAbsolutePath() + " : " + pair.getChild().getAbsolutePath() + "\n");
}
public void onFinished() {//结束线程
this.render = null;
this.scanner = null;
}
}
/*FileQueue*/
import java.util.LinkedList;
import java.util.List;
public class FileQueue {//文件缓冲队列
public static final Object RENDER = new Object();//作为Render类中的锁
public static final Object SCANNER = new Object();//作为Scanner类中的锁
private static final int LIMIT = 3;//共享队列queue的最大容量限制
private final List<FilePair> queue;//共享数据列表queue,存放FilePair父子对
private final Object locker;//队列对应的锁,控制读写
public FileQueue() {
this.queue = new LinkedList<>();//创建队列
this.locker = new Object();//new一个锁对象
}
public boolean addPair(FilePair pair) {//写的操作
//同步
/*
* 对这个对象上锁,控制单一进程,执行代码块,如果共享队列queue存放FilePair对象个数大于2个就返回false,反之就将FilePair对象添加到队列中
* */
synchronized (this.locker) {
if(this.queue.size() >= LIMIT) {
return false;
}
this.queue.add(pair);
return true;
}
}
/*
* 对这个对象上锁,控制单一进程,执行代码块,如果共享队列queue空就返回null,反之就可以读取队列中的FilePair数据对象并出队
* */
public FilePair getPair() {//读的操作
synchronized (this.locker) {
if(this.queue.isEmpty()) {
return null;
}
return this.queue.remove(0);//实现队列的队首数据的读取并出队
}
}
}
/*FilePair*/
import java.io.File;
public class FilePair {
private final File parent;//文本字段选择器界面中选择到的初始目录
private final File child;//根据初始目录扫描到的子目录
public FilePair(File parent, File child) {//构造方法
this.parent = parent;
this.child = child;
}
//访问方法只设置读取
public File getParent() {
return this.parent;
}
public File getChild() {
return this.child;
}
}
/*Scanner*/
import java.io.File;
public class Scanner implements Runnable{//写者
private final FileQueue queue;//队列的引用
private final File where;//扫描的起始目录的引用
public Scanner(FileQueue queue, File where) {
this.queue = queue;
this.where = where;
}
@Override
public void run() {//线程的入口点
boolean running = true;
File[] files = where.listFiles();//返回子目录的文件数组
for(File file : files){
FilePair pair = new FilePair(where,file);//每个子目录与父目录创建父子对
running = queue(pair);//将父子对放入输出队列中,若队列满了则返回true,break退出for循环,否则继续for循环
if(!running) {
break;
}
}
FilePair pair = new FilePair(where,where);
queue(pair);//为Reander类中run()方法的if条件语句判断做准备,使Scanner线程结束
}
private boolean queue(FilePair pair) {//读/写转换判断
while(true) {
if(this.queue.addPair(pair)) {//将FileQueue类中的addPair方法,将父子对放入输出队列中,如果队列满了
synchronized (FileQueue.RENDER) {//加锁
FileQueue.RENDER.notify();//若之前为阻塞状态,则通知转为等待状态
}
return true;
}
synchronized (FileQueue.SCANNER) {//加锁
try {
FileQueue.SCANNER.wait();//调用object类中的等待方法,转为等待状态
}catch(InterruptedException e) {//若发生中断异常,则返回false
return false;
}
}
}
}
}
/*Render*/
import javax.swing.SwingUtilities;
public class Render implements Runnable{//读者
private final FileQueue queue;//创建文件队列
private final MainPanel panel;//创建面板
public Render(FileQueue queue, MainPanel panel) {
this.panel = panel;
this.queue = queue;
}
@Override
public void run() {//重写线程里的run()方法
while(!Thread.interrupted()) {//如果没有被终断
FilePair pair = this.queue.getPair();//获取文本字段选择器界面中选择到的初始目录
if(pair != null) {//若文件对不为空
if(pair.getParent() == pair.getChild()) {//若父子对相同,则扫描完毕
SwingUtilities.invokeLater(new Runnable() {//更新图形用户接口
@Override
public void run() {
panel.onFinished();//线程结束
}
});
break;
}else {//继续扫描
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
panel.onFilePair(pair);//得到输出内容
}
});
}
synchronized (FileQueue.SCANNER) {
FileQueue.SCANNER.notify();//扫描线程由阻塞状态转化为等待状态
}
continue;
}
synchronized (FileQueue.RENDER) {
try {
FileQueue.RENDER.wait();//处理线程转化为等待状态
}catch(InterruptedException e) {
break;
}
}
}
}
}
|