目录
3.2.1 使用SwingUtilities.invokeAndWait()
最近在学习Swing GUI编程时,尝试着自己动手实现了一个简易的文本编辑器。通过这个项目,我不仅加深了对Swing组件的理解,还学会了如何组织代码结构以及如何处理各种事件。下面我将分享一下我的实践过程和代码实现。
1. 项目背景
文本编辑器是计算机软件中的基础工具之一,对于学习编程的人来说,制作一个简单的文本编辑器是一个很好的练习项目。它可以帮助我们熟悉基本的图形用户界面设计,同时也能够让我们掌握文件读写的基本操作。
2. 技术栈
- Java SE
- Swing
3. 项目结构
项目主要分为两个部分:EditFrame
类和 JEditMain
类。EditFrame
类负责整个编辑器界面的设计与逻辑处理;而 JEditMain
类则是项目的入口点,用于启动编辑器。
3.1 EditFrame类
EditFrame
类继承自 JFrame
,用于创建一个窗口,并在这个窗口内添加文本区域、菜单栏和上下文菜单。此外,该类还包括了一些辅助方法,如创建菜单项、显示文件打开/保存对话框等。
3.1.1 界面布局
编辑器的主界面包含一个文本区域,用户可以在其中输入文本。同时,我们为它添加了一个滚动条以便于查看长文档。此外,还有菜单栏提供了文件操作选项(如打开、保存、退出等),以及编辑选项(如复制、粘贴、查找替换等)。为了方便用户操作,我们还为文本区域添加了一个上下文菜单,当用户右击文本区域时,会弹出一个包含常用编辑命令的小菜单。
如上图所示,这是我们的编辑器主界面。可以看到顶部的菜单栏包含了文件(File)、编辑(Edit)和帮助(Help)三个主菜单,右侧的滚动条可以帮助浏览长文档。
3.1.2 功能实现
- 文件操作:支持文件的打开、保存及另存为。
- 编辑功能:支持文本的复制、粘贴、剪切及查找替换。
- 帮助信息:提供简单帮助和关于信息。
3.2 JEditMain类
JEditMain
类是程序的入口点,它包含了 main
方法。在这个类中,我们首先设置了系统的默认外观和感觉,然后调用 createGUI
方法来创建并显示我们的编辑器界面。
3.2.1 使用SwingUtilities.invokeAndWait()
由于Swing组件必须在事件调度线程(Event Dispatch Thread)上创建和更新,因此我们在 main
方法中使用了 SwingUtilities.invokeAndWait()
方法来确保我们的GUI创建代码在正确的线程上执行。
4. 源代码
以下是关键代码片段:
package com.xiaoqi.java;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.*;
import java.util.HashMap;
import static javax.swing.JOptionPane.PLAIN_MESSAGE;
public class EditFrame extends JFrame {
private JTextArea mainTextArea;//文本域
private Font font = new Font("微软雅黑", Font.PLAIN, 12);//字体对象
//卷曲面板
private JScrollPane jScrollPane;
//全局事件监听器
private ActionMenuItemListener actionMenuItemListener = new ActionMenuItemListener();
//我也不知道我为什么要定义全局指向,应为tm的内部类要使用父类
private Component globalThis = this;
//弹出菜单
private JPopupMenu mainPopupMenu;
public EditFrame(String title) throws HeadlessException {
super(title);
//获取布局容器
Container mainContentPane = getContentPane();
mainContentPane.setLayout(new BorderLayout());
//创建菜单
createMenuToMainFrame(mainContentPane);
//创建文本域
createTexTMain(mainContentPane);
//创建弹出菜单
createPopMenu(mainContentPane,mainTextArea);
}
/**
* @Description 创建右键菜单
* @param mainContentPan
* @param mainTextArea
* @return void
*/
private void createPopMenu(Container mainContentPan,JTextArea mainTextArea){
//创建菜单
mainPopupMenu = new JPopupMenu();
JMenuItem copy = createMenuItem("复制", "copy");
JMenuItem paste = createMenuItem("粘贴", "paste");
JMenuItem repalce = createMenuItem("查找替换", "repalce");
JMenuItem cut = createMenuItem("剪切", "cut");
mainPopupMenu.add(copy);
mainPopupMenu.add(paste);
mainPopupMenu.addSeparator();
mainPopupMenu.add(repalce);
mainPopupMenu.addSeparator();
mainPopupMenu.add(cut);
//添加事件
copy.addActionListener(actionMenuItemListener);
paste.addActionListener(actionMenuItemListener);
repalce.addActionListener(actionMenuItemListener);
cut.addActionListener(actionMenuItemListener);
//添加菜单弹出事件
mainTextArea.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
//判断鼠标右键点击
if (e.getButton() == MouseEvent.BUTTON3){
mainPopupMenu.show(e.getComponent(),e.getX(),e.getY());
}
}
});
}
/**
* @param mainContentPan
* @return void
* @Description 创建文本域
*/
private void createTexTMain(Container mainContentPan) {
mainTextArea = new JTextArea();
mainTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); //设置鼠标在文本域的样式
mainTextArea.setLineWrap(true);//自动换行
mainTextArea.setFont(font);//设置文本域全局字体
mainTextArea.setDisabledTextColor(Color.BLACK);
//使用JScrollPan才有滚动条
jScrollPane = new JScrollPane();
mainContentPan.add(jScrollPane, BorderLayout.CENTER);
jScrollPane.setViewportView(mainTextArea);
// mainContentPan.add(mainTextArea);
}
/**
* @param mainContentPane
* @return void
* @Description 创建菜单
*/
private void createMenuToMainFrame(Container mainContentPane) {
//创建菜单
JMenuBar mainMenuBar = new JMenuBar();
setJMenuBar(mainMenuBar);
//创建文件菜单
JMenu fileMenu = new JMenu("文件(file)");
mainMenuBar.add(fileMenu);
//创建子菜单
JMenuItem openFile = createMenuItem("打开 (open)", "open");
JMenuItem saveFile = createMenuItem("保存 (save)", "save");
JMenuItem savaAs = createMenuItem("另存为 (save as)", "saveAs");
JMenuItem exit = createMenuItem("退出 (Exit)", "exit");
fileMenu.add(openFile);
fileMenu.add(saveFile);
fileMenu.add(savaAs);
fileMenu.addSeparator();
fileMenu.add(exit);
//添加菜单事件
openFile.addActionListener(actionMenuItemListener);
saveFile.addActionListener(actionMenuItemListener);
savaAs.addActionListener(actionMenuItemListener);
exit.addActionListener(actionMenuItemListener);
//创建编辑菜单
JMenu editMrnu = new JMenu("编辑(edit)");
JMenuItem copy = createMenuItem("复制(copy)", "copy");
JMenuItem paste = createMenuItem("粘贴(paste)", "paste");
JMenuItem repalce = createMenuItem("查找替换(replace)", "repalce");
JMenuItem cut = createMenuItem("剪切(cut)", "cut");
editMrnu.add(copy);
editMrnu.add(paste);
editMrnu.addSeparator();
editMrnu.add(repalce);
editMrnu.addSeparator();
editMrnu.add(cut);
mainMenuBar.add(editMrnu);
//添加事件
copy.addActionListener(actionMenuItemListener);
paste.addActionListener(actionMenuItemListener);
repalce.addActionListener(actionMenuItemListener);
cut.addActionListener(actionMenuItemListener);
//创建帮助菜单
JMenu helpMenu = new JMenu("帮助(help)");
mainMenuBar.add(helpMenu);
//创建子菜单
JMenuItem help = createMenuItem("帮助 (help)", "help");
JMenuItem about = createMenuItem("关于 (about)", "about");
helpMenu.add(help);
helpMenu.addSeparator();
helpMenu.add(about);
//添加子菜单事件
help.addActionListener(actionMenuItemListener);
about.addActionListener(actionMenuItemListener);
}
/**
* @param title
* @param command
* @return javax.swing.JMenuItem
* @Description 创建子菜单项
*/
private JMenuItem createMenuItem(String title, String command) {
JMenuItem item = new JMenuItem(title);
item.setActionCommand(command);
// item.setFont(font);
return item;
}
private class ActionMenuItemListener implements ActionListener {
/**
* @Description 事件分发
* @param e
* @return void
*/
@Override
public void actionPerformed(ActionEvent e) {
//子菜单标识
String actionCommand = e.getActionCommand();
System.out.println(actionCommand);
if (actionCommand.equals("save")){
showSaveWindow(globalThis);
}else if (actionCommand.equals("open")){
showOpenWindow(globalThis);
}else if (actionCommand.equals("saveAs")){
showSaveWindow(globalThis);
}else if (actionCommand.equals("exit")){
System.exit(0);
}else if (actionCommand.equals("help") || actionCommand.equals("about")){
JOptionPane.showMessageDialog(globalThis,"欢迎使用记事本","提示:", PLAIN_MESSAGE);
}else if (actionCommand.equals("copy")){
mainTextArea.copy();
}else if (actionCommand.equals("paste")){
mainTextArea.paste();
}else if (actionCommand.equals("repalce")){
HashMap<String, String> replaceStringHashMap = createReplaceD();
if (null != replaceStringHashMap) {
String mainTextAreaText = mainTextArea.getText();
String replaceAllString = mainTextAreaText.replaceAll(replaceStringHashMap.get("src"), replaceStringHashMap.get("rep"));
mainTextArea.setText(replaceAllString);
}
}else if (actionCommand.equals("cut")){
mainTextArea.cut();
}
}
/**
* @Description 弹出输入框获取替换字符
* @param
* @return java.lang.String
*/
private HashMap<String, String> createReplaceD(){
String inputValue = JOptionPane.showInputDialog(globalThis, "输入要替换的文本: 被替换的文本/替换文本", "提示:", PLAIN_MESSAGE);
if ("".equals(inputValue) || inputValue.isEmpty()) return null;
String[] split = inputValue.split("/");
HashMap<String, String> repalceStringHashMap = new HashMap<>();
if (split.length != 2){
return null;
}
repalceStringHashMap.put("src",split[0]);
repalceStringHashMap.put("rep",split[1]);
return repalceStringHashMap;
}
/**
* @Description 打开选着文件对话框
* @param This
* @return void
*/
public void showOpenWindow(Component This){
//创建文件选着框
JFileChooser selectFileChooser = new JFileChooser();
//创建文件过滤器并设置到文件选则框
FileNameExtensionFilter fileNameExtensionFilter = new FileNameExtensionFilter("文本文件", "txt", "java", "js", "html", "json", "xml", "ini", "conf","md");
selectFileChooser.setFileFilter(fileNameExtensionFilter);
//获取状态码
int selectStatus = selectFileChooser.showOpenDialog(This);
if (selectStatus == JFileChooser.APPROVE_OPTION){
File selectedFile = selectFileChooser.getSelectedFile();
//读入文件到全局文本域
readFileToAreaText(selectedFile.getAbsolutePath(),mainTextArea);
}
}
/**
* @Description 打开保存文件对话框
* @param This
* @return void
*/
private void showSaveWindow(Component This){
//创建文件选着框
JFileChooser saveFileChooser = new JFileChooser();
//创建文件过滤器并设置到文件选则框
FileNameExtensionFilter fileNameExtensionFilter = new FileNameExtensionFilter("文本文件", "txt", "java", "js", "html", "json", "xml", "ini", "conf","md");
saveFileChooser.setFileFilter(fileNameExtensionFilter);
int openStatus = saveFileChooser.showSaveDialog(This);
if (openStatus == JFileChooser.APPROVE_OPTION){
//获取文件的绝对路径写出文件
File selectedFile = saveFileChooser.getSelectedFile();
writeFile(selectedFile.getAbsolutePath(),mainTextArea);
}
}
/**
* @Description 创建一个文件读入流,读入到全局文本域中
* @param filePath
* @param mainTextArea
* @return void
*/
public void readFileToAreaText(String filePath,JTextArea mainTextArea){
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(filePath));
char[] buffer = new char[1024];
int len = 0;
while ((len = bufferedReader.read(buffer)) != -1) {
String str = new String(buffer, 0, len);
mainTextArea.append(new String(str));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedReader != null){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* @Description 创建写出文件流
* @param filePath
* @param mainTextArea
* @return void
*/
public void writeFile(String filePath,JTextArea mainTextArea) {
BufferedOutputStream bufferedOutputStream = null;
try {
//创建一个OutputStream
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePath));
String areaText = mainTextArea.getText();
bufferedOutputStream.write(areaText.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedOutputStream != null){
try {
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
package com.xiaoqi.java;
import javax.swing.*;
import java.lang.reflect.InvocationTargetException;
public class JEditMain {
public static void createGUI() {
EditFrame jEdit = new EditFrame("记事本");
jEdit.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jEdit.setSize(750, 450);
jEdit.setLocationRelativeTo(null);
jEdit.setVisible(true);
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
//直接使用方法引用少了行代码
SwingUtilities.invokeAndWait(JEditMain::createGUI);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (UnsupportedLookAndFeelException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
5. 总结
通过这个简单的文本编辑器项目,我学到了很多关于Swing的知识,同时也意识到还有很多可以改进的地方。比如,我们可以增加更多的功能,如撤销/重做功能、支持多种编码格式等。希望未来能够继续深入学习Swing,开发出更加实用的应用程序。
标签:文本编辑,菜单,void,JMenuItem,mainTextArea,简易,add,Swing,new From: https://blog.csdn.net/love3512986643/article/details/142058672