需求如下:
基础功能:
- 输入文字并显示
- 复制粘贴(一般系统都支持)
- 保存到本地(存档功能, 下次还能打开)
进阶功能:(额外加分): - 可以打开外部txt文件
- 打开多个txt文件(分页)
- 可调节字体大小&颜色
下面是该需求的代码实现:
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地址
程序效果图: