用java实现的文本编辑器可以媲美windows自带的编辑器吗?(功能全,超详细)

介绍

1.菜单栏(文本,编辑,格式,关于)
在这里插入图片描述

2.文本子菜单及其功能
在这里插入图片描述
3.编辑子菜单及其功能
在这里插入图片描述
4.格式子菜单及其功能

在这里插入图片描述
5.关于

在这里插入图片描述

实现功能

1. 编辑器可以打开,保存,另存为,关闭文件。
2. 编辑器可以撤销,恢复,剪切,复制黏贴文本。
3. 编辑器可以设置编码和解码的格式(慎用)
4. 编辑器支持ctrl+s,ctrl+c,ctrl+v,ctrl+z,ctrl+y等快捷键。
5. 编辑器支持ctrl+鼠标滚轮放大和缩小字体。
6. 编辑器下方有状态栏,显示当前文本状态,字体大小以及有效字数统计。

入口index文件

index.java

package main;

import main.TextEditBox;

public class index {
	// 主函数
	public static void main(String[] args) {
		TextEditBox notePad = new TextEditBox();
		notePad.launchFrame();
	}
}

主体编辑器对象文件

阅读提示:从最后的launchFrame方法开始阅读本段代码会更加清晰

TextEditBox.java

package main;

import main.tools;

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.*;
import java.io.*;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.UndoManager;

public class TextEditBox extends JFrame {

	/**
	 *
	 * @author zzh
	 * @Time 2020-7-23
	 **/
	private static final long serialVersionUID = 1L;
	private boolean isChanged = false;
	private File file = null;

	private JMenuBar bar = null;
	private JMenu dJMenu = null;
	private JMenu eJMenu = null;
	private JMenu aJMenu = null;
	private JMenu cJMenu = null;
	private JMenu encodeItem = null;
	private JMenu decodeItem = null;
	private JMenuItem openItem = null;
	private JMenuItem saveItem = null;
	private JMenuItem saveAsItem = null;
	private JMenuItem closeItem = null;
	private JMenuItem undoItem = null;
	private JMenuItem redoItem = null;
	private JMenuItem cutItem = null;
	private JMenuItem copyItem = null;
	private JMenuItem pasteItem = null;
	private JRadioButton en_utf_8_Item = null;
	private JRadioButton en_gbk_Item = null;
	private JRadioButton de_utf_8_Item = null;
	private JRadioButton de_gbk_Item = null;
	private JMenuItem aboutItem = null;

	private JFileChooser jfc = null;
	private JTextArea ta = null;
	private JScrollPane scrollPane = null;
	private JPanel jDown = null;
	private JLabel labelLeft = null;
	private JLabel labelCenter = null;
	private JLabel labelRight = null;
	
	//编码格式
	private String encode = "UTF-8";
	private String decode = "UTF-8";

	// ctrl键是否按下
	private boolean key_ctrl = false;
	// 是否在打开文件
	private boolean isOpen = false;
	// 系统滚动条
	private MouseWheelListener sysWheel;
	// 文本域字体大小
	Font f = new Font("Serif", 0, 28);
	// 撤销的监听器
	UndoManager um = new UndoManager();

	public TextEditBox() {
		// 初始化jfc(文件选择器)
		jfc = new JFileChooser();
	}

	// 内部类
	private class event extends MouseAdapter {
		@Override
		public void mouseWheelMoved(MouseWheelEvent e) {
			if (e.isControlDown()) {// 当ctrl键被按下,滚动为放大缩小
				// System.out.println(e.getWheelRotation());
				if (e.getWheelRotation() < 0 && f.getSize() < 60) {// 如果滚动条向前就放大文本
					f = new Font(f.getFamily(), f.getStyle(), f.getSize() + 1);
					ta.setFont(f);
				} else if (e.getWheelRotation() > 0 && f.getSize() > 0) {// 滚动条向后就缩小文本
					f = new Font(f.getFamily(), f.getStyle(), f.getSize() - 1);
					ta.setFont(f);
				}
				labelCenter.setText("字体大小:" + f.getSize());
			} else {// 当ctrl没有被按下,则为系统滚动
				scrollPane.addMouseWheelListener(sysWheel);
				sysWheel.mouseWheelMoved(e);// 触发系统滚动事件。
				scrollPane.removeMouseWheelListener(sysWheel);
			}
		}
	}

	// 内部类
	private class change implements DocumentListener {
		@Override
		public void insertUpdate(DocumentEvent e) {
			changedUpdate(e);
		}

