JAVAFX实现类似记事本简单功能的小程序

需求如下:
基础功能:

  1. 输入文字并显示
  2. 复制粘贴(一般系统都支持)
  3. 保存到本地(存档功能, 下次还能打开)
    进阶功能:(额外加分):
  4. 可以打开外部txt文件
  5. 打开多个txt文件(分页)
  6. 可调节字体大小&颜色

下面是该需求的代码实现:

package com.moyisuiying.view;

import com.moyisuiying.app.FontSettingSage;
import com.moyisuiying.util.Charsetutil;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.*;
import java.util.HashMap;
import java.util.Map;


/**
 * @author 陌意随影
 * @create 2020-03-23 14:35
 * @desc 主面板
 **/
public class NodePadPane extends VBox {
    //头部菜单面板
    private MenuBar  menuBar = null;
    //文件菜单
    private Menu file_menu = null;
    //字体菜单
    private Menu font_menu = null;
    //顶部面板
    private HBox topPane = null;
    //主面板
    private Pane centerPane = null;
    private TextArea  textArea = null;
    private TabPane tabPnae = null;
    private Tab tab = null;
    private FileChooser fileChooser = null;
    private static  Map<String,TextArea> map  = new HashMap<String,TextArea>();
    private static  Map<String,String> filePathMap  = new HashMap<String,String>();
    private     Stage stage = null;
    public  NodePadPane(){
        this.menuBar = new MenuBar();
        this.file_menu  = new Menu("文件(F)");
        this.font_menu = new Menu("字体(C)");
        this.topPane = new HBox();
        this.centerPane = new HBox();
        this.textArea = new TextArea();
        this.tabPnae = new TabPane();
        initToPane();
        initCenterPane();
        topPane.setPrefHeight(30);
        tabPnae.setPrefHeight(50);
        initTabPane();
        this.getChildren().addAll(this.topPane,tabPnae,this.centerPane);
    }
    private  void initTabPane(){
   tabPnae.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>() {
       @Override
       public void changed(ObservableValue<? extends Tab> observable, Tab oldValue, Tab newValue) {
           if (newValue!= null){
               if (map.containsKey(newValue.getText())){
                   centerPane.getChildren().clear();
                   centerPane.getChildren().add(map.get(newValue.getText()));
               }
           }
       }
   });

    }
    private  void createTabByName(String tabName){
        if (tabName == null || tabName.trim().length() == 0) return;
        ObservableList<Tab> tabs = this.tabPnae.getTabs();
        if (tabs != null && tabs.size() > 0){
            for (int i = 0 ;i< tabs.size();i++){
                String text = tabs.get(i).getText();
                if(text.equals(tabName)){
                    this.tabPnae.getSelectionModel().select(i);
                   return;
                }
            }
        }
        Tab tab = new Tab(tabName);
        tab.setOnCloseRequest(new EventHandler<Event>() {
            @Override
            public void handle(Event event) {
               if (filePathMap.containsKey(tab.getText())){
                   filePathMap.remove(tab.getText());
               }
               int index = 0;
               for (int i = 0;i < tabs.size();i++){
                   if (tabs.get(i).getText().equals(tab.getText())){
                       index = i;
                   }
               }
               if (index > 0){
                   tabPnae.getSelectionModel().select(index-1);
               }else{
                   Label label = new Label("请打开一个文件或新建一个文件");
                   label.setStyle("-fx-font-size: 20");
                   centerPane.getChildren().clear();
                   centerPane.getChildren().add(label);
               }
               if (map.containsKey(tab.getText())){
                   map.remove(tab.getText());
               }
            }
        });
        this.tabPnae.getTabs().add(tab);
        tabPnae.getSelectionModel().select(tab);
    }
    private void initCenterPane() {
        textArea.setPrefWidth(800);
        centerPane.setPadding(new Insets(0,1,1,1));
        centerPane.setPrefSize(800,550);
        Label label = new Label("请打开一个文件或新建一个文件");
        label.setStyle("-fx-font-size: 20");
        centerPane.getChildren().add(label);

    }

    private void initToPane() {
         this.topPane.spacingProperty().setValue(15);
        MenuItem item_newFile = new MenuItem("新建一个文件");
        item_newFile.setOnAction(e->{
            ObservableList<Tab> tabs = tabPnae.getTabs();
            String name ="";
             if (tabs == null || tabs.size() == 0){
                 name="new 1";
             }else{
                 int count = 0;
                 for (int i = 0;i < tabs.size();i++){
                     Tab tab1 = tabs.get(i);
                     if (tab1.getText().startsWith("new ")){
                         count ++;
                     }
                 }
                 name="new "+(count+1);
            }
            createTabByName(name);
            createTextArea(name,null);
        });
        MenuItem item_openFile =new MenuItem("打开一个文件");
        item_openFile.setOnAction(e->{
            fileChooser = new FileChooser();
            fileChooser.setInitialDirectory(new File("."));
            File file = fileChooser.showOpenDialog(null);
            if(file == null ){
                return;
            }
            String name = file.getName();
            filePathMap.put(name,file.getAbsolutePath());
            createTabByName(name);
            createTextArea(name,file);
        });
        MenuItem item_saveFile =new MenuItem("保存文件");
        item_saveFile.setOnAction(e->{
            Tab selectedItem = tabPnae.getSelectionModel().getSelectedItem();
            if (selectedItem == null){
                Alert alert  = new Alert(Alert.AlertType.INFORMATION);
                alert.setContentText("您尚未有要保存的文件!");
                alert.show();
                return;
            }
            String fileName = selectedItem.getText();
            String path = null;
            boolean fla = false;
           if (filePathMap.containsKey(fileName)){
               path = filePathMap.get(fileName);
               path = path.substring(0,path.lastIndexOf("\\"));
               fla = true;
           }else{
               path=".";
           }

            fileChooser = new FileChooser();
           if (!fileName.contains(".")){
               fileName =fileName+".txt";
           }
            fileChooser.setInitialDirectory(new File(path));
            fileChooser.setInitialFileName(fileName);
            fileChooser.getExtensionFilters().addAll(
                    new FileChooser.ExtensionFilter("All FILE", "*.*"),
                    new FileChooser.ExtensionFilter("txt", "*.txt")
            );
            File file = fileChooser.showSaveDialog(null);
            if(file == null ){
                return;
            }
            String name = file.getName();
            if (name == null || name.trim().length() == 0){
               return;
            }
            if (!fla){
                fileName = fileName.substring(0,fileName.lastIndexOf("."));
            }
            TextArea textArea = map.get(fileName);
            saveFile(textArea.getText(),file);

        });
        this.file_menu.getItems().addAll(item_newFile,item_openFile,item_saveFile);
        file_menu.setStyle("-fx-font-size: 16");
        font_menu.setStyle("-fx-font-size: 16");
        Menu item_color =new Menu("字体设置");
        item_color.setOnAction(event -> {
           if (stage == null){
               stage = new FontSettingSage(map);
               stage.show();
           }else{
               stage.show();
           }
        });
        this.font_menu.getItems().addAll(item_color);
        this.menuBar.getMenus().addAll(file_menu,this.font_menu);
        this.topPane.getChildren().add(menuBar);
    }

    private void saveFile(String textValue, File file) {
        try (FileOutputStream fileOutputStream = new FileOutputStream(file,false)) {
            byte[] bytes = textValue.getBytes();
            fileOutputStream.write(bytes);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void createTextArea(String name, File file) {
        if (map.containsKey(name)){
            this.centerPane.getChildren().clear();
            this.centerPane.getChildren().add(map.get(name));
            return;
        }

        TextArea textArea = new TextArea();
        if (file != null){
            try (FileInputStream fileInputStream = new FileInputStream(file)) {
                byte[] buff = new byte[1024];
                int len = -1;
                StringBuilder sb = new StringBuilder();
                String filecharset = Charsetutil.getFilecharset(file);
                while ((len = fileInputStream.read(buff)) != -1){
                    sb.append(new String(buff,0,len,filecharset));
                }
                textArea.setText(sb.toString());

            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
         map.put(name,textArea);
        textArea.setPrefWidth(800);
        this.centerPane.getChildren().clear();
        this.centerPane.getChildren().add(textArea);
    }
}

package com.moyisuiying.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;

/**
 * @author 陌意随影
 * @create 2020-03-23 16:39
 * @desc 判断文件字符编码的工具类
 **/
public class Charsetutil {
    //判断编码格式方法
    public static  String getFilecharset(File sourceFile) {
        String charset = "GBK";
        byte[] first3Bytes = new byte[3];
        try {
            boolean checked = false;
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
            bis.mark(0);
            int read = bis.read(first3Bytes, 0, 3);
            if (read == -1) {
                return charset; //文件编码为 ANSI
            } else if (first3Bytes[0] == (byte) 0xFF
                    && first3Bytes[1] == (byte) 0xFE) {
                charset = "UTF-16LE"; //文件编码为 Unicode
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xFE
                    && first3Bytes[1] == (byte) 0xFF) {
                charset = "UTF-16BE"; //文件编码为 Unicode big endian
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xEF
                    && first3Bytes[1] == (byte) 0xBB
                    && first3Bytes[2] == (byte) 0xBF) {
                charset = "UTF-8"; //文件编码为 UTF-8
                checked = true;
            }
            bis.reset();
            if (!checked) {
                int loc = 0;
                while ((read = bis.read()) != -1) {
                    loc++;
                    if (read >= 0xF0)
                        break;
                    if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的,也算是GBK
                        break;
                    if (0xC0 <= read && read <= 0xDF) {
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
                            // (0x80
                            // - 0xBF),也可能在GB编码内
                            continue;
                        else
                            break;
                    } else if (0xE0 <= read && read <= 0xEF) {// 也有可能出错,但是几率较小
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) {
                            read = bis.read();
                            if (0x80 <= read && read <= 0xBF) {
                                charset = "UTF-8";
                                break;
                            } else
                                break;
                        } else
                            break;
                    }
                }
            }
            bis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return charset;
    }
}

package com.moyisuiying.app;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.Map;
/**
 * @author 陌意随影
 * @create 2020-03-26 13:14
 * @desc 字体设置面板
 **/
public class FontSettingSage  extends Stage {
    private   Map<String,TextArea> map = null;
    private VBox box = null;
    private ColorPicker colorPicker = null;
    private ComboBox<Integer> comboBox = null;
    private Scene scene = null;
    private Button btn_ok = null;
    private HBox btnBox = null;
    public FontSettingSage( Map<String,TextArea> map){
        this.map = map;
        this.box = new VBox();
        this.colorPicker = new ColorPicker(Color.web("#1A1A1A"));
        this.comboBox = new ComboBox<>();
        this.btn_ok = new Button("确定");
        this.btnBox = new HBox();
        installComponents();
        this.scene = new Scene(box,400,400);
        this.setScene(scene);
    }

    private void installComponents() {
        this.comboBox.setEditable(true);
        this.comboBox.setValue(14);
        for (int i = 1;i < 60;i++){
            this.comboBox.getItems().add(i);
        }
        this.btn_ok.setOnAction(e->{
            String hex = Integer.toHexString(colorPicker.getValue().hashCode());
            String str = "-fx-text-fill:#"+hex;
                for (Map.Entry<String,TextArea> entry:map.entrySet()){
                    entry.getValue().setStyle("-fx-font-size: "+comboBox.getSelectionModel().getSelectedItem()+";"+str);
            }
                FontSettingSage.this.hide();
        });

        this.btnBox.getChildren().addAll(btn_ok);
        Label label_size = new Label("字体大小:");
        label_size.setStyle("-fx-font-size: 16;");
        Label label_color = new Label("字体颜色");
        label_color.setStyle("-fx-font-size: 16;");
        HBox fontSizeBox = new HBox();
        fontSizeBox.getChildren().addAll(label_size,comboBox);
        HBox fontColorBox = new HBox();
        fontColorBox.getChildren().addAll(label_color,colorPicker);
       this.box.getChildren().addAll(fontSizeBox,fontColorBox,btnBox);
    }


}

package com.moyisuiying.app;

import com.moyisuiying.view.NodePadPane;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

/**
 * @author 陌意随影
 * @create 2020-03-23 14:34
 * @desc 程序入口
 **/
public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
    primaryStage.setTitle("NotePad");
        NodePadPane nodePadPane = new NodePadPane();
        Scene scene = new Scene(nodePadPane,800,600);
        primaryStage.setScene(scene);
        primaryStage.show();
        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent event) {
                System.exit(0);
            }
        });
    }

    public static void main(String[] args) {
        launch(args);
    }
}

该小程序的直接可执行文件已分享在百度云网盘,有需要的可直接获取:
百度云网盘链接
提取码:s93w

顺便附上该小程序项目的GitHub地址:
GitHub地址

程序效果图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陌意随影

您的鼓励是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值