电脑自带的记事本不太方便,所以想自己在记事本的基础上增加些功能。
准备工作:
eclipse外来包log4j
在目标文件夹内建立想要记录的某种类型的txt文本,文本内包含其绝对路径(方便保存时找到路径),新建log.txt用于日志记录。
功能如下:
菜单1:包含打开和保存2个功能,如下---
打开会定位到设定的文件夹,过滤需要选择的文件类型(比如txt),然后在显示的时候自动追加日期,根据【】标记符将内容显示为红色加大字体。
保存会去除文本末尾空格,去除没有文字记录的日期。
菜单2:我是分成日记、收支、实验3类,分别设置各种打开快捷键,其实就是简化打开操作(比如ctrl+D打开日记,比选择打开-选择文件-点击确定方便),这部分单独设置个监听器即可。
菜单3:附加功能,设置了总可见文字的字数、总行数(包含换行)、换行并缩进(缩进量为txt文本2个汉字)、按日期查找记录、产生成对【】标记符、光标定位到可见文字末尾 这些功能
代码如下
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.Logger;
/**
* updated by littlebird on 2018/02/26.
*
*
*/
public class TextNote {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
TFrame frame = new TFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
class TFrame extends JFrame {
/**
* @author littlebird
*/
private static final long serialVersionUID = 20180226L;
Logger log = Logger.getLogger(TextNote.class);//日志
Pattern p;//用于正则表达式匹配日期和【】标记符
Matcher m;
Map<Integer, Integer> map = new TreeMap<>();//用于检测两个日期间是否有文字记录
JTextPane area;//比JTextArea好,可以设置部分字体的格式
SimpleAttributeSet attrSet = new SimpleAttributeSet();//文本属性配置
StyledDocument doc;//可以设置部分文字效果
JFileChooser chooser = new JFileChooser();//文件类型过滤器
String name;//记录文件名
static final int WIDTH = 1000;//窗口显示宽、高
static final int HEIGHT = 800;
JMenuBar menus = new JMenuBar();//菜单栏,用于放置菜单Menu
JMenu file = new JMenu("文件");//菜单,被添加到菜单栏
JMenu sort = new JMenu("分类");
JMenu function = new JMenu("功能");
JMenuItem diary = new JMenuItem("日记");//元素,被添加到菜单
JMenuItem experiment = new JMenuItem("实验");
JMenuItem account = new JMenuItem("账目");
JMenuItem open = new JMenuItem("打开");
JMenuItem save = new JMenuItem("保存");
JMenuItem charnum = new JMenuItem("字数");
JMenuItem linenum = new JMenuItem("行数");
JMenuItem font = new JMenuItem("标号");
JMenuItem shiftEnter = new JMenuItem("新段落");
JMenuItem toTextEnd = new JMenuItem("trim效果");
JMenuItem findByDate = new JMenuItem("查找");
public TFrame() {
super("notebook1.0");
PropertyConfigurator.configure("src/log4j.properties");//加载位于src下的日志配置文件,配置见后
file.setFont(new Font("TimesRoman", Font.BOLD, 15));//菜单字体效果
sort.setFont(new Font("TimesRoman", Font.BOLD, 15));
function.setFont(new Font("TimesRoman", Font.BOLD, 15));
menus.add(file);
menus.add(sort);
menus.add(function);
file.add(open);
file.addSeparator();
file.add(save);
sort.add(diary);
sort.addSeparator();
sort.add(account);
sort.addSeparator();
sort.add(experiment);
function.add(charnum);
function.addSeparator();
function.add(linenum);
function.addSeparator();
function.add(font);
function.addSeparator();
function.add(shiftEnter);
function.addSeparator();
function.add(toTextEnd);
function.addSeparator();
function.add(findByDate);
// 设置快捷键,比如第一个为Ctrl+O
open.setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
save.setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
experiment.setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
account.setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
diary.setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_D, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
shiftEnter.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_MASK));
linenum.setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_L, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
toTextEnd.setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_UP, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
findByDate.setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
font.setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
charnum.setAccelerator(
KeyStroke.getKeyStroke(KeyEvent.VK_N, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
area = new JTextPane();
area.setSelectedTextColor(new Color(238, 121, 66));//设置选中文本颜色
area.setSelectionColor(new Color(238, 238, 209));//设置选中区域颜色(不仅仅是文本,还有空白的地方)
area.setBackground(new Color(245, 255, 250));//背景色
area.setText("");
area.setFont(new Font("楷体", Font.BOLD, 20));//字体
doc = area.getStyledDocument();
JScrollPane scrollPane = new JScrollPane(area);// 添加滚动条,应该是装饰着模式
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
int width = (int) screen.getWidth();
int height = (int) screen.getHeight();
setLocation(width / 4, height / 20);//窗口左上顶点所处电脑屏幕位置
setSize(WIDTH, HEIGHT);
setJMenuBar(menus);//设置添加完后的菜单栏
getContentPane().add(scrollPane, BorderLayout.CENTER);//将包装后的文本区域添加到窗口中
setResizable(true);//可调节窗口大小
FileNameExtensionFilter filter = new FileNameExtensionFilter("文本文件", "txt");//文件后缀名过滤器
chooser.setFileFilter(filter);
// chooser.setFileView(new FileIconView(filter, null));图标暂无
//以下添加监听器
open.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
chooser.setCurrentDirectory(new File("D:\\MyDiary"));//设置要打开文件所在目录
int result = chooser.showOpenDialog(TFrame.this);//弹出对话框选择
if (result == JFileChooser.APPROVE_OPTION) {//选择并点击确定
name = chooser.getSelectedFile().getPath();
File f = new File(name);
String content;
try {
//因为电脑每次保存时txt文档都会变为ANSI编码,即GB2312格式
InputStreamReader isr = new InputStreamReader(new FileInputStream(f), "GB2312");
BufferedReader br = new BufferedReader(isr);
area.setText("");//先清空
//日期格式
String currentTime = new SimpleDateFormat("yyyy年MM月dd日 E").format(new Date());
while ((content = br.readLine()) != null) {
try {
doc.insertString(doc.getLength(), content + "\n", attrSet);
} catch (BadLocationException e1) {
log.error("打开文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt"))
+ "时追加文本错误");
e1.printStackTrace();
}
}
if (!area.getText().contains(currentTime)) {
try {
doc.insertString(doc.getLength(), "\n"+currentTime + "\n\n", attrSet);
} catch (BadLocationException e1) {
log.error("打开文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt"))
+ "时追加文本错误");
e1.printStackTrace();
}
}
try {
doc.insertString(doc.getLength(), " ", attrSet);
} catch (BadLocationException e1) {
log.error("");
e1.printStackTrace();
}
br.close();
isr.close();
} catch (FileNotFoundException ex) {
log.error("打开文件时找不到文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt")));
ex.printStackTrace();
} catch (IOException ex) {
log.trace("打开文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt")) + "时IO错误");
ex.printStackTrace();
} finally {
content = null;
}
p = Pattern.compile("【[^【]*】");
// windows中\r\n换行符到这里算一个字符,要扣掉,不然后面setCharacterAttributes方法参数不对
m = p.matcher(area.getText().replaceAll("\r\n", "\n"));
map.clear();
while (m.find()) {
map.put(m.start(), m.end() - m.start());// 收集开始index和长度
}
if (!map.isEmpty()) {//如果有【】标记符
Iterator<Integer> it = map.keySet().iterator();
StyleConstants.setForeground(attrSet, Color.red);//更改文本颜色和字体大小
StyleConstants.setFontSize(attrSet, 24);
Integer in;
while (it.hasNext()) {
in = it.next();
doc.setCharacterAttributes(in, map.get(in), attrSet, true);//设置部分文本以上效果
}
StyleConstants.setForeground(attrSet, Color.darkGray);//设置完后配置调回来
StyleConstants.setFontSize(attrSet, 20);
}
}
}
});
save.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int result = JOptionPane.showConfirmDialog(TFrame.this, "确定保存?", "提示",
JOptionPane.YES_NO_CANCEL_OPTION);//对话框:是否保存
if (result == JOptionPane.YES_OPTION) {
int first = area.getText().indexOf("D:");
int last = area.getText().indexOf(".txt");
if (first != -1 && last > first) {//如果包含路径才能保存
name = area.getText().substring(first, last + 4);
OutputStreamWriter osw;
BufferedWriter bw;
List<Integer> list = new ArrayList<>();
StringBuilder sb = new StringBuilder();
sb.append(area.getText());
// 去除无记录的日期
p = Pattern.compile("\\b[1-9]\\d{3}.((0[1-9])|(1[0-2])).((0[1-9])|([12]\\d)|(3[0-1])).\\b");
m = p.matcher(area.getText());
while (m.find()) {
list.add(m.start());//list添加所有日期字符串的开始index
}
//先看看最后一个日期后面有无文字记录,没有就删除
if(area.getText().substring(list.get(list.size()-1)+15).trim().isEmpty())
sb.delete(list.get(list.size()-1), list.get(list.size()-1)+15);
//再看看其他日期后面有无文字记录,没有删除
for (int i = 0; i < list.size() - 1; i++) {
if (area.getText().substring((list.get(i) + 15), (list.get(i + 1))).trim().isEmpty()) {
sb.delete(list.get(i), list.get(i + 1));
}
}
try {
osw = new OutputStreamWriter(new FileOutputStream(name), "GB2312");//同样编码问题
bw = new BufferedWriter(osw);
bw.write(sb.toString().trim());
bw.close();
osw.close();
log.info("保存文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt")) + "成功");
JOptionPane.showMessageDialog(TFrame.this, "保存成功");
} catch (FileNotFoundException e1) {
log.error("保存文件时找不到文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt")));
e1.printStackTrace();
} catch (IOException e1) {
log.trace("保存文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt"))
+ "时IO错误");
e1.printStackTrace();
}
} else {
JOptionPane.showMessageDialog(TFrame.this, "缺少路径,不能保存");
}
}
}
});
shiftEnter.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int caret = area.getCaretPosition();//光标位置
try {
area.getDocument().insertString(caret, "\n ", attrSet);
} catch (BadLocationException e1) {
log.error("shift+enter时出错");
e1.printStackTrace();
}
area.setCaretPosition(caret + 5);// 1个enter4个空格
}
});
toTextEnd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
area.setCaretPosition(area.getText().trim().replaceAll("\r\n", "\n").length());//trim+replace
}
});
// 按日期进行查找
findByDate.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// \b标识开始和结尾
p = Pattern.compile("\\b[1-9]\\d{3}.((0[1-9])|(1[0-2])).((0[1-9])|([12]\\d)|(3[0-1])).\\b");//日期匹配
m = p.matcher(area.getText());
List<Object> list = new ArrayList<>();
while (m.find()) {
list.add(m.group());//装入匹配到的日期
}
if (list.size() != 0) {
//弹出下拉列表对话框
String start = (String) JOptionPane.showInputDialog(TFrame.this, "开始日期", "查找",
JOptionPane.INFORMATION_MESSAGE, null, list.toArray(), list.get(0));
if (start != null) {
String end = (String) JOptionPane.showInputDialog(TFrame.this, "截止日期(不包含)", "查找",
JOptionPane.INFORMATION_MESSAGE, null, list.toArray(), list.get(list.size() - 1));
if (end != null) {
area.setText(area.getText()
.substring(area.getText().indexOf(start), area.getText().indexOf(end)).trim());
} else
area.setText(area.getText().substring(area.getText().indexOf(start)).trim());
}
}
p = Pattern.compile("【[^【]*】");
m = p.matcher(area.getText().replaceAll("\r\n", "\n"));// windows中\r\n换行符到这里算一个字符,要扣掉
map.clear();
while (m.find()) {
map.put(m.start(), m.end() - m.start());// 收集开始index和长度
}
if (!map.isEmpty()) {
Iterator<Integer> it = map.keySet().iterator();
StyleConstants.setForeground(attrSet, Color.red);
StyleConstants.setFontSize(attrSet, 24);
Integer in;
while (it.hasNext()) {
in = it.next();
doc.setCharacterAttributes(in, map.get(in), attrSet, true);
}
StyleConstants.setForeground(attrSet, Color.darkGray);
StyleConstants.setFontSize(attrSet, 20);
}
}
});
//本来这个变量是修改字体的,但是对于txt没有意义,改为使用【】标记符
font.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int index = area.getCaretPosition();
try {
area.getDocument().insertString(index, "【】", attrSet);
} catch (BadLocationException e1) {
e1.printStackTrace();
}
area.setCaretPosition(index + 1);
}
});
linenum.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int line = area.getDocument().getDefaultRootElement().getElementCount();
JOptionPane.showMessageDialog(TFrame.this, "行数: " + line);
}
});
charnum.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int count = area.getText().replaceAll(" ", "").replaceAll("\r\n", "").length();//去除空格和换行
JOptionPane.showMessageDialog(TFrame.this, "总字数: " + count);
}
});
diary.addActionListener(new OpenListener());
account.addActionListener(new OpenListener());
experiment.addActionListener(new OpenListener());
}
// 单独设置快捷方式
class OpenListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == diary) {
name = "D:\\MyDiary\\diary.txt";
} else if (e.getSource() == account) {
name = "D:\\MyDiary\\account.txt";
} else if (e.getSource() == experiment) {
name = "D:\\MyDiary\\experiment.txt";
}
String content;
try {
File f = new File(name);
InputStreamReader isr = new InputStreamReader(new FileInputStream(f), "GB2312");
BufferedReader br = new BufferedReader(isr);
area.setText("");
String currentTime = new SimpleDateFormat("yyyy年MM月dd日 E").format(new Date());
while ((content = br.readLine()) != null) {
try {
doc.insertString(doc.getLength(), content + "\n", attrSet);
} catch (BadLocationException e1) {
log.error(
"打开文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt")) + "时追加文本错误");
e1.printStackTrace();
}
}
if (!area.getText().contains(currentTime)) {
try {
doc.insertString(doc.getLength(), "\n"+currentTime + "\n\n", attrSet);
} catch (BadLocationException e1) {
log.error(
"打开文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt")) + "时追加文本错误");
e1.printStackTrace();
}
}
try {
doc.insertString(doc.getLength(), " ", attrSet);
} catch (BadLocationException e1) {
log.error("打开文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt")) + "时追加文本错误");
e1.printStackTrace();
}
br.close();
isr.close();
} catch (FileNotFoundException ex) {
log.error("打开文件时找不到文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt")));
ex.printStackTrace();
} catch (IOException ex) {
log.trace("打开文件" + name.substring(name.lastIndexOf("\\") + 1, name.indexOf(".txt")) + "时IO错误");
ex.printStackTrace();
} finally {
content = null;
}
p = Pattern.compile("【[^【]*】");
m = p.matcher(area.getText().replaceAll("\r\n", "\n"));// windows中\r\n换行符到这里算一个字符,要扣掉
map.clear();
while (m.find()) {
map.put(m.start(), m.end() - m.start());// 收集开始index和长度
}
if (!map.isEmpty()) {
Iterator<Integer> it = map.keySet().iterator();
StyleConstants.setForeground(attrSet, Color.red);
StyleConstants.setFontSize(attrSet, 24);
Integer in;
while (it.hasNext()) {
in = it.next();
doc.setCharacterAttributes(in, map.get(in), attrSet, true);
}
StyleConstants.setForeground(attrSet, Color.darkGray);
StyleConstants.setFontSize(attrSet, 20);
}
}
}
}
下面附上log配置:
log4j.rootLogger=INFO,logfile#根级别
log4j.appender.logfile=org.apache.log4j.RollingFileAppender#滚动
log4j.appender.logfile.Threshold=INFO#级别
log4j.appender.logfile.File=D:\\MyDiary\\log.txt#日志目录
log4j.appender.logfile.Append=true#在末尾追加
log4j.appender.logfile.MaxFileSize=2MB#最大文件大小
log4j.appender.logfile.MaxBackupIndex=3#最大日志数
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout#格式
log4j.appender.logfile.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS}(%r)-->[%t] %l: %m %x %n
log4j.appender.logfile.encoding=GB2312#日志编码与记事本相同即可
完。