本文是对记事本中的新建、打开、保存、另存为和退出功能的实现和讲解。主要需要解决的问题有:如何从文件中读取内容并放入文本编辑区域?如何将对已有文件中内容的改变保存回文件中?如何创建新文件保存文本内容?接下来就逐个解决。
一、文件读取
首先是文件的读取,文件的读取方式有很多,以下是本人在实现记事本程序的时候参考的一种
String readFile() {// 声明缓冲字符流变量BufferedReader br = null;StringBuilder sb = null;try {br = new BufferedReader(new FileReader(jfilechoose.getSelectedFile()));sb = new StringBuilder();// 创建缓冲字符串String str;int count = 0;//控制添加换行符while ((str = br.readLine()) != null) {if (count == 0){sb.append(str);//第一行不换行count=1;}elsesb.append("\n" + str);//后面的行为换行+内容}} catch (FileNotFoundException e1) {// 弹出“文件未找到”对话框,showMessageDialog中的null表示对话框采用默认框架JOptionPane.showMessageDialog(null, "未找到该文件!");return null;} catch (IOException e1) {// 弹出“文件读取异常”对话框,null表示对话框采用默认框架JOptionPane.showMessageDialog(null, "文件读取异常");return null;} finally {// 关闭字符流if (br != null)try {br.close();//关闭流} catch (IOException e1) {e1.printStackTrace();}}File file = jfilechoose.getSelectedFile();//获取选中的文件name = file.getName();//文件名filepath = file.getAbsolutePath();//文件绝对路径setTitle(name + " - 记事本");//设置记事本的标题return sb.toString();//转换成字符串输出}
BufferedReader是从缓冲区中读取内容,作用是对带有换行符的文本内容的按行读取,创建时需要一个Reader的参数,由Reader去用流的方式读取数据,而BufferedReader只是解析流数据并组成一行一行的String。
StringBuilder()不传入参数时是构造一个字符串构建器,其中不包含任何字符,初始容量为16个字符,也可以传入参数指定初始容量和初始字符串内容。
readLine的读取方式为读取一行内容,直到换行符,但不读取换行符,因此在文本区域显示时要添加换行符。添加换行符的方式也可以采用“每行内容+换行符“的形式,这样无非是最后一行文本后多出一个空行,这对记事本而言是没有影响的。
对文件绝对路径的记录有助于了解当前文本区域显示内容属于哪个文件。
二、文件保存
void saveFile(File file) {BufferedWriter bw = null;try {bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));bw.write(jtext.getText());//写入文件bw.flush();} catch (FileNotFoundException e1) {JOptionPane.showMessageDialog(MyNotepad.this, "文件保存出错" + e1.getMessage());} catch (IOException e1) {e1.printStackTrace();} finally {try {if (bw != null)bw.close();} catch (IOException e1) {}}}
BufferedWriter(Writer out, int size)传入size可以指定大小,不传则创建使用默认大小的输出缓冲区的缓冲字符输出流。
OutputStreamWriter是从字符流到字节流的桥接。
用FileOutputStream写入文件的流程图如下:
调用BufferedWriter的write方法时,数据写入缓冲区,并没有直接写到目的文件里,必须调用BufferedWriter的flush()方法刷新一下该缓冲流,把数据写入到目的文件里。
三、文件创建(另存为功能)
文件创建首先弹出一个是否保存的对话框,这个对话框在JFileChooser(文件选择器)这个类中。获取选中文件的文件名和绝对地址,文件名用于设置记事本标题,绝对地址用于记录当前文本内容的所属文件。对于选中文件判断是否以及存在于系统中,存在便询问是否覆盖原文件,覆盖或者文件不存在都可以调用上一步的文件保存方法;不覆盖则重新弹出文件保存对话框。
void createFile() {File file = null;// 选择保存或取消if (jfilechoose.showSaveDialog(MyNotepad.this) == JFileChooser.APPROVE_OPTION)file = jfilechoose.getSelectedFile();// 获取选中的文件elsereturn;name = jfilechoose.getName(file);// 获取输入的文件名filepath = file.getAbsolutePath();//获取文件的绝对路径if (file.exists()) { // 若选择已有文件----询问是否要覆盖int select = JOptionPane.showConfirmDialog(null, "该文件已存在,是否覆盖原文件", "确认", JOptionPane.YES_NO_OPTION);if (select == JOptionPane.YES_OPTION)saveFile(file);elsejfilechoose.showSaveDialog(MyNotepad.this);// 重新选择} else//文件不存在,则直接保存saveFile(file);}
另存为功能就是另外创建一个文件保存当前文件内容,因此另存为功能的实现如下
createFile();setTitle(name + " - 记事本");
四、保存功能的实现
有了以上3个函数的支撑就可以开始实现保存功能了,保存分为两种情况,第一种是当前文本内容尚未存在过任何文件中,此时应该调用文件创建的方法;第二种是当前文本内容是从已有文件中读取出来的,此时应该调用文件保存的方法。
int Save() {int select = 0;// 判断文件是否已存在if (filepath == null) {select = JOptionPane.showConfirmDialog(MyNotepad.this, "是否保存修改?");if (select == JOptionPane.YES_OPTION)createFile();//文件不存在,则创建文件} else {Boolean isSaved = jtext.getText().equals(readFile())//文本区域内容与文件中相同返回trueif (!isSaved){// 文件未保存//去掉以下两行,保留第3行就是已有文件直接保存,不提醒 select = JOptionPane.showConfirmDialog(MyNotepad.this, "是否保存修改?");if(select == JOptionPane.YES_OPTION)saveFile(jfilechoose.getSelectedFile());}}return select;// 返回选项}
filepath是文件的绝对路径,初始化为null,在读取文件和创建文件后会获取读取或创建文件的绝对路径。JOptionPane.YES_OPTION是弹出窗口的四个选项中的”是(Y)“。
否(N):JOptionPane.NO_OPTION
取消:JOptionPane.CANCEL_OPTION
右上角的X:JOptionPane.CLOSED_OPTION
则保存菜单的点击事件处理操作如下:
Save();//调用文件保存方法setTitle(name + " - 记事本");//设置记事本标题
五、打开功能的实现
打开新文件之前要先判断当前文本内容是否保存,之后便是从文件选择器中选择要打开的文件,读取文件,设置记事本标题。如果写了撤销功能,还需要撤销所有的"撤销"操作,并将撤销功能设为不可用状态。
// 是否保存对原文件修改int select = Save();if (select == JOptionPane.CANCEL_OPTION||select == JOptionPane.CLOSED_OPTION)// 取消、关闭按钮,则返回return;// 弹出一个 "Open File" 文件选择器对话框select = jfilechoose.showOpenDialog(MyNotepad.this);// 选择打开文件,则读写文件if (select == JFileChooser.APPROVE_OPTION) {jtext.setText(readFile());// 写入文本框jtext.setCaretPosition(0);// 定位光标至行首undom.discardAllEdits(); //撤消所有的"撤消"操作 edit_undo.setEnabled(false);//撤销功能设为不可用}
六、新建功能的实现
新建功能与打开功能类似,区别在于不需要打开文件,只要清空文本区域即可,还有文件路径和记事本标题的不同。
int select=showSaveDialog();if (select == JOptionPane.CANCEL_OPTION||select == JOptionPane.CLOSED_OPTION)//取消、关闭按钮,则返回return;else{jtext.replaceRange("",0,jtext.getText().length());//清空文本区域setTitle("无标题 - 记事本");//设置记事本标题filepath=null;//当前文件路径为空undom.discardAllEdits(); //撤消所有的"撤消"操作 edit_undo.setEnabled(false);//撤销功能设为不可用}
七、退出功能的实现
退出程序之前询问一下是否要保存当前文本内容,因为有新窗口功能的存在,所以退出是关闭当前窗口,而不是关闭程序。
int select = showSaveDialog();if (select != JOptionPane.CANCEL_OPTION && select != JOptionPane.CLOSED_OPTION)dispose();//释放窗体,dispose()
点击窗口右上角关闭的四种关闭方式:
1.this.setDefaultCloseOperation(0);// DO_NOTHING_ON_CLOSE,不执行任何操作。
2.this.setDefaultCloseOperation(1);//HIDE_ON_CLOSE,只隐藏界面,setVisible(false)。
3.this.setDefaultCloseOperation(2);//DISPOSE_ON_CLOSE,隐藏并释放窗体,dispose(),当最后一个窗口被释放后,则程序也随之运行结束。
4.this.setDefaultCloseOperation(3);//EXIT_ON_CLOSE,直接关闭应用程序,System.exit(0),一个main函数对应一整个程序。
上述四种关闭方式源自CSDN博主「Andrewlu58」的原创文章,链接附上:/xiangyong58/article/details/825,如果上述四种关闭方式对你有所帮助,可以点点链接前往原文章支持一波。
主界面窗口右上角关闭的操作与退出功能一样,不同的是dispose()用于windowClosing中会关闭全部的窗体,因此要用setDefaultCloseOperation(2)。
addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {int select = showSaveDialog();if (select != JOptionPane.CANCEL_OPTION &&select != JOptionPane.CLOSED_OPTION)setDefaultCloseOperation(2);//隐藏并释放窗体,dispose(),当最后一个窗口被释放后,则程序也随之运行结束。 }});