JavaFX记事本

  • JavaFX实现记事本
  • 一、需求说明
  • 二、功能演示
  • 三、 设计流程
  • 四、实现代码


JavaFX实现记事本

一、需求说明

记事本程序可实现以下功能:

  1. 文件新建
  2. 文件保存
  3. 文件打开
  4. 撤销,回复
  5. 灰度
  6. 查找
  7. 替换
  8. 字体
  9. 修改前保
  10. 状态栏
  11. 右键
  12. 自动换行

二、功能演示

  1. 文件新建
  2. Java防记事本 javafx记事本_Java防记事本

  3. 文件保存
  4. Java防记事本 javafx记事本_Java防记事本_02

  5. 文件打开
  6. Java防记事本 javafx记事本_java_03

  7. 撤销,回复
  8. Java防记事本 javafx记事本_Java防记事本_04


  9. Java防记事本 javafx记事本_字符串_05


  10. Java防记事本 javafx记事本_自动换行_06

  11. 灰度
    在字体框中没有内容时,无法使用查找与替换功能
  12. Java防记事本 javafx记事本_自动换行_07


  13. Java防记事本 javafx记事本_字符串_08

  14. 查找
  15. Java防记事本 javafx记事本_java_09

  16. 替换
  17. Java防记事本 javafx记事本_Java防记事本_10


  18. Java防记事本 javafx记事本_Java防记事本_11

  19. 字体
    整体框图
    字体->选择字体
    字形->选择粗体和斜体
    大小->选择字号
    示例->展示当前所选的字体是什么样字
  20. Java防记事本 javafx记事本_字符串_12


  21. Java防记事本 javafx记事本_java_13


  22. Java防记事本 javafx记事本_字符串_14


  23. Java防记事本 javafx记事本_字符串_15

  24. 修改前保
    如果文本框中有内容但无存储路径,则在打开和新建之前将询问用户是否保存
    如果文本框中有内容且有存储路径,则在打开和新建之前将替用户将内容保存到当前路径下的txt文件中
  25. Java防记事本 javafx记事本_Java防记事本_16

  26. 状态栏
    可以选择是否可视,状态栏显示当前光标位于第几个字符的位置
  27. Java防记事本 javafx记事本_自动换行_17

  28. 右键
    在文本框点击鼠标右键可出现右键菜单
  29. Java防记事本 javafx记事本_Java防记事本_18

  30. 自动换行
    可选是否自动换行,若选择自动换行则超出文本框显示部分的文本将自动显示到记事本下一行的位置,效果如下图
  31. Java防记事本 javafx记事本_字符串_19


  32. Java防记事本 javafx记事本_自动换行_20

三、 设计流程

结构图

Java防记事本 javafx记事本_java_21

界面设计

界面设计使用JavaFX Scene Builder 2.0完成

记事本主界面设计

Java防记事本 javafx记事本_java_22


Java防记事本 javafx记事本_java_23


Java防记事本 javafx记事本_java_24

字体选择框设计

Java防记事本 javafx记事本_Java防记事本_25


Java防记事本 javafx记事本_Java防记事本_26


Java防记事本 javafx记事本_自动换行_27

功能实现

Java防记事本 javafx记事本_自动换行_28

实现对txt文件的读取操作

Java防记事本 javafx记事本_自动换行_29

实现记事本的主要功能

Java防记事本 javafx记事本_自动换行_30

实现记事本字体选择功能

Java防记事本 javafx记事本_Java防记事本_31

加载记事本场景

Java防记事本 javafx记事本_自动换行_32

启动场景

四、实现代码

注释中说明了每段代码的功能

Java防记事本 javafx记事本_自动换行_28

package application;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;

public class FileTools {

