因为大部分的内容都是摘自林老师的JDK7学习笔记的,只做了细微的修改,所以就定为转载了。
=============================JNotePad.java=============================
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.swing.BorderFactory;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
public class JNotePad extends JFrame {
private TextDAO textDAO;
/**
* 按照Eclipse的提示,给这个可以序列化的JNotePad加上serialVersionUID 虽然书上没有这个内容。
*/
private static final long serialVersionUID = 1L;
// 为各个menu相关对象命名,再回忆一下关系 Bar - Menu - Item
private JMenuBar menuBar;
// 首先,上面是菜单栏
private JMenu fileMenu;
private JMenuItem menuOpen;
private JMenuItem menuSave;
private JMenuItem menuSaveAs;
private JMenuItem menuClose;
// 这样第一个菜单,文件菜单就都在这里了。
// 再看第二个菜单
private JMenu editMenu;
private JMenuItem menuCut;
private JMenuItem menuCopy;
private JMenuItem menuPaste;
// 第三个
private JMenu aboutMenu;
private JMenuItem menuAbout;
// 文字编辑区和状态栏
private JTextArea textArea;
private JLabel stateBar;
// 文本区域的右击弹出窗口
private JPopupMenu popUpMenu;
// 用来选择文件的fileChooser
private JFileChooser fileChooser;
// 由于P442,需要将如何具体地创建、读、保存文本文件的方法传入构造器,于是创建一个新的构造器
public JNotePad(TextDAO textDAO) {
this();
this.textDAO = textDAO;
}
// 这里就是构造器了,P429提到说,在这里就需要对JFrame中的组件进行组合
// 由于P442修改了构造器,对公的构造器只有传入具体操作接口的上面那个构造器,这个构造器改为protected的。原因我目前还不知道。
protected JNotePad() {
initComponents();
initEventListeners();
}
// 初始化组件,对JNotePad设置标题,并设置尺寸。
// 初始化组件的时候,要把组件都加上去。
private void initComponents() {
this.setTitle("新增纯文本文档");
this.setSize(400, 300);
// 新建第一个菜单
fileMenu = new JMenu("文档");
menuOpen = new JMenuItem("打开文档");
menuSave = new JMenuItem("保存文档");
menuSaveAs = new JMenuItem("另存为");
menuClose = new JMenuItem("关闭");
// 然后对菜单进行组装。
fileMenu.add(menuOpen);
fileMenu.addSeparator();
fileMenu.add(menuSave);
fileMenu.addSeparator();
fileMenu.add(menuSaveAs);
fileMenu.addSeparator();
fileMenu.add(menuClose);
// 新建第二个菜单
editMenu = new JMenu("编辑");
menuCut = new JMenuItem("剪切");
menuCopy = new JMenuItem("复制");
menuPaste = new JMenuItem("黏贴");
// 组装
editMenu.add(menuCut);
editMenu.add(menuCopy);
editMenu.add(menuPaste);
// 第三个菜单
aboutMenu = new JMenu("关于");
menuAbout = new JMenuItem("关于JNotePad");
aboutMenu.add(menuAbout);
// 菜单栏增加各个菜单
menuBar = new JMenuBar();
menuBar.add(fileMenu);
menuBar.add(editMenu);
menuBar.add(aboutMenu);
// 把menuBar加到JFrame上
this.setJMenuBar(menuBar);
// 在这里对文本编辑区和状态栏做初始化操作
textArea = new JTextArea();
// 设置字体
textArea.setFont(new Font("宋体", Font.PLAIN, 16));
// 自动换行
textArea.setLineWrap(true);
// 给出JScrollPane,并往JScrollPane中,增加文本编辑区
// 已经设置了自动换行,则水平滚动条已不需要,设置垂直滚动条为asNeeded。
JScrollPane panel = new JScrollPane(textArea,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
// MenuBar和ContentPane在窗口中,共同分享大小。除开menuBar的地盘,剩下的就都是ContentPane的了。
// 之所以这边使用的是Container类呢,是因为JFrame的getContentPane()方法本身返回的类型就是Container。
Container contentPane = this.getContentPane();
// textArea -> panel -> contentPane
// BorderLayout.CENTER用来设置contentPane的东西南北中,五个区域里的中。
contentPane.add(panel, BorderLayout.CENTER);
// 然后再给contentPane加上stateBar
stateBar = new JLabel("未修改");
// 设置JLabel中的文字水平左边对齐
stateBar.setHorizontalAlignment(SwingConstants.LEFT);
// 设置JLabel的边框为蚀刻边框
stateBar.setBorder(BorderFactory.createEtchedBorder());
// 接下去就可以加进去了
contentPane.add(stateBar, BorderLayout.SOUTH);
// 创建文本区域的右击弹出窗口
popUpMenu = editMenu.getPopupMenu();
// 创建JFileChooser并赋给fileChooser
fileChooser = new JFileChooser();
}
// 初始化事件监听器
private void initEventListeners() {
// 监听的是,关闭按钮事件
// 已经通过WindowsAdapter重写windowClosing方法,所以这句就不需要了。
// this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 监听快捷键,对应一个MenuItem设置一个对应的快捷键。这样按下一个快捷键,就等同于单击了那个menuItem
menuOpen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
InputEvent.CTRL_MASK));
menuSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
InputEvent.CTRL_MASK));
menuClose.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
InputEvent.CTRL_MASK));
menuCut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X,
InputEvent.CTRL_MASK));
menuCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,
InputEvent.CTRL_MASK));
menuPaste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V,
InputEvent.CTRL_MASK));
// 增加窗口关闭按钮的监听器,而不再使用默认的关闭动作
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
closeFile();
}
});
// 打开菜单项目监听器
menuOpen.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
openFile();
}
});
// 存储菜单项目监听器
menuSave.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
saveFile();
}
});
// 另存为菜单项目监听器
menuSaveAs.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
saveFileAs();
}
});
// 关闭菜单项目监听器
menuClose.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
closeFile();
}
});
// 剪切菜单项目监听器
menuCut.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
cut();
}
});
// 复制菜单项目监听器
menuCopy.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
copy();
}
});
// 黏贴菜单项目监听器
menuPaste.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
paste();
}
});
// 关于菜单项目监听器
menuAbout.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
JOptionPane.showOptionDialog(null,
"JNotePad 0.1\n 来自 krave@163.com", "关于JNotePad",
JOptionPane.YES_NO_OPTION,
JOptionPane.INFORMATION_MESSAGE, null, null, null);
}
});
// 文本编辑区按键监听器
textArea.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
jtextAreaActionPerformed();
}
});
// 文本编辑区鼠标右键弹菜单监听器
textArea.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
popUpMenu.show(textArea, e.getX(), e.getY());
}
}
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
popUpMenu.setVisible(false);
}
}
});
}
private void closeFile() {
// 检查文本内容是否变更过
if (stateBar.getText().equals("未修改")) {
// 则说明已经存储过文件,或者记事本打开后就没编辑过东西。直接关闭即可。
this.dispose();
} else {
// 如果修改过了,就询问是否要保存,保存的话,调用保存方法,不然就直接关闭了哈。
int option = JOptionPane.showConfirmDialog(null, "文档修改过了,要保存不?",
"保存文档?", JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE, null);
switch (option) {
case JOptionPane.YES_OPTION:
saveFile();
dispose();
break;
case JOptionPane.NO_OPTION:
dispose();
}
}
}
private void openFile() {
if (stateBar.getText().equals("未修改")) {
// 如果文本内容为:未修改,则直接调用选取文件的对话框
showFileDialog(); // 显示文件对话框,打开现已存在的文件
} else {
// 如果已经修改了,则提示是否要保存现有文本文件,是则保存,否则放弃该文本
int option = JOptionPane.showConfirmDialog(null, "文档已经修改,是否保存?",
"保存文档?", JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE, null);
switch (option) {
case JOptionPane.YES_OPTION: // 选择是要保存文件的
saveFile();// 调用保存文件的方法
break;
case JOptionPane.NO_OPTION:
showFileDialog(); // 显示文件对话框,打开现已存在的文件
break;
}
}
}
private void showFileDialog() {
int option = fileChooser.showDialog(null, null);// 显示文件选择对话框,并且对对话框做出选择
if (option == JFileChooser.APPROVE_OPTION) {
try {
// 设定JNotePad的标题为文件名
this.setTitle(fileChooser.getSelectedFile().toString());
// 清空文本框
textArea.setText("");
stateBar.setText("未修改");
// 实际上这一块可以坐优化,因为fileChooser本身就可以返回File对象了。
String text = textDAO.read(fileChooser.getSelectedFile()
.toString());
// 把选择的文本文件的文字内容添加到文本框中
textArea.setText(text);
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, e.toString(), "打开文档失败",
JOptionPane.ERROR_MESSAGE);
}
}
}
private void saveFile() {
// 先从标题栏取得文件名
Path path = Paths.get(this.getTitle());
if (Files.notExists(path)) {
saveFileAs();
} else {
// 如果这个文档存在的话,就直接保存
try {
// 调用保存方法
textDAO.save(path.toString(), textArea.getText());
// 将状态栏变更为未修改
stateBar.setText("未修改");
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.toString(), "保存文档失败",
JOptionPane.ERROR_MESSAGE);
}
}
}
private void saveFileAs() {
// 显示文档选择对话框
int option = fileChooser.showDialog(null, null);
// 如果确实选取了文档要保存的话
if (option == JFileChooser.APPROVE_OPTION) {
// 在标题栏设置文档路径
setTitle(fileChooser.getSelectedFile().toString());
// 调用textDAO来创建新文档并保存
textDAO.create(fileChooser.getSelectedFile().toString());
saveFile();
}// 如果取消的话,关闭文档选择器,恢复到之前的状况。
}
private void cut() {
textArea.cut();
stateBar.setText("已修改");
popUpMenu.setVisible(false);
}
private void copy() {
textArea.copy();
stateBar.setText("已修改");
popUpMenu.setVisible(false);
}
private void paste() {
textArea.paste();
stateBar.setText("已修改");
popUpMenu.setVisible(false);
}
private void jtextAreaActionPerformed() {
stateBar.setText("已修改");
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
new JNotePad(new FileTextDAO()).setVisible(true);
}
});
}
}
=============================TextDAO.java=============================
public interface TextDAO {
void create(String file);
String read(String file);
void save(String file, String text);
}
=============================FileTextDAO.java=============================
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.logging.Level;
import java.util.logging.Logger;
public class FileTextDAO implements TextDAO {
@Override
public void create(String file) {
try {
Files.createFile(Paths.get(file));
} catch (Exception e) {
Logger.getLogger(FileTextDAO.class.getName()).log(Level.SEVERE,
null, e);
}
}
@Override
public void save(String file, String text) {
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(file),
Charset.forName(System.getProperty("file.encoding")))) {
writer.write(text);
} catch (IOException e) {
Logger.getLogger(FileTextDAO.class.getName()).log(Level.SEVERE,
null, e);
}
}
@Override
public String read(String file) {
byte[] textData = null;
try {
textData = Files.readAllBytes(Paths.get(file));
} catch (IOException e) {
Logger.getLogger(FileTextDAO.class.getName()).log(Level.SEVERE,
null, e);
}
return new String(textData);
}
}