		@Override
		public void removeUpdate(DocumentEvent e) {
			changedUpdate(e);
		}

		@Override
		public void changedUpdate(DocumentEvent e) {
			changed();
		}

		public void changed() {
			if(!isOpen) {
				isChanged = true;
				labelLeft.setText("修改状态:已修改");
				// 统计字数
				labelRight.setText("字数:" + tools.replaceBlank(ta.getText()));
			}
		}
	}
	
	//重新打开读取
	public void openagainDialog() {
		isOpen = true;
		BufferedReader br = null;
		if (file != null) {
			String str = "";
			try {
				InputStreamReader fReader = new InputStreamReader(new FileInputStream(file),decode);
				br = new BufferedReader( fReader);
				ta.setText("");
				str = br.readLine();
				while (str != null) {
					ta.append(str + '\n');
					str = br.readLine();
				}
			} catch (FileNotFoundException e1) {
				ta.setText(ta.getText() + '\n' + "文件未找到");
				e1.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			} finally {
				if (br != null) {
					try {
						br.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
				isOpen = false;
				isChanged = false;
				labelLeft.setText("状态:已打开");
				this.setTitle(file.getName());
				labelRight.setText("字数:" + tools.replaceBlank(ta.getText()));
			}
		}
	}
	// 打开文件读取对话框的方法,打开控件调用此方法
	public void openOpenDialog() {
		isOpen = true;
		int status = jfc.showOpenDialog(TextEditBox.this);
		BufferedReader br = null;
		if (status == JFileChooser.APPROVE_OPTION) {
			file = jfc.getSelectedFile();
			String str = "";
			try {
				InputStreamReader fReader = new InputStreamReader(new FileInputStream(file),decode);
				br = new BufferedReader( fReader);
				ta.setText("");
				str = br.readLine();
				while (str != null) {
					ta.append(str + '\n');
					str = br.readLine();
				}
			} catch (FileNotFoundException e1) {
				ta.setText(ta.getText() + '\n' + "文件未找到");
				e1.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			} finally {
				if (br != null) {
					try {
						br.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
				isOpen = false;
				isChanged = false;
				labelLeft.setText("状态:已打开");
				this.setTitle(file.getName());
				labelRight.setText("字数:" + tools.replaceBlank(ta.getText()));
			}
		}
	}

	// 打开文件保存对话框的方法,保存和另存为时调用此方法
	public void openSaveDialog() {
		int status = jfc.showSaveDialog(TextEditBox.this);
		BufferedWriter bw = null;
		if (status == JFileChooser.APPROVE_OPTION) {
			file = jfc.getSelectedFile();
			try {
				OutputStreamWriter fWriter = new OutputStreamWriter(new FileOutputStream(file),encode);
				bw = new BufferedWriter(fWriter);
				String[] strs = ta.getText().split("\n");
				for (String str : strs) {
					bw.write(str);
					bw.newLine();
					bw.flush();
				}
			} catch (FileNotFoundException e1) {
				ta.setText(ta.getText() + '\n' + "文件未找到");
				e1.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			} finally {
				if (bw != null) {
					try {
						bw.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
				isChanged = false;
				labelLeft.setText("状态:已保存");
				this.setTitle(file.getName());
			}
		}
	}

	// 不打开文件保存对话框直接按照当前文件保存的方法
	public void onlySave() {
		BufferedWriter bw = null;
		try {
			OutputStreamWriter fWriter = new OutputStreamWriter(new FileOutputStream(file),encode);
			bw = new BufferedWriter(fWriter);
			String[] strs = ta.getText().split("\n");
			for (String str : strs) {
				bw.write(str);
				bw.newLine();
				bw.flush();
			}
		} catch (IOException e1) {
			e1.printStackTrace();
		} finally {
			if (bw != null) {
				try {
					bw.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
			isChanged = false;
			labelLeft.setText("状态:已保存");
			this.setTitle(file.getName());
		}
	}

	// 判断按钮是否可点击
	public void ifClick() {
		if (um.canUndo()) {
			undoItem.setEnabled(true);
		} else {
			undoItem.setEnabled(false);
		}
		if (um.canRedo()) {
			redoItem.setEnabled(true);
		} else {
			redoItem.setEnabled(false);
		}
	}

	// 全局按键事件监听
	public void addKeyListener() {
		Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
			public void eventDispatched(AWTEvent event) {
				if (((KeyEvent) event).getID() == KeyEvent.KEY_RELEASED) {
					int key = ((KeyEvent) event).getKeyCode();
					// System.out.println(key);
					if (key == 17) {
						key_ctrl = false;
					}
				}
				if (((KeyEvent) event).getID() == KeyEvent.KEY_PRESSED) {
					int key = ((KeyEvent) event).getKeyCode();
					// System.out.println(key);
					if (key == 17) {
						key_ctrl = true;
					}
					if (key == 83 && key_ctrl == true) {
						System.out.println("ctrl+s");
						if (file == null) {
							openSaveDialog();
						} else {
							onlySave();
						}
					}
					if (key == 90 && key_ctrl == true) {
						System.out.println("ctrl+z");
						if (um.canUndo()) {// 撤销
							um.undo();
						}
					}
					if (key == 89 && key_ctrl == true) {
						System.out.println("ctrl+y");
						if (um.canRedo()) {// 恢复
							um.redo();
						}
					}

				}
			}
		}, AWTEvent.KEY_EVENT_MASK);
	}

	// 添加Bar各个组件的监听器
	public void addBarListener() {
		// 菜单栏焦点事件
		eJMenu.addMenuListener(new MenuListener() {
			@Override
			public void menuCanceled(MenuEvent arg0) {
				menuSelected(arg0);
			}

			@Override
			public void menuDeselected(MenuEvent arg0) {
				menuSelected(arg0);
			}

			@Override
			public void menuSelected(MenuEvent arg0) {
				ifClick();

			}
		});
		// 关于NotePad触发监听器
		aboutItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				JOptionPane.showMessageDialog(TextEditBox.this, "在此有关于<网文专家>的详细介绍……", e.getActionCommand(),
						JOptionPane.INFORMATION_MESSAGE);
			}
		});

		// 编辑栏的撤销、恢复、剪切、复制、粘贴监听器
		undoItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.out.println((um.canUndo() ? "可" : "不可") + "撤销");
				if (um.canUndo()) {// 撤销
					um.undo();
				}
				ifClick();
			}
		});
		redoItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.out.println((um.canRedo() ? "可" : "不可") + "恢复");
				if (um.canRedo()) {// 恢复
					um.redo();
				}
				ifClick();
			}
		});
		cutItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				ta.cut();
			}
		});
		copyItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				ta.copy();
			}
		});
		pasteItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				ta.paste();
			}
		});
		
		//格式栏的编码和解码
		en_utf_8_Item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				encode = "UTF-8";
			}
		});
		en_gbk_Item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				encode = "GBK";
			}
		});
		de_utf_8_Item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.out.println("utf-8");
				decode = "UTF-8";
				openagainDialog();
			}
		});
		de_gbk_Item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.out.println("gbk");
				decode = "GBK";
				openagainDialog();
			}
		});

		// 打开按键(如果文件没修改直接调用读取文件的文件选择对话框,修改了的话弹出提示框提示是否保存再进一步操作)
		openItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (isChanged == false) {
					openOpenDialog();
				} else if (file == null) {
					new JOptionPane();
					int result = JOptionPane.showConfirmDialog(TextEditBox.this, "文件还未保存,是否保存", "提示",
							JOptionPane.YES_NO_CANCEL_OPTION);
					// 选择是的时候调用保存文件的文件选择对话框,否的时候直接打开读取文件的文件选择对话框
					if (result == JOptionPane.YES_OPTION) {
						openSaveDialog();
					} else if (result == JOptionPane.NO_OPTION) {
						openOpenDialog();
					}
				} else if (file != null) {
					new JOptionPane();
					int result = JOptionPane.showConfirmDialog(TextEditBox.this, "文件还未保存,是否保存到" + file.getPath(), "提示",
							JOptionPane.YES_NO_CANCEL_OPTION);
					// 选择是的时候直接保存,否的时候打开读取文件的文件选择对话框
					if (result == JOptionPane.YES_OPTION) {
						onlySave();
					} else if (result == JOptionPane.NO_OPTION) {
						openOpenDialog();
					}
				}
			}
		});

		// 另存为按键
		saveAsItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				openSaveDialog();
			}
		});

		// 保存按钮监听
		saveItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (file == null) {
					openSaveDialog();
				} else {
					onlySave();
				}
			}
		});

		// 关闭的控件
		closeItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 如果修改未保存弹出对话框,保存了则直接退出
				if (isChanged == false) {
					System.exit(0);
				} else if (file == null) {
					new JOptionPane();
					int result = JOptionPane.showConfirmDialog(TextEditBox.this, "文件还未保存,是否保存", "提示",
							JOptionPane.YES_NO_CANCEL_OPTION);
					// 选择是的时候调用保存文件的文件选择对话框,否的时候直接退出
					if (result == JOptionPane.YES_OPTION) {
						openSaveDialog();
					} else if (result == JOptionPane.NO_OPTION) {
						System.exit(0);
					}
				} else if (file != null) {
					new JOptionPane();
					int result = JOptionPane.showConfirmDialog(TextEditBox.this, "文件还未保存,是否保存到" + file.getPath(), "提示",
							JOptionPane.YES_NO_CANCEL_OPTION);
					// 选择是的时候直接保存,否的时候直接退出
					if (result == JOptionPane.YES_OPTION) {
						onlySave();
					} else if (result == JOptionPane.NO_OPTION) {
						System.exit(0);
					}
				}
			}
		});

	}

	// 窗口的监听器
	public void myaddWindowListener() {
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				// 如果修改未保存弹出对话框,保存了则直接退出
				if (isChanged == false) {
					System.exit(0);
				} else if (file == null) {
					new JOptionPane();
					int result = JOptionPane.showConfirmDialog(TextEditBox.this, "文件还未保存,是否保存", "提示",
							JOptionPane.YES_NO_CANCEL_OPTION);
					// 选择是的时候调用保存文件的文件选择对话框,否的时候直接退出
					if (result == JOptionPane.YES_OPTION) {
						openSaveDialog();
					} else if (result == JOptionPane.NO_OPTION) {
						System.exit(0);
					}
				} else if (file != null) {
					new JOptionPane();
					int result = JOptionPane.showConfirmDialog(TextEditBox.this, "文件还未保存,是否保存到" + file.getPath(), "提示",
							JOptionPane.YES_NO_CANCEL_OPTION);
					// 选择是的时候直接保存,否的时候直接退出
					if (result == JOptionPane.YES_OPTION) {
						onlySave();
					} else if (result == JOptionPane.NO_OPTION) {
						System.exit(0);
					}
				}
			}
		});
	}

	// 加载文件菜单
	public void launchBar() {
		bar = new JMenuBar();
		dJMenu = new JMenu("文本");
		eJMenu = new JMenu("编辑");
		cJMenu = new JMenu("格式");
		aJMenu = new JMenu("关于");
		
		openItem = new JMenuItem("打开");
		saveItem = new JMenuItem("保存(ctrl+s)");
		saveAsItem = new JMenuItem("另存为");
		closeItem = new JMenuItem("关闭");
		
		undoItem = new JMenuItem("撤销(ctrl+z)");
		redoItem = new JMenuItem("恢复(ctrl+y)");
		cutItem = new JMenuItem("剪切(ctrl+x)");
		copyItem = new JMenuItem("复制(ctrl+c)");
		pasteItem = new JMenuItem("粘贴(ctrl+v)");
		
		encodeItem = new JMenu("编码");
		decodeItem = new JMenu("解码");
		en_utf_8_Item = new JRadioButton("UTF-8",true);
		en_gbk_Item = new JRadioButton("GBK",false);
		de_utf_8_Item = new JRadioButton("UTF-8",true);
		de_gbk_Item = new JRadioButton("GBK",false);
		//按钮分组
		ButtonGroup group1 = new ButtonGroup(); 
		ButtonGroup group2 = new ButtonGroup();
		group1.add(en_utf_8_Item);
		group1.add(en_gbk_Item);
		group2.add(de_utf_8_Item);
		group2.add(de_gbk_Item);
		
		aboutItem = new JMenuItem("关于网文专家");

		setJMenuBar(bar);
		bar.add(dJMenu);
		bar.add(eJMenu);
		bar.add(cJMenu);
		bar.add(aJMenu);
		
		dJMenu.add(openItem);
		dJMenu.add(saveItem);
		dJMenu.add(saveAsItem);
		dJMenu.add(closeItem);
		eJMenu.add(undoItem);
		eJMenu.add(redoItem);
		eJMenu.add(cutItem);
		eJMenu.add(copyItem);
		eJMenu.add(pasteItem);
		cJMenu.add(encodeItem);
		cJMenu.add(decodeItem);
		aJMenu.add(aboutItem);
		
		encodeItem.add(en_utf_8_Item);
		encodeItem.add(en_gbk_Item);
		decodeItem.add(de_utf_8_Item);
		decodeItem.add(de_gbk_Item);
	}

	// 加载文本域
	public void launchTextArea() {
		ta = new JTextArea();
		ta.setFont(f);
		// 自动换行
		ta.setLineWrap(true);
		// 文本编辑了则触发监听器设置isChanged变量为true
		// 监听状态改变
		ta.getDocument().addDocumentListener(new change());
		// 监听撤销
		ta.getDocument().addUndoableEditListener(new UndoableEditListener() {// 注册撤销可编辑监听器
			public void undoableEditHappened(UndoableEditEvent e) {
				um.addEdit(e.getEdit());
			}
		});// 编辑撤销的监听
		// 文本域滚动条
		scrollPane = new JScrollPane(ta);
		scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		sysWheel = scrollPane.getMouseWheelListeners()[0];// 得到系统滚动事件
		scrollPane.removeMouseWheelListener(sysWheel);// 移除系统滚动,需要时添加
		scrollPane.addMouseWheelListener(new event());
		add(scrollPane, BorderLayout.CENTER);
	}

	// 加载Label的板块
	public void launchLabel() {
		jDown = new JPanel(); // Label的板块
		labelLeft = new JLabel("修改状态:未修改");
		labelCenter = new JLabel("字体大小:" + f.getSize());
		labelRight = new JLabel("字数:" + tools.replaceBlank(ta.getText()));
		jDown.setLayout(new GridLayout(1, 5));
		jDown.add(labelLeft);
		jDown.add(labelCenter);
		jDown.add(labelRight);
		add(jDown, BorderLayout.SOUTH);
	}

	// 加载整个窗口
	public void launchFrame() {
		setBounds(20, 20, 1000, 800);
		setTitle("网文专家v1.1");
		setLayout(new BorderLayout());

		// 加载文件菜单
		this.launchBar();
		// 加载文本域
		this.launchTextArea();
		// 加载Label的板块
		this.launchLabel();
		// 按钮组件监听
		this.addBarListener();
		// 全局按键事件监听
		this.addKeyListener();
		// 窗口的监听器
		this.myaddWindowListener();

		setVisible(true);
		// 设置JFram不要默认关闭,为了用对话框触发退出
		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
	}
}