	//读文件
	public static String readFile(File file) {
		StringBuilder resultStr = new StringBuilder();
		try {
			BufferedReader bReader = new BufferedReader(new FileReader(file));
			String line = bReader.readLine();
			while (line != null) {
				resultStr.append(line+"\n");
				line = bReader.readLine();
			}
			bReader.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return resultStr.toString();
	}

	//写文件
	public static void writeFile(File file, String str) {
		try {
			BufferedWriter bWriter = new BufferedWriter(new FileWriter(file));
			bWriter.write(str);
			bWriter.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Java防记事本 javafx记事本_自动换行_29

package application;

import java.io.File;
import java.io.IOException;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

public class NoteBookController {

	@FXML
	private MenuItem SaveMenu;
	@FXML
	private MenuItem FindMenu;
	@FXML
	private CheckMenuItem WrapMenu;
	@FXML
	private AnchorPane layoutPane;
	@FXML
	private MenuItem ReplaceMenu;
	@FXML
	private CheckMenuItem StateMenu;
	@FXML
	private MenuItem OpenMenu;
	@FXML
	private MenuItem TypefaceMenu;
	@FXML
	private MenuItem NewMenu;
	@FXML
	private TextArea ta;
	@FXML
	private Label label;
	@FXML
	private MenuItem Redo;
	@FXML
	private MenuItem Undo;

	private File result;

	// 开始搜索的位置
	int startIndex = 0;
	// textarea中光标的位置
	int position = 0;
	
	// 灰度控制
	public void initialize() {
		// 初始状态下不可使用查找与替换功能
		FindMenu.setDisable(true);
		ReplaceMenu.setDisable(true);
		//初始状态不可使用撤销和重做功能
		Redo.setDisable(true);
		Undo.setDisable(true);
		
		// 状态栏不可视
		label.setVisible(false);

		ta.setOnMouseClicked(e -> {
			if (e.getButton() == MouseButton.PRIMARY) {
				position = ta.getCaretPosition();
				label.setText("第" + position + "个字符");
			}
		});

		// 对textarea的内容是否改变进行监听
		ta.textProperty().addListener(new ChangeListener<String>() {
			@Override
			public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
				// 如果testarea中内容部位空,则可以使用查找与替换
				if (ta.getLength() > 0) {
					FindMenu.setDisable(false);
					ReplaceMenu.setDisable(false);
				} // 否则禁用查找与替换
				else {
					FindMenu.setDisable(true);
					ReplaceMenu.setDisable(true);
				}
				Redo.setDisable(false);
				Undo.setDisable(false);
				// 光标位置
				position = ta.getCaretPosition();
				label.setText("第" + position + "个字符");
			}
		});
	}

	// 修改前保
	void saveadvance() {
		if (result != null && ta.getLength() > 0) {
			
			FileTools.writeFile(result, ta.getText());
		} else if (result == null && ta.getLength() > 0) {
			FileChooser fileChooser = new FileChooser();
			FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt");
			fileChooser.getExtensionFilters().add(extFilter);
			fileChooser.setTitle("保存当前内容");
			result = fileChooser.showSaveDialog(null);
			if (result != null) {
				FileTools.writeFile(result, ta.getText());
			}
		}
	}

	// 新建功能
	@FXML
	void onNewMenu(ActionEvent event) {

		// 新建前保存
		saveadvance();

		ta.clear();
		result = null;
	}

	// 打开功能
	@FXML
	void onOpenMenu(ActionEvent event) {

		// 打开前保存
		saveadvance();

		FileChooser fileChooser = new FileChooser();
		FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt");
		fileChooser.getExtensionFilters().add(extFilter);
		result = fileChooser.showOpenDialog(null);
		if (result != null) {
			ta.setText(FileTools.readFile(result));
		}
	}

	// 保存功能
	@FXML
	void onSaveMenu(ActionEvent event) throws IOException {
		if (result != null)// 如果已经存在保存路径
		{
			FileTools.writeFile(result, ta.getText());
		} else// 如果不存在保存的路径
		{
			FileChooser fileChooser = new FileChooser();
			FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt");
			fileChooser.getExtensionFilters().add(extFilter);
			result = fileChooser.showSaveDialog(null);
			if (result != null) {
				FileTools.writeFile(result, ta.getText());
			}
		}
	}

	//撤销
	@FXML
	void onUndoMenu(ActionEvent event) {
		ta.undo();
		Undo.setDisable(true);
	}

	//重做
	@FXML
	void onRedoMenu(ActionEvent event) {
		ta.redo();
		Redo.setDisable(true);
	}

	// 查找功能
	@FXML
	void onFindMenu(ActionEvent event) throws IOException {
		HBox h1 = new HBox();
		h1.setPadding(new Insets(20, 5, 20, 5));
		h1.setSpacing(5);
		Label lable1 = new Label("查找内容(N):");
		TextField tf1 = new TextField();
		h1.getChildren().addAll(lable1, tf1);

		VBox v1 = new VBox();
		v1.setPadding(new Insets(20, 5, 20, 10));
		Button btn1 = new Button("查找下一个(F)");
		v1.getChildren().add(btn1);

		HBox findRootNode = new HBox();
		findRootNode.getChildren().addAll(h1, v1);

		Stage findStage = new Stage();
		Scene scene1 = new Scene(findRootNode, 450, 90);
		findStage.setTitle("查找");
		findStage.setScene(scene1);
		findStage.setResizable(false); // 固定窗口大小
		findStage.show();

		btn1.setOnAction((ActionEvent e) -> {
			String textString = ta.getText(); // 获取记事本文本域的字符串
			String tfString = tf1.getText(); // 获取要查找的字符串
			if (!tf1.getText().isEmpty()) {
				if (textString.contains(tfString)) {
					// 查找方法
					if (startIndex == -1) {// not found
						Alert alert1 = new Alert(AlertType.WARNING);
						alert1.titleProperty().set("提示");
						alert1.headerTextProperty().set("已经找不到相关内容了!");
						alert1.show();
					}
					startIndex = ta.getText().indexOf(tf1.getText(), startIndex);
					if (startIndex >= 0 && startIndex < ta.getText().length()) {
						ta.selectRange(startIndex, startIndex + tf1.getText().length());
						startIndex += tf1.getText().length();
					}
				}
				if (!textString.contains(tfString)) {
					Alert alert1 = new Alert(AlertType.WARNING);
					alert1.titleProperty().set("提示");
					alert1.headerTextProperty().set("找不到相关内容!");
					alert1.show();
				}
			} else if (tf1.getText().isEmpty()) {
				Alert alert1 = new Alert(AlertType.WARNING);
				alert1.titleProperty().set("出错了");
				alert1.headerTextProperty().set("输入内容为空");
				alert1.show();
			}
		});
	}

	// 替换功能
	@FXML
	void onReplaceMenu(ActionEvent event) throws IOException {
		HBox h1 = new HBox();
		h1.setPadding(new Insets(20, 5, 10, 8));
		h1.setSpacing(5);
		Label label1 = new Label("查找下一个(F)");
		TextField tf1 = new TextField();
		h1.getChildren().addAll(label1, tf1);

		HBox h2 = new HBox();
		h2.setPadding(new Insets(5, 5, 20, 8));
		h2.setSpacing(5);
		Label label2 = new Label("替换内容(N):");
		TextField tf2 = new TextField();
		h2.getChildren().addAll(label2, tf2);

		VBox v1 = new VBox();
		v1.getChildren().addAll(h1, h2);

		VBox v2 = new VBox();
		v2.setPadding(new Insets(21, 5, 20, 10));
		v2.setSpacing(13);
		Button btn1 = new Button("查找下一个");
		Button btn2 = new Button("替换为");
		v2.getChildren().addAll(btn1, btn2);

		HBox replaceRootNode = new HBox();
		replaceRootNode.getChildren().addAll(v1, v2);

		Stage replaceStage = new Stage();
		Scene scene = new Scene(replaceRootNode, 430, 120);
		replaceStage.setTitle("替换");
		replaceStage.setScene(scene);
		replaceStage.setResizable(false); // 固定窗口大小
		replaceStage.show();

		btn1.setOnAction((ActionEvent e) -> {
			String textString = ta.getText(); // 获取记事本文本域的字符串
			String tfString = tf1.getText(); // 获取查找内容的字符串
			if (!tf1.getText().isEmpty()) {
				if (textString.contains(tfString)) {
					if (startIndex == -1) {// not found
						Alert alert1 = new Alert(AlertType.WARNING);
						alert1.titleProperty().set("提示");
						alert1.headerTextProperty().set("已经找不到相关内容了!");
						alert1.show();
					}
					startIndex = ta.getText().indexOf(tf1.getText(), startIndex);
					if (startIndex >= 0 && startIndex < ta.getText().length()) {
						ta.selectRange(startIndex, startIndex + tf1.getText().length());
						startIndex += tf1.getText().length();
					}
					btn2.setOnAction((ActionEvent e2) -> {
						ta.replaceSelection(tf2.getText());
					});
				}
				if (!textString.contains(tfString)) {
					Alert alert1 = new Alert(AlertType.WARNING);
					alert1.titleProperty().set("提示");
					alert1.headerTextProperty().set("找不到相关内容!");
					alert1.show();
				}

			} else if (tf1.getText().isEmpty()) {
				Alert alert1 = new Alert(AlertType.WARNING);
				alert1.titleProperty().set("出错了");
				alert1.headerTextProperty().set("输入内容为空");
				alert1.show();
			}
		});
	}

	// 自动换行
	@FXML
	void onWrapMenu(ActionEvent event) {
		if (WrapMenu.isSelected())
			ta.setWrapText(true);
		else
			ta.setWrapText(false);
	}

	// 字体
	@FXML
	void onTypefaceMenu(ActionEvent event) throws IOException {
		Stage stage = new Stage();
		FXMLLoader loader = new FXMLLoader(getClass().getResource("Typeface.fxml"));
		Parent parent = loader.load();
		Scene scene = new Scene(parent);
		stage.setScene(scene);
		// 等待窗口被关闭在做出下一步反应
		stage.showAndWait();
		if (scene.getUserData() != null)// 如果用户有设定字体的样式,则将记事本中的字体改为该样式
		{
			Font font = (Font) scene.getUserData();
			ta.setFont(font);
		}
	}

	// 状态栏
	@FXML
	void onStateMenu(ActionEvent event) {
		if (StateMenu.isSelected())
			label.setVisible(true);
		else
			label.setVisible(false);
	}

}

Java防记事本 javafx记事本_自动换行_30

package application;

import java.util.List;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextArea;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;

public class TypefaceController {
	//字体
    @FXML
    private ComboBox<String> Cb1;
    //字形
    @FXML
    private ComboBox<String> Cb2;
    //大小
    @FXML
    private ComboBox<String> Cb3;
    @FXML
    private Button SureBtn;
    @FXML
    private TextArea ta;
    
    public Font Txtfont=Font.font(12);
    private Font font=Font.font(12);
    
    //字体
    String type="宋体";
    //字形
    String special="常规";
    //大小
    int size=12;
    
    @FXML
    void initialize() {
    	
    List<String> getFontName = Font.getFontNames();
    Cb1.getItems().addAll(getFontName);
    
    Cb2.getItems().addAll("常规","粗体","斜体","粗斜体");
    
    for(int i=2;i<=32;i=i+2)
    {
    	Cb3.getItems().add(i+"");
    }
    
    Cb1.setOnAction(e->{
        type = Cb1.getValue();
        setFont();
    });
    
    Cb2.setOnAction(e->{
        special = Cb2.getValue();
        setFont();
    });
    
    Cb3.setOnAction(e->{
        size = Integer.parseInt(Cb3.getValue());
        setFont();
    });
    }
    
    //设置字体样式
    public void setFont() {
    	font = Font.font(type, size);
        if(special.equals("常规"))
        font=Font.font(type, FontWeight.NORMAL, FontPosture.REGULAR,size);//常规
        else if(special.equals("粗体"))
        font=Font.font(type, FontWeight.BOLD, FontPosture.REGULAR,size);//粗体
        else if(special.equals("斜体"))
        font=Font.font(type, FontWeight.NORMAL, FontPosture.ITALIC,size);//斜体
        else if(special.equals("粗斜体"))
        font=Font.font(type, FontWeight.BOLD, FontPosture.ITALIC,size);//粗斜体     
        ta.setFont(font);
    }
    
    
    @FXML
    void onSureBtn(ActionEvent event) {
    	Txtfont=font; 
        Scene scene = SureBtn.getScene();
        //把选定的字体记录到用户数据中
        scene.setUserData(Txtfont); 	
    	//点击确定后,关闭窗口
    	Stage stage=(Stage)SureBtn.getScene().getWindow();
    	stage.close();
    }

}

Java防记事本 javafx记事本_java_22

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

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

<BorderPane prefHeight="461.0" prefWidth="699.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.NoteBookController">
   <center>
      <TextArea fx:id="ta" prefHeight="434.0" prefWidth="658.0" BorderPane.alignment="CENTER" />
   </center>
   <top>
      <MenuBar BorderPane.alignment="CENTER">
        <menus>
          <Menu mnemonicParsing="false" text="文件">
            <items>
              <MenuItem fx:id="NewMenu" mnemonicParsing="false" onAction="#onNewMenu" text="新建" />
                  <MenuItem fx:id="OpenMenu" mnemonicParsing="false" onAction="#onOpenMenu" text="打开" />
                  <MenuItem fx:id="SaveMenu" mnemonicParsing="false" onAction="#onSaveMenu" text="保存" />
            </items>
          </Menu>
          <Menu mnemonicParsing="false" text="编辑">
            <items>
              <MenuItem fx:id="FindMenu" mnemonicParsing="false" onAction="#onFindMenu" text="查找" />
                  <MenuItem fx:id="ReplaceMenu" mnemonicParsing="false" onAction="#onReplaceMenu" text="替换" />
                  <MenuItem fx:id="Undo" mnemonicParsing="false" onAction="#onUndoMenu" text="撤销" />
                  <MenuItem fx:id="Redo" mnemonicParsing="false" onAction="#onRedoMenu" text="重做" />
            </items>
          </Menu>
            <Menu mnemonicParsing="false" text="格式">
              <items>
                  <CheckMenuItem fx:id="WrapMenu" mnemonicParsing="false" onAction="#onWrapMenu" text="自动换行" />
                <MenuItem fx:id="TypefaceMenu" mnemonicParsing="false" onAction="#onTypefaceMenu" text="字体..." />
              </items>
            </Menu>
            <Menu mnemonicParsing="false" text="查看">
               <items>
                  <CheckMenuItem fx:id="StateMenu" mnemonicParsing="false" onAction="#onStateMenu" text="状态栏" />
               </items>
            </Menu>
        </menus>
      </MenuBar>
   </top>
   <bottom>
      <Label fx:id="label" text="状态栏" BorderPane.alignment="CENTER" />
   </bottom>
</BorderPane>

Java防记事本 javafx记事本_Java防记事本_25

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

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

<AnchorPane prefHeight="222.0" prefWidth="448.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.TypefaceController">
   <children>
      <ComboBox fx:id="Cb1" layoutX="50.0" layoutY="16.0" prefHeight="30.0" prefWidth="96.0" promptText="字体" />
      <ComboBox fx:id="Cb2" layoutX="182.0" layoutY="16.0" prefHeight="30.0" prefWidth="96.0" promptText="字形" />
      <ComboBox fx:id="Cb3" layoutX="319.0" layoutY="16.0" prefHeight="30.0" prefWidth="88.0" promptText="大小" />
      <TextArea fx:id="ta" editable="false" focusTraversable="false" layoutX="14.0" layoutY="91.0" mouseTransparent="true" prefHeight="116.0" prefWidth="264.0" text="个人记事本
AaBbYyZz" />
      <Label layoutX="14.0" layoutY="61.0" text="示例" />
      <Button fx:id="SureBtn" layoutX="328.0" layoutY="177.0" mnemonicParsing="false" onAction="#onSureBtn" prefHeight="30.0" prefWidth="71.0" text="确定" />
   </children>
</AnchorPane>