自制文本文件编辑器

Text(6) Text Editor ——自制文本编辑器

题目描述:

Text Editor – Notepad style application that can open, edit, and save text documents. Add syntax highlighting and other features.

题目翻译:

文本编辑器——编写一个笔记本类型的应用,可以打开,编辑和保存文本文档(text文档)。可以添加一些语法高亮和其他的特性。

实现过程:

基于java的GUI框架javaFX来实现整个编辑器的功能。

使用java的textArea组件作为文本编辑器的编辑区,并且textArea已经集成了基本的文本编辑操作(复制,剪切,粘贴,撤销,恢复,全选),所以编辑器的实现重点是文本文件的新建(create),保存(save)和另存为(save as)

接下来按照前端界面(front-end)和后端(back-end)讲述文本编辑器的实现。

javaFX框架简介

javaFX框架可以用来开发跨平台的GUI应用程序。

javaFX框架可以基于MVC模式来构建GUI应用程序。

javaFX框架使用FXML文件来定义用户界面。(View层,展示数据),使用Controller来操作前端上的数据。

1.1 前端设计

整个编辑器主界面采用BorderPane布局(top,center,left,right,bottom五个子节点)。

top节点为菜单栏(MenuBar)

center节点设置为编辑区(editArea)

bottom节点设置为尾部状态栏(state)

left节点暂定为行号显示栏(暂时未实现)

image-20201117173652536

对于菜单栏:包括菜单,编辑,保存,字体调整等菜单(Menu)

菜单:

  1. 打开文件

  2. 新建文件

编辑:(javaFx的TextArea以及集成了全部功能,无须自己实现)

  1. 复制(copy)
  2. 剪切(cut)
  3. 粘贴(paste)
  4. 撤销(undo)
  5. 恢复(consume)
  6. 高亮(highlight)

保存:

  1. 保存文件
  2. 另存为文件

字体调整:

设置三个级别的字体大小(small,middle,large)

整个编辑器界面对应的fxml文件如下:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="com.sunfulv.ViewController"
            fx:id="borderPane"
            prefHeight="400.0" prefWidth="600.0">
    <top>
        <MenuBar>
            <Menu id="fileMenu" text="菜单">
                <MenuItem fx:id="openMenu" text="打开文件" onAction="#openFileAction"></MenuItem>
                <MenuItem id="createMenu" text="新建文件" onAction="#createFileAction"></MenuItem>
            </Menu>
            <Menu id="editMenu" text="编辑">
                <MenuItem fx:id="copyMenu" text="复制" onAction="#copyMenuAction"></MenuItem>
                <MenuItem id="cutMenu" text="剪切"></MenuItem>
                <MenuItem id="pasteMenu" text="粘贴"></MenuItem>
                <MenuItem id="undo" text="撤销"></MenuItem>
                <MenuItem id="resumeMenu" text="恢复"></MenuItem>
            </Menu>
            <Menu id="saveMenu" text="保存" >
                <MenuItem id="save" text="保存文件" onAction="#saveFileAction"></MenuItem>
                <MenuItem id="saveAs" text="另存为" onAction="#saveAsFileAction"></MenuItem>
            </Menu>
            <Menu id="fontMenu" text="字体大小">
                <MenuItem id="smallFont" text="小字" onAction="#smallFontAction"></MenuItem>
                <MenuItem id="middleFont" text="中号字体" onAction="#middleFontAction"></MenuItem>
                <MenuItem id="largeFont" text="大号字体" onAction="#lagreFontAction"></MenuItem>
            </Menu>
        </MenuBar>
    </top>
    <center>
        <TextArea fx:id="textArea" text="Hello World">
        </TextArea>
    </center>
    <bottom >
        <AnchorPane snapToPixel="true"></AnchorPane>
    </bottom>
    <left>
        <
    </left>
</BorderPane>

1.2 后端设计

在设计完前端界面时,会为每个菜单绑定(onAction)事件响应方法,我们需要在后端实现这些事件响应方法。

在javaFX框架中,我们将这些事件响应方法统一定义在自定义的ViewController类中。同时在fxml文件中引入这个类。

首先在ViewController类中定义一个File实例,用于代表当前正被激活的文本文件(当前没有打开的文件时,File实例默认为null)

在ViewController类中,使用@FXML注解来导入在fxml文件中定义的容器和组件

这里导入在fxml中定义的布局组件(borderPane)和文本区域组件(textArea)

@FXML private BorderPane borderPane;
@FXML private TextArea textArea;

在定义事件响应方法时,方法名要和fxml中各个菜单组件绑定的响应方法名相同,

在响应方法前添加@FXML注解, 可以将两者绑定起来。

package com.sunfulv;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Optional;


public class ViewController {