工具tools文件

tools.java

package main;

public class tools {
	// 字符串去无效字符(空格,制表符,换行,标点等)后的长度.
	public static int replaceBlank(String str) {
		String dest = "";
		if (str != null) {
			str = str.replaceAll("[\\pP‘’“”]", "");
			dest = str.replaceAll("\\s*|\t|\r|\n", "");
		}
		return dest.length();
	}
}

实现难点1 ---- 如何实现快捷键ctrl+任意键位

在Java的swing图形界面中,键位识别都是单个的,没有直接可以使用的组合键,这个时候要实现组合键的功能可以利用按键按下状态(KeyEvent.KEY_RELEASED or KeyEvent.KEY_PRESSED)和一个boolean值,例如,当ctrl键按下,这时让bool为true,这时按下其他的按键就可以实现组合,一旦ctrl键释放,bool为false,组合键失效。(代码参照TextEditBox.java的285行,addKeyListener方法)

实现难点2 ---- 如何实现有效字数的统计

ta.getDocument().addDocumentListener(new change());
用上面的方法给输入文本框绑定自己的DocuListener事件,方法的参数是106行的第二个内部类,它实现了DocumentListener接口,重写了change方法,每次用户输入都统计一次有效字数。
这里的有效字数是调用tools.java中的replaceBlank方法,利用正则匹配消除掉字符串中的无效字符(空格,制表符,换行,标点等)。

实现难点3 ---- 如何实现ctrl+鼠标滚轮控制字体大小

自定义自己的鼠标滚轮事件,并替换掉系统的鼠标滚轮事件。参照TextEditBox.java代码602到608行。这里的自定义鼠标事件是第一个内部类的实例,在84行的第一个内部类继承了MouseAdapter,重写了mouseWheelMoved事件。

和windows自带编辑器的对比

1. 在打开一个大文本的速度上两者相差无几。
2. 基本功能都可以复原windows自带的编辑器,除了一些查找替换的功能还没有实现(利用正则应该不难实现,可以拓展)。
3. windows自带编辑器似乎没有修改编码格式的功能。没有统计有效字数的功能。

总的来说和windows编辑器相差不多,并且代码可以再做优化拓展。

如代码实现有误,或者有更好的实现方法,请在评论区留言。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值