    File currentFile = null;//始终代表当前编辑框中对应的文件
    @FXML private BorderPane borderPane;
    @FXML private TextArea textArea;
    //打开文件
    @FXML protected void openFileAction(ActionEvent event) throws IOException {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("打开所选文件");
        fileChooser.getExtensionFilters().addAll( //添加文件过滤器,默认只允许文本文件
                new FileChooser.ExtensionFilter("文本文件","*.txt"),
                new FileChooser.ExtensionFilter("全部文件","*.*")
        );

        Stage currentStage = (Stage)borderPane.getScene().getWindow();
        currentFile = fileChooser.showOpenDialog(currentStage);//读取选中的文件
        if(currentFile == null) return; //如果没有选择要打开的文件,直接返回
        currentStage.setTitle("Text Editor-"+currentFile.getName());
        //System.out.println(textFile.getName());

        FileInputStream fin= null;
        Reader fr = null;
        try {
            fin= new FileInputStream(currentFile);
            fr = new InputStreamReader(fin, StandardCharsets.UTF_8); //默认使用utf8格式打开
            String str = null;
            char[] byteArray = new char[(int) currentFile.length()];
            fr.read(byteArray);
            str = new String(byteArray);
            System.out.println(str);
            textArea.setText(str);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            fr.close();
        }
    }

    // 新建文件
    @FXML protected void createFileAction(ActionEvent event) throws IOException{
        //新建一个文件前,首先确认是否保存当前正在编辑的文件
        Alert alert = new Alert(Alert.AlertType.INFORMATION,"是否保存当前文件!",new ButtonType("是", ButtonBar.ButtonData.YES),new ButtonType("否",ButtonBar.ButtonData.NO));
        alert.setHeaderText("提示信息");
        Optional<ButtonType> buttonType = alert.showAndWait(); //显示对话框,并等待返回,用于确定对话框选择的是确认按钮还是取消按钮
        if(buttonType.get().getButtonData().equals(ButtonBar.ButtonData.YES)){ //如果选择的是,表明要保存当前正在编辑的文件
            ////判断当前编辑框中没有打开的文件,如果没有,直接清空当前编辑框
            if(currentFile !=null)
                saveFile(currentFile);
        }else{
            return;
        }

        textArea.setText(""); //清空当前编辑框
        currentFile = null;
        Stage currentStage = (Stage)borderPane.getScene().getWindow();
        currentStage.setTitle("Text Editor-"+"未命名文件");

    }

    // 保存文件
    @FXML protected void saveFileAction(ActionEvent event) throws IOException{
        //保存当前编辑器中的内容
        if(currentFile !=null) //如果当前有打开的文件,可以进行保存,否则将当前编辑框中的内容另存为一个新的文件
            saveFile(currentFile);
        else {
            if(saveAsFile()) {
                Stage currentStage = (Stage) borderPane.getScene().getWindow();
                currentStage.setTitle("Text Editor-" + currentFile.getName());
            }
        }
    }
    //文件另存为
    @FXML protected void saveAsFileAction(ActionEvent event) throws IOException{
        saveAsFile();
    }

    //字体大小调整

    @FXML protected void smallFontAction(ActionEvent event){
        textArea.setStyle("-fx-font-size: 20px");
    }

    @FXML protected void middleFontAction(ActionEvent event){
        textArea.setStyle("-fx-font-size: 40px");
    }

    @FXML protected void lagreFontAction(ActionEvent event){
        textArea.setStyle("-fx-font-size: 60px");
    }
    //保存当前正在编辑的文件(将编辑框中的字符串保存到对应的文件中)
    private boolean saveFile(File currentFile) throws IOException{
        String currentStr = textArea.getText();//读取编辑框中的内容
        Writer writer = null;
        try {
            writer = new FileWriter(currentFile);
            char[] byteArray = currentStr.toCharArray();
            writer.write(byteArray);
    //            System.out.println(new String(byteArray));
            return true;
        }catch(Exception e){
            e.printStackTrace();
            return false;
        }finally{
            writer.close();
        }
    }

    // 将当前编辑框中的内容另存为一个新的文件
    protected boolean saveAsFile() throws IOException{
        //新建一个txt文件
        FileChooser createFileChooser = new FileChooser();
        createFileChooser.setTitle("另存为");
        createFileChooser.getExtensionFilters().addAll( //添加文件过滤器,默认只允许文本文件
                new FileChooser.ExtensionFilter("文本文件","*.txt"),
                new FileChooser.ExtensionFilter("全部文件","*.*")
        );
        Stage currentStage = (Stage)borderPane.getScene().getWindow();
        File newFile = createFileChooser.showSaveDialog(currentStage);
        if(newFile == null) //如果没有选择要将文件另存为,直接返回
            return false;
        saveFile(newFile);
        currentFile = newFile;
        System.out.println(newFile.getName());
        return true;
    }
}





posted @ 2020-11-17 20:36  sunfulv  阅读(991)  评论(0编辑  收藏  举报