一、项目结构
JDK8.0
domain是记事本的各个组件,main是入口类包,utils存放处理文本框的工具类,resource存放记事本资源,为避免不必要的资源路径问题,本文不提供resource下的文件
二、domain包—记事本各个组件
2.1、MenuBarFactory菜单组件
菜单效果图
代码
MenuBarFactory.java
package domain;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import utils.TextAreaUtils;
import java.awt.print.PageFormat;
import java.awt.print.PrinterJob;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @ClassName MenuButtonFactory
* @Description TODO
* @Author Mr_X
* @Date 2022/5/20 9:26
* @Version 1.0
*/
public class MenuBarFactory {
public StringBuffer originalPath;
private SearchPaneFactory searchPaneFactory = new SearchPaneFactory();
private TextAreaFactory textAreaFactory = new TextAreaFactory();
private TextArea textArea = new TextArea();
private final Menu fileMenu = new Menu("文件(F)");
private final MenuItem newItem = new MenuItem("新建(N)");
private final MenuItem openItem = new MenuItem("打开(O)...");
private final MenuItem saveItem = new MenuItem("保存(S)");
private final MenuItem aItem = new MenuItem("另存为(A)...");
private final SeparatorMenuItem separator0 = new SeparatorMenuItem();
private final MenuItem uItem = new MenuItem("页面设置(U)...");
private final MenuItem printItem = new MenuItem("打印(P)...");
private final SeparatorMenuItem separator1 = new SeparatorMenuItem();
private final MenuItem exitItem = new MenuItem("退出(X)");
private final Menu editMenu = new Menu("编辑(E)");
private final MenuItem undoItem = new MenuItem("撤销(U)");
private final MenuItem tItem = new MenuItem("剪切(T)");
private final MenuItem copyItem = new MenuItem("复制(C)");
private final MenuItem pasteItem = new MenuItem("粘贴(P)");
private final MenuItem delItem = new MenuItem("删除(L)");
private final MenuItem exploreItem = new MenuItem("使用Bing搜索");
private final MenuItem findItem = new MenuItem("查找(F)");
private final MenuItem nextFindItem = new MenuItem("查找下一个(N)");
private final MenuItem prevFindItem = new MenuItem("查找上一个(V)");
private final MenuItem replaceItem = new MenuItem("替换(R)...");
private final MenuItem forwardItem = new MenuItem("转到(G)...");
private final MenuItem selectAllItem = new MenuItem("全选(A)");
private final MenuItem dateItem = new MenuItem("时间/日期(D)");
private final SeparatorMenuItem separator3 = new SeparatorMenuItem();
private final SeparatorMenuItem separator4 = new SeparatorMenuItem();
private final SeparatorMenuItem separator5 = new SeparatorMenuItem();
private final Menu formatMenu = new Menu("格式(O)");
private final RadioMenuItem wrapItem = new RadioMenuItem("自动换行(W)");
private final MenuItem fontStyleItem = new MenuItem("字体(F)...");
private final Menu viewMenu = new Menu("查看(V)");
private final Menu zoomMenu = new Menu("缩放(Z)");
private final RadioMenuItem stateItem = new RadioMenuItem("状态栏(S)");
private final MenuItem largeItem = new MenuItem("放大(I)");
private final MenuItem smallItem = new MenuItem("缩小(O)");
private final MenuItem defaultSizeItem = new MenuItem("恢复默认缩放");
private final Menu helpMenu = new Menu("帮助(H)");
private final MenuItem searchForHelpItem = new MenuItem("查看帮助(H)");
private final MenuItem sendFeedbackItem = new MenuItem("发送反馈(F)");
private final MenuItem aboutItem = new MenuItem("关于记事本(A)");
private final SeparatorMenuItem separator6 = new SeparatorMenuItem();
private final MenuBar menuBar = new MenuBar(fileMenu, editMenu, formatMenu, viewMenu, helpMenu);
/*
初始化,设置菜单栏和菜单项的从属关系,绑定快捷键
*/
{
fileMenu.setAccelerator(KeyCombination.valueOf("alt+f"));
newItem.setAccelerator(KeyCombination.valueOf("CTRL+N"));
openItem.setAccelerator(KeyCombination.valueOf("CTRL+O"));
saveItem.setAccelerator(KeyCombination.valueOf("CTRL+S"));
aItem.setAccelerator(KeyCombination.valueOf("CTRL+SHIFT+S"));
printItem.setAccelerator(KeyCombination.valueOf("CTRL+P"));
setMenu(fileMenu, newItem, openItem, saveItem, aItem, separator0,
uItem, printItem, separator1, exitItem);
undoItem.setAccelerator(KeyCombination.valueOf("CTRL+Z"));
undoItem.setDisable(true);
tItem.setAccelerator(KeyCombination.valueOf("CTRL+T"));
tItem.setDisable(true);
copyItem.setAccelerator(KeyCombination.valueOf("CTRL+C"));
copyItem.setDisable(true);
pasteItem.setAccelerator(KeyCombination.valueOf("CTRL+V"));
pasteItem.setDisable(false);
delItem.setAccelerator(KeyCombination.valueOf("DEL"));
delItem.setDisable(true);
exploreItem.setAccelerator(KeyCombination.valueOf("CTRL+E"));
exploreItem.setDisable(true);
findItem.setAccelerator(KeyCombination.valueOf("CTRL+F"));
findItem.setDisable(true);
nextFindItem.setAccelerator(KeyCombination.valueOf("SHIFT+F4"));
nextFindItem.setDisable(true);
prevFindItem.setAccelerator(KeyCombination.valueOf("SHIFT+F3"));
prevFindItem.setDisable(true);
replaceItem.setAccelerator(KeyCombination.valueOf("CTRL+H"));
forwardItem.setAccelerator(KeyCombination.valueOf("CTRL+G"));
forwardItem.setDisable(true);
selectAllItem.setAccelerator(KeyCombination.valueOf("CTRL+A"));
dateItem.setAccelerator(KeyCombination.valueOf("F5"));
editMenu.setAccelerator(KeyCombination.valueOf("alt+e"));
setMenu(editMenu, undoItem, separator3, tItem, copyItem, pasteItem, delItem, separator4, exploreItem, findItem,
nextFindItem, prevFindItem, replaceItem, forwardItem, separator5, selectAllItem, dateItem);
wrapItem.setSelected(true);
textArea.setWrapText(wrapItem.isSelected());
setMenu(formatMenu, wrapItem, fontStyleItem);
formatMenu.setAccelerator(KeyCombination.valueOf("alt+o"));
stateItem.setSelected(true);
largeItem.setAccelerator(KeyCombination.valueOf("ctrl+l"));
smallItem.setAccelerator(KeyCombination.valueOf("ctrl+m"));
defaultSizeItem.setAccelerator(KeyCombination.valueOf("ctrl+0"));
setMenu(zoomMenu, largeItem, smallItem, defaultSizeItem);
setMenu(viewMenu, zoomMenu, stateItem);
viewMenu.setAccelerator(KeyCombination.valueOf("alt+v"));
setMenu(helpMenu, searchForHelpItem, sendFeedbackItem, separator6, aboutItem);
helpMenu.setAccelerator(KeyCombination.valueOf("alt+h"));
menuBar.setPrefWidth(900);
menuBar.setPadding(new Insets(0.0));
findItem.setOnAction(e->searchPaneFactory.showSearchStage(textAreaFactory));
largeItem.setOnAction(e->TextAreaUtils.enLarge(textArea));
smallItem.setOnAction(e->TextAreaUtils.shrink(textArea));
defaultSizeItem.setOnAction(e->textArea.setStyle("-fx-font-size: 15"));
replaceItem.setOnAction(e->searchPaneFactory.showSearchStage(textAreaFactory));
nextFindItem.setOnAction(e->TextAreaUtils.findWords(textArea,textArea.getSelectedText(),false,true,false));
prevFindItem.setOnAction(e->TextAreaUtils.backSearch(textArea,textArea.getSelectedText(),false,true,false));
selectAllItem.setOnAction(e->textArea.selectAll());
dateItem.setOnAction(e->{
Date date = new Date();
//0:15 2022/5/23
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm yyyy/MM/dd");
String insertDate = sdf.format(date);
textArea.insertText(textArea.getCaretPosition(),insertDate);
});
uItem.setOnAction(event -> {
PageFormat pf = new PageFormat();
PrinterJob.getPrinterJob().pageDialog(pf);
});
wrapItem.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
System.out.println("是否换行:"+newValue);
forwardItem.setDisable(newValue);
textArea.setWrapText(newValue);
}
});
undoItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
if(textArea.isUndoable()){
textArea.undo();
}
}
});
tItem.setOnAction(e->textArea.cut());
copyItem.setOnAction(e->textArea.copy());
pasteItem.setOnAction(e->textArea.paste());
delItem.setOnAction(e->textArea.replaceSelection(""));
exploreItem.setOnAction(event -> {
String searchString = textArea.getSelectedText();
try
{
java.net.URI uri = java.net.URI.create("https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=baidu&wd=" + searchString);
java.awt.Desktop dp = java.awt.Desktop.getDesktop();
if(dp.isSupported(java.awt.Desktop.Action.BROWSE))
{
dp.browse(uri);
}
}
catch(Exception e)
{
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("自动搜索出错!");
alert.setHeaderText("");
alert.setContentText("搜索内容过长!请手动搜索!");
alert.showAndWait();
try
{
java.net.URI uri = java.net.URI.create("www.baidu.com");
java.awt.Desktop dp = java.awt.Desktop.getDesktop();
if(dp.isSupported(java.awt.Desktop.Action.BROWSE))
{
try
{
dp.browse(uri);
}
catch(IOException e1) {
e1.printStackTrace();
}
}
}
catch(Exception e2) {
e2.printStackTrace();
}
}
});
FileSavePromptFactory.bindFileSaveMenuBar(this);
}
public void setTextFactory(TextAreaFactory textAreaFactory,Stage stage){
this.textAreaFactory = textAreaFactory;
this.textArea = textAreaFactory.getTextArea();
bindMenuText(stage);
textArea.setWrapText(wrapItem.isSelected());
findItem.setDisable(textAreaFactory.isEmpty());
nextFindItem.setDisable(textAreaFactory.isEmpty());
prevFindItem.setDisable(textAreaFactory.isEmpty());
replaceItem.setDisable(textAreaFactory.isEmpty());
}
public MenuBar getMenubar() {
menuBar.setStyle(
"-fx-background-color: #fdfdfe"
);
return menuBar;
}
public void setOwnerAnc(AnchorPane anc){
anc.getChildren().add(menuBar);
AnchorPane.setLeftAnchor(menuBar,0.0);
AnchorPane.setBottomAnchor(menuBar,0.0);
AnchorPane.setRightAnchor(menuBar,0.0);
AnchorPane.setTopAnchor(menuBar,0.0);
anc.widthProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
adjustWidth(newValue.doubleValue());
}
});
}
/**
* @description: 绑定菜单与文本框的监听事件,作为初始化条件
* @param:
* @return: void
* @author
* @date: 13:44 2022/5/21
* @param stage
*/
private void bindMenuText(Stage stage){
aboutItem.setOnAction(e-> System.out.println(originalPath));
aItem.setOnAction(e->FileSavePromptFactory.showSaveAsPane(textArea,stage));
saveItem.setOnAction(e-> {
FileSavePromptFactory.showSavePromptPane(textArea,stage,originalPath);
});
exitItem.setOnAction(e->FileSavePromptFactory.showExistSavePane(textArea,stage));
newItem.setOnAction(e->FileSavePromptFactory.showNewFileSavePane(textArea,stage));
openItem.setOnAction(e->FileSavePromptFactory.showOpenFileSavePane(textArea,stage));
forwardItem.setOnAction(e-> ToTargetPaneFactory.showToTargetStage(stage,textArea));
fontStyleItem.setOnAction(e->FontSettingPaneFactory.showFontSettingPane(stage,textArea));
//监听文本是否可撤销操作以便判断是否需要提示保存更改
textArea.undoableProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
undoItem.setDisable(!newValue);
System.out.println("是否可撤销:"+newValue);
if(newValue){
addStarTitle(stage);
}else{
if(stage.getTitle().contains("无标题")) {
removeStarTitle(stage);
}
}
}
});
//监听新文本的输入
textArea.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
boolean hasContent = newValue != null && newValue.length() != 0;
findItem.setDisable(!hasContent);
nextFindItem.setDisable(!hasContent);
prevFindItem.setDisable(!hasContent);
replaceItem.setDisable(!hasContent);
//通知SearchPaneFactory需要重置存储条件
SearchPaneFactory.setCanReset(true);
System.out.println("menu判断是否有内容"+hasContent);
addStarTitle(stage);
}
});
//监听选择的文本内容
textArea.selectedTextProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
boolean isUsable = true;
if(newValue == null || newValue.length() == 0){
isUsable = false;
}
System.out.println("是否选中内容:\t"+isUsable);
tItem.setDisable(!isUsable);
copyItem.setDisable(!isUsable);
delItem.setDisable(!isUsable);
exploreItem.setDisable(!isUsable);
}
});
//监听文本框的光标
textArea.caretPositionProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
//一切正常
System.out.println("新光标的位置为:\t"+textAreaFactory.getCaretPosition());
//更新状态栏
TextAreaUtils.updateStatusLabel(textArea,textAreaFactory.getStatusLabel());
//待定
textArea.setScrollTop(textArea.getScrollTop());
}
});
}
public void addStarTitle(Stage stage){
String original = stage.getTitle();
if(!original.contains("*")){
stage.setTitle("*"+original);
}
}
public void removeStarTitle(Stage stage){
stage.setTitle(stage.getTitle().replace("*",""));
}
private void adjustWidth(double width){
menuBar.setPrefWidth(width);
}
private void setMenu(Menu m, MenuItem... menuItems) {
for (MenuItem menuItem : menuItems) {
m.getItems().add(menuItem);
}
}
}
2.2、TextAreaFactory文本框组件
效果图
代码
TextAreaFactory.java
package domain;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.AnchorPane;
import javafx.scene.text.Font;
import utils.TextAreaUtils;
/**可以获得设置文本框的光标位置,获取改动状态、获取设置文本是否为空、获取设置文本内容
* @ClassName TextAreaFactory
* @Description TODO
* @Author Mr_X
* @Date 2022/5/20 17:55
* @Version 1.0
*/
public class TextAreaFactory {
private int caretPosition = 0;
private boolean isUnchanged = true;
public TextArea textArea = new TextArea();
private boolean isEmpty = true;
private String textContent = "";
private String selectContent = textArea.getSelectedText();
private Label statusLabel = new Label();
public TextArea getTextArea(){
initializeTextArea();
return textArea;
}
public void setOwnerAnc(AnchorPane anc){
anc.getChildren().add(textArea);
AnchorPane.setLeftAnchor(textArea,0.0);
AnchorPane.setBottomAnchor(textArea,0.0);
AnchorPane.setRightAnchor(textArea,0.0);
AnchorPane.setTopAnchor(textArea,0.0);
}
/**
* 根据需求移动光标
* @param caretPosition
*/
public void setCaretPosition(int caretPosition) {
textArea.positionCaret(caretPosition);
this.caretPosition = caretPosition;
}
/**
* 得到实时的光标位置
* @return
*/
public int getCaretPosition() {
caretPosition = textArea.getCaretPosition();
return caretPosition;
}
public void setTextContent(String textContent) {
textArea.setText(textContent);
this.textContent = textContent;
}
public void setEmpty(boolean empty) {
if(empty){
textArea.clear();
}
isEmpty = empty;
}
public String getSelectContent() {
selectContent = textArea.getSelectedText();
return selectContent;
}
// public void setSelectContent(String selectContent) {
// this.selectContent = selectContent;
// }
public Label getStatusLabel() {
return statusLabel;
}
public String getTextContent() {
textContent = textArea.getText();
return textContent;
}
public boolean isEmpty() {
isEmpty = textArea.getText().isEmpty();
return isEmpty;
}
public boolean isUnchanged() {
//如果不可以撤销文本那么可以认为此文本未做改动,故可直接退出不鄙视保存
isUnchanged = textArea.isUndoable();
return isUnchanged;
}
public void bindTextAreaLabel(Label bindLabel){
this.statusLabel = bindLabel;
textArea.fontProperty().addListener(new ChangeListener<Font>() {
@Override
public void changed(ObservableValue<? extends Font> observable, Font oldValue, Font newValue) {
TextAreaUtils.updateStatusLabel(textArea,bindLabel);
}
});
}
public void initializeTextArea(){
textArea.setStyle("-fx-font-size: 15");
textArea.setOnScroll(event -> {
if(event.isControlDown() && event.getDeltaY() < 0) {
//缩小文本
TextAreaUtils.shrink(textArea);
} else if(event.isControlDown() && event.getDeltaY() > 0) {
//放大文本
TextAreaUtils.enLarge(textArea);
}
//更新状态栏
System.out.println("环论滚动");
});
}
}
2.3、SearchPaneFactory查找替换组件
效果
代码(其中的TextAreaUtils.java在下文)
SearchPaneFactory.java
package domain;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.Mnemonic;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import utils.TextAreaUtils;
import java.io.InputStream;
/**
* @ClassName SearchPaneFactory
* @Description TODO
* @Author Mr_X
* @Date 2022/5/21 20:08
* @Version 1.0
*/
public class SearchPaneFactory {
private final static double STAGE_HEIGHT = 430;
private final static double STAGE_WIDTH = 320;
private final static Label KEY_LABEL = new Label("查找内容(N)");
private final static Label REPLACE_LABEL = new Label("替换为");
private final static Label TOWARD_LABEL = new Label("方向");
private final static Label SCOPE_LABEL = new Label("范围");
private final static Label OPTION_LABEL = new Label("可选项");
private final static Label OPERA_LABEL = new Label("操作指令");
private final static TextField KEY_STRING_TF = new TextField();
private final static TextField REPLACEMENT_TF = new TextField();
private final static RadioButton UP_RADIO_BTN = new RadioButton("向上(U)");
private final static RadioButton DOWN_RADIO_BTN = new RadioButton("向下(D)");
private final static ToggleGroup TOWARD_TG = new ToggleGroup();
private final static RadioButton WRAP_RADIO_BTN = new RadioButton("所在行内");
private final static RadioButton ALL_RADIO_BTN = new RadioButton("全文范围");
private final static ToggleGroup SCOPE_TG = new ToggleGroup();
private final static Button NEXT_BTN = new Button("查找下一个(F)");
private final static Button REPLACE_BTN = new Button("替换");
private final static Button REPLACE_ALL_BTN = new Button("全部替换");
private final static Button EXIT_BTN = new Button("取消");
private final static CheckBox IGNORE_CASE_BOX = new CheckBox("区分大小写(C)");
private final static CheckBox RECYCLE_BOX = new CheckBox("循环(R)");
private final static VBox INPUT_TIP_VBOX = new VBox(KEY_LABEL, REPLACE_LABEL);
private final static VBox INPUT_VBOX = new VBox(KEY_STRING_TF, REPLACEMENT_TF);
private final static VBox TOWARD_VBOX = new VBox(TOWARD_LABEL, UP_RADIO_BTN, DOWN_RADIO_BTN);
private final static VBox SCOPE_VBOX = new VBox(SCOPE_LABEL, WRAP_RADIO_BTN, ALL_RADIO_BTN);
private final static VBox OPTION_VBOX = new VBox(OPTION_LABEL, IGNORE_CASE_BOX, RECYCLE_BOX);
private final static VBox OPERA_VBOX = new VBox(OPERA_LABEL, NEXT_BTN, REPLACE_BTN, REPLACE_ALL_BTN, EXIT_BTN);
private final static GridPane GRID_PANE = new GridPane();
private final static Scene SCENE = new Scene(GRID_PANE);
private final static Stage PRIMARY_STAGE = new Stage();
private final static KeyCombination KC_NEXT = new KeyCodeCombination(KeyCode.F, KeyCombination.ALT_DOWN);
private final static Mnemonic MNEMONIC_NEXT = new Mnemonic(NEXT_BTN, KC_NEXT);
private static boolean isCaseIgnored = true;
private static boolean isCycle = false;
private static boolean isAllScope = false;
private static String key;
private static String replacement;
private static boolean isUp;
private static boolean canReset = false;
/*
初始化加载,只加载一次即可,减少内存占用
*/
static {
REPLACEMENT_TF.setFocusTraversable(false);
REPLACEMENT_TF.setPromptText("可输入代替换词");
UP_RADIO_BTN.setToggleGroup(TOWARD_TG);
DOWN_RADIO_BTN.setToggleGroup(TOWARD_TG);
WRAP_RADIO_BTN.setToggleGroup(SCOPE_TG);
ALL_RADIO_BTN.setToggleGroup(SCOPE_TG);
NEXT_BTN.setPrefWidth(100.0);
EXIT_BTN.setPrefWidth(100.0);
REPLACE_BTN.setPrefWidth(100.0);
REPLACE_ALL_BTN.setPrefWidth(100.0);
DOWN_RADIO_BTN.setSelected(true);
ALL_RADIO_BTN.setSelected(true);
TOWARD_VBOX.setStyle(
"-fx-border-style: dotted"
);
SCOPE_VBOX.setStyle(
"-fx-border-style: dotted"
);
OPTION_VBOX.setStyle(
"-fx-border-style: dotted"
);
INPUT_TIP_VBOX.setSpacing(15.0);
INPUT_VBOX.setSpacing(5.0);
TOWARD_VBOX.setSpacing(10.0);
SCOPE_VBOX.setSpacing(10.0);
OPTION_VBOX.setSpacing(10.0);
OPERA_VBOX.setSpacing(10.0);
GRID_PANE.add(INPUT_TIP_VBOX, 0, 0);
GRID_PANE.add(INPUT_VBOX, 1, 0);
GRID_PANE.add(TOWARD_VBOX, 0, 1);
GRID_PANE.add(SCOPE_VBOX, 1, 1);
GRID_PANE.add(OPTION_VBOX, 0, 2);
GRID_PANE.add(OPERA_VBOX, 1, 2);
GridPane.setMargin(OPERA_VBOX, new Insets(10.0));
GRID_PANE.setAlignment(Pos.CENTER);
GRID_PANE.setHgap(20.0);
GRID_PANE.setVgap(20.0);
GRID_PANE.setStyle(
"-fx-background-color: #efefef"
);
PRIMARY_STAGE.setScene(SCENE);
PRIMARY_STAGE.setTitle("查找");
PRIMARY_STAGE.setHeight(STAGE_HEIGHT);
PRIMARY_STAGE.setWidth(STAGE_WIDTH);
InputStream rs1 = Thread.currentThread().getContextClassLoader().getResourceAsStream("resource/logo.jpg");
assert rs1 != null;
Image image = new Image(rs1, 50, 50, false, true);
PRIMARY_STAGE.getIcons().add(image);
SCENE.addMnemonic(MNEMONIC_NEXT);
NEXT_BTN.setOnAction(e -> System.out.println("nextBtn触发"));
NEXT_BTN.setDisable(true);
REPLACE_BTN.setDisable(true);
REPLACE_ALL_BTN.setDisable(true);
//绑定输入框监听
KEY_STRING_TF.textProperty().addListener((observable, oldValue, newValue) -> {
NEXT_BTN.setDisable(newValue.isEmpty());
if(!newValue.isEmpty()){
key = newValue;
System.out.println("查找的单词为:"+key);
}
boolean isEmpty = newValue.isEmpty() || REPLACEMENT_TF.getText().isEmpty();
REPLACE_BTN.setDisable(isEmpty);
REPLACE_ALL_BTN.setDisable(isEmpty);
});
REPLACEMENT_TF.textProperty().addListener((observable, oldValue, newValue) -> {
boolean isEmpty = newValue.isEmpty() || KEY_STRING_TF.getText().isEmpty();
if(!newValue.isEmpty()){
replacement = newValue;
System.out.println("替换为:"+replacement);
}
REPLACE_BTN.setDisable(isEmpty);
REPLACE_ALL_BTN.setDisable(isEmpty);
});
//设置单选组的响应
TOWARD_TG.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
RadioButton r = (RadioButton) newValue;
isUp = r == UP_RADIO_BTN;
System.out.println("是否向上:\t"+isUp);
});
SCOPE_TG.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
RadioButton r = (RadioButton) newValue;
isAllScope = r == ALL_RADIO_BTN;
System.out.println("是否全文范围:\t"+isAllScope);
});
//设置可选项的监听响应
IGNORE_CASE_BOX.selectedProperty().addListener((observable, oldValue, newValue) -> {
isCaseIgnored = !newValue;
System.out.println("是否区分大小写:\t"+isCaseIgnored);
});
RECYCLE_BOX.selectedProperty().addListener((observable, oldValue, newValue) -> {
isCycle = newValue;
System.out.println("是否循环寻找目标:\t"+isCaseIgnored);
});
//设置按钮的响应事件
EXIT_BTN.setOnAction(e -> PRIMARY_STAGE.close());
}
/** 当文本框中的内容发生改变时,全局循环的触发条件也相应地发生改变,此时需要重置TextAreaUtils中的参数
* @description:
* @param: canReset
* @return: void
* @author
* @date: 20:33 2022/5/22
*/
public static void setCanReset(boolean canReset) {
SearchPaneFactory.canReset = canReset;
}
public void showSearchStage(TextAreaFactory textAreaFactory) {
bindTextSearch(textAreaFactory);
textAreaFactory.textArea.requestFocus();
KEY_STRING_TF.setText(textAreaFactory.getSelectContent());
PRIMARY_STAGE.show();
}
public static void bindTextSearch(TextAreaFactory textAreaFactory) {
TextArea textArea = textAreaFactory.textArea;
textArea.requestFocus();
/*
private final static Button NEXT_BTN = new Button("查找下一个(F)");
private final static Button REPLACE_BTN = new Button("替换");
private final static Button REPLACE_ALL_BTN = new Button("全部替换");
*/
NEXT_BTN.setOnAction(e->{
if(isUp){
TextAreaUtils.backSearch(textArea,key,isCaseIgnored,isAllScope,isCycle);
}else{
TextAreaUtils.findWords(textArea,key,isCaseIgnored,isAllScope,isCycle);
}
});
REPLACE_BTN.setOnAction(e->TextAreaUtils.replace(textArea,key,replacement,isCaseIgnored,isAllScope,isCycle));
REPLACE_ALL_BTN.setOnAction(e->TextAreaUtils.replaceAll(textArea,key,replacement,isCaseIgnored));
}
}
2.4、ToTargetPaneFactory转到行组件
效果
代码
ToTargetPaneFactory.java
package domain;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import utils.TextAreaUtils;
import java.io.InputStream;
/**
* @ClassName ToTargetPane
* @Description TODO
* @Author Mr_X
* @Date 2022/5/22 22:09
* @Version 1.0
*/
public class ToTargetPaneFactory {
private static final double STAGE_WIDTH = 300;
private static final double STAGE_HEIGHT = 150;
private static final Label LABEL = new Label("行号(L):");
private static final TextField TEXT_FIELD = new TextField();
private static final Label PLACE_HOLDER = new Label();
private static final Button TURN_TO_BTN = new Button("转到");
private static final Button CANCEL_BTN = new Button("取消");
private static final HBox H_BOX = new HBox(10.0, PLACE_HOLDER, TURN_TO_BTN, CANCEL_BTN);
private static final VBox V_BOX = new VBox(5.0, LABEL, TEXT_FIELD, H_BOX);
private static final Scene SCENE = new Scene(V_BOX);
private static final Tooltip TOOLTIP = new Tooltip();
private static final Alert ALERT = new Alert(Alert.AlertType.ERROR);
private static final String REG = "^[0-9]*[1-9][0-9]*$";
/*
初始化一次,节约内存开销
*/
static {
PLACE_HOLDER.setPrefWidth(90);
TURN_TO_BTN.setPrefWidth(60);
CANCEL_BTN.setPrefWidth(60);
TEXT_FIELD.setPrefWidth(120);
V_BOX.setPadding(new Insets(10.0));
VBox.setMargin(H_BOX,new Insets(20.0));
TOOLTIP.setText("不能接受的字符\n你只能在此处输入数字。");
TEXT_FIELD.setTooltip(TOOLTIP);
ALERT.setTitle("跳行");
ALERT.setHeaderText("");
ALERT.setContentText("输入行数必须为正整数");
}
public static void showToTargetStage(Stage ownerStage,TextArea textArea){
Stage stage = new Stage();
InputStream rs1 = Thread.currentThread().getContextClassLoader().getResourceAsStream("resource/logo.jpg");
Image image = new Image(rs1, 50, 50, false, true);
stage.getIcons().add(image);
stage.setResizable(false);
stage.setScene(SCENE);
stage.setTitle("转到指定行");
stage.setHeight(STAGE_HEIGHT);
stage.setWidth(STAGE_WIDTH);
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(ownerStage);
TURN_TO_BTN.setOnAction(e->{
if(!TEXT_FIELD.getText().matches(REG)){
ALERT.showAndWait();
TEXT_FIELD.clear();
}else{
TextAreaUtils.turnToTargetLine(textArea,Integer.parseInt(TEXT_FIELD.getText()));
}
});
CANCEL_BTN.setOnAction(e->stage.close());
stage.show();
}
}
2.5、FontSettingPaneFactory字体设置组件
效果
代码
FontSettingPaneFactory.java
package domain;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.awt.*;
import java.io.InputStream;
/**
* @ClassName FontSettingPaneFactory
* @Description TODO
* @Author Mr_X
* @Date 2022/5/23 9:00
* @Version 1.0
*/
public class FontSettingPaneFactory{
private static final double STAGE_WIDTH = 600;
private static final double STAGE_HEIGHT = 500;
private static final Label FONT_LABEL = new Label("字体(F):");
private static final Label GLYPH_LABEL = new Label("字形(Y):");
private static final Label SIZE_LABEL = new Label("大小(S):");
private static final Label COLOR_LABEL = new Label("颜色(C):");
private static final Label PREVIEW_LABEL = new Label("预览:");
private static final TextField FONT_TF = new TextField();
private static final TextField GLYPH_TF = new TextField();
private static final TextField SIZE_TF = new TextField();
private static final GraphicsEnvironment E = GraphicsEnvironment.getLocalGraphicsEnvironment();
private static final ObservableList<String> FONT_ARRAY = FXCollections.observableArrayList(E.getAvailableFontFamilyNames());
private static final ObservableList<String> GLYPH_ARRAY = FXCollections.observableArrayList("常规","粗体","细体","斜体");
private static final ObservableList<String> SIZE_ARRAY = FXCollections.observableArrayList("8", "9", "10", "11", "12", "14", "16", "18", "20",
"22", "24", "26", "28", "36", "48", "72", "初号", "小初", "一号", "小一", "二号", "小二", "三号", "小三",
"四号", "小四", "五号", "小五", "六号", "小六", "七号", "八号");
private static final ObservableList<String> COLOR_ARRAY = FXCollections.observableArrayList("BLACK", "BLUE",
"GREEN", "GRAY", "RED", "WHITE", "YELLOW");
private static final int[] SIZE_INT_ARRAY = {8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36,
48, 72, 42, 36, 26, 24, 22, 18, 16, 15, 14, 12, 10, 9, 8, 7, 6, 5};
private static final FontPosture[] FONT_POSTURES = new FontPosture[]{FontPosture.REGULAR,FontPosture.ITALIC};
private static final FontWeight[] FONT_WEIGHTS = new FontWeight[]{FontWeight.NORMAL,FontWeight.BOLD,FontWeight.LIGHT};
private static final ListView<String> FONT_SELECTOR = new ListView<>(FONT_ARRAY);
private static final ListView<String> GLYPH_SELECTOR = new ListView<>(GLYPH_ARRAY);
private static final ListView<String> SIZE_SELECTOR = new ListView<>(SIZE_ARRAY);
private static final ComboBox<String> COLOR_SELECTOR = new ComboBox<>(COLOR_ARRAY);
private static final String EXP_CHINESE = "为人民服务";
private static final String EXP_ENGLISH = "Migrant workers in the new era";
private static final String EXP_NUMBER = "1024";
private static final TextArea PREVIEW_TEXTAREA = new TextArea(EXP_CHINESE+"\n"+EXP_ENGLISH+"\n"+EXP_NUMBER);
private static final javafx.scene.control.Button CONFIRM_BTN = new javafx.scene.control.Button("确定");
private static final javafx.scene.control.Button CANCEL_BTN = new javafx.scene.control.Button("取消");
private static final VBox FONT_VBOX = new VBox(5.0, FONT_LABEL, FONT_TF, FONT_SELECTOR);
private static final VBox GLYPH_VBOX = new VBox(5.0, GLYPH_LABEL, GLYPH_TF, GLYPH_SELECTOR);
private static final VBox SIZE_VBOX = new VBox(5.0, SIZE_LABEL, SIZE_TF, SIZE_SELECTOR);
private static final VBox COLOR_VBOX = new VBox(5.0, COLOR_LABEL, COLOR_SELECTOR);
private static final VBox PREVIEW_AREA_VBOX = new VBox(5.0, PREVIEW_LABEL, PREVIEW_TEXTAREA);
private static final VBox BUTTON_VBOX = new VBox(10.0, CONFIRM_BTN, CANCEL_BTN);
private static final GridPane FONT_SET_PANE = new GridPane();
private static int fontSize = 15;
private static String fontColor = "BLACK";
private static String family = FONT_SELECTOR.getSelectionModel().getSelectedItem();
private static FontPosture fontPosture = FontPosture.REGULAR;
private static FontWeight fontWeight = FontWeight.NORMAL;
private static Font font = Font.font(family, fontWeight, fontPosture, fontSize);
private static String colorStyle = "-fx-text-fill: black";
private static TextArea mainTextArea = new TextArea();
private static final Scene SCENE = new Scene(FONT_SET_PANE);
static {
FONT_TF.setEditable(false);
GLYPH_TF.setEditable(false);
SIZE_TF.setEditable(false);
PREVIEW_TEXTAREA.setEditable(false);
FONT_TF.setFocusTraversable(false);
GLYPH_TF.setFocusTraversable(false);
SIZE_TF.setFocusTraversable(false);
PREVIEW_TEXTAREA.setFocusTraversable(false);
PREVIEW_TEXTAREA.setPrefWidth(300);
PREVIEW_TEXTAREA.setPrefHeight(100);
//绑定文本框的显示
FONT_TF.textProperty().bind(FONT_SELECTOR.getSelectionModel().selectedItemProperty());
GLYPH_TF.textProperty().bind(GLYPH_SELECTOR.getSelectionModel().selectedItemProperty());
SIZE_TF.textProperty().bind(SIZE_SELECTOR.getSelectionModel().selectedItemProperty());
//默认选择字体选项
FONT_SELECTOR.getSelectionModel().select("微软雅黑");
GLYPH_SELECTOR.getSelectionModel().select(0);
SIZE_SELECTOR.getSelectionModel().select(4);
COLOR_SELECTOR.getSelectionModel().select(0);
CONFIRM_BTN.requestFocus();
FONT_SET_PANE.add(GLYPH_VBOX,0,0);
FONT_SET_PANE.add(FONT_VBOX,1,0);
FONT_SET_PANE.add(SIZE_VBOX,2,0);
FONT_SET_PANE.add(COLOR_VBOX,0,1);
FONT_SET_PANE.add(BUTTON_VBOX,2,1);
FONT_SET_PANE.add(PREVIEW_AREA_VBOX,1,1);
FONT_SET_PANE.setPadding(new Insets(10.0));
FONT_SET_PANE.setHgap(10.0);
FONT_SET_PANE.setVgap(10.0);
GridPane.setMargin(BUTTON_VBOX,new Insets(20.0));
SIZE_SELECTOR.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
fontSize = SIZE_INT_ARRAY[newValue.intValue()];
System.out.println("fontSize:\t"+fontSize);
font = Font.font(family, fontWeight, fontPosture, fontSize);
PREVIEW_TEXTAREA.setFont(font);
mainTextArea.setFont(font);
}
});
GLYPH_SELECTOR.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
int index = newValue.intValue();
//"常规","粗体","细体","斜体");设置字体
if(index == 0){
fontWeight = FONT_WEIGHTS[index];
fontPosture = FONT_POSTURES[index];
}else if(index == 1){
fontWeight = FONT_WEIGHTS[index];
}else if(index == 2){
fontWeight = FONT_WEIGHTS[index];
}else if(index == 3){
fontPosture = FONT_POSTURES[1];
}
font = Font.font(family, fontWeight, fontPosture, fontSize);
PREVIEW_TEXTAREA.setFont(font);
mainTextArea.setFont(font);
System.out.println("weight:\t"+fontWeight+"\t,posture:\t"+fontPosture);
}
});
COLOR_SELECTOR.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
fontColor = newValue;
font = Font.font(family, fontWeight, fontPosture, fontSize);
PREVIEW_TEXTAREA.setFont(font);
//将设置的颜色传入
colorStyle = "-fx-text-fill: "+fontColor;
PREVIEW_TEXTAREA.setStyle("-fx-text-fill: "+fontColor);
mainTextArea.setStyle("-fx-text-fill: "+fontColor);
System.out.println("fontColor:\t"+fontColor);
}
});
FONT_SELECTOR.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
family = newValue;
font = Font.font(family, fontWeight, fontPosture, fontSize);
System.out.println("family:\t"+family);
PREVIEW_TEXTAREA.setFont(font);
mainTextArea.setFont(font);
}
});
}
public static void showFontSettingPane(Stage ownerStage,TextArea mainArea){
//存储源文本的原格式,若用户点击取消按钮,则恢复源格式
mainTextArea = mainArea;
Font originalFont = mainArea.getFont();
String originalStyle = mainArea.getStyle();
Stage stage = new Stage();
InputStream rs1 = Thread.currentThread().getContextClassLoader().getResourceAsStream("resource/logo.jpg");
Image image = new Image(rs1, 50, 50, false, true);
stage.getIcons().add(image);
stage.setResizable(false);
stage.setScene(SCENE);
stage.setTitle("字体设置");
stage.setHeight(STAGE_HEIGHT);
stage.setWidth(STAGE_WIDTH);
stage.initOwner(ownerStage);
stage.initModality(Modality.APPLICATION_MODAL);
CONFIRM_BTN.setOnAction(e->{
mainArea.setFont(font);
mainArea.setStyle("-fx-text-fill: "+fontColor);
stage.close();
});
CANCEL_BTN.setOnAction(e->{
mainArea.setStyle(originalStyle);
mainArea.setFont(originalFont);
stage.close();
});
stage.show();
}
}
2.6、StatusLabelFactory状态栏组件
效果
代码
StatusLabelFactory.java
package domain;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
/**
* @ClassName LabelFactory
* @Description TODO
* @Author Mr_X
* @Date 2022/5/21 11:10
* @Version 1.0
*/
public class StatusLabelFactory {
private static Label statusLabel = new Label();
public Label getLabel(double stageWidth,TextArea textArea){
statusLabel.setText(" 第 1 行, 第 1列 共 0 字 缩放:100%");
statusLabel.setPrefWidth(stageWidth);
return statusLabel;
}
}
2.7、FileSavePromptFactory文件保存提示组件
效果
代码
FileSavePromptFactory.java
package domain;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
import utils.TextAreaUtils;
import java.io.File;
import java.io.InputStream;
/**
* 保存文件的提示弹窗
*
* @ClassName FileSaveHintPane
* @Description TODO
* @Author Mr_X
* @Date 2022/5/23 18:49
* @Version 1.0
*/
public class FileSavePromptFactory {
private static MenuBarFactory menuBarFactory;
private final static double STAGE_WIDTH = 400;
private final static double STAGE_HEIGHT = 160;
private static final Label INFO_LABEL = new Label();
private static final Label PLACE_HOLDER = new Label();
private static final Button SAVE_BTN = new Button("保存(S)");
private static final Button ABORT_BTN = new Button("不保存(N)");
private static final Button CANCEL_BTN = new Button("取消");
private static final HBox BTN_BOX = new HBox(10.0, PLACE_HOLDER, SAVE_BTN, ABORT_BTN, CANCEL_BTN);
private static final VBox CONTENT_VBOX = new VBox(10.0, INFO_LABEL, BTN_BOX);
private static final Scene SCENE = new Scene(CONTENT_VBOX);
private static StringBuffer path;
static {
INFO_LABEL.setPrefWidth(STAGE_WIDTH);
INFO_LABEL.setPrefHeight(STAGE_HEIGHT / 2);
INFO_LABEL.setStyle(
"-fx-text-fill: #003398;" +
"-fx-font-size: 16"
);
SAVE_BTN.setPrefWidth(90.0);
ABORT_BTN.setPrefWidth(90.0);
CANCEL_BTN.setPrefWidth(90.0);
CONTENT_VBOX.setPadding(new Insets(10.0));
}
public static void bindFileSaveMenuBar(MenuBarFactory factory) {
menuBarFactory = factory;
}
/**
* 设置保存事件
*
* @description:
* @param: textArea ownerStage originalPath
* @return: void
* @author
* @date: 19:14 2022/5/23
*/
public static void showSavePromptPane(TextArea textArea, Stage ownerStage, StringBuffer originalPath) {
//说明是新建的文本,直接弹出对话框,选择保存
if (originalPath == null) {
Stage fileStage = new Stage();
FileChooser fc = new FileChooser();
fc.setTitle("保存文件");
///设置默认打开的文件夹路径
fc.setInitialDirectory(new File("C:\\"));
//设置过滤器,指定打开文件的类型
fc.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("文本文件", "*.txt"));
//打开文件选择器并返回返回打开的文件所在的完整路径
File file = fc.showSaveDialog(fileStage);
if (file != null) {
//选择了路径,保存,更改主面板标题、MenuBar源文件路径不为空了
String absolutePath = file.getAbsolutePath();
try {
TextAreaUtils.saveTextAreaContent(textArea, absolutePath);
} catch (Exception ex) {
ex.printStackTrace();
}
path = new StringBuffer(absolutePath);
menuBarFactory.originalPath = path;
ownerStage.setTitle(path.toString());
}
} else {
//保存过,静默保存,将源窗口的标题的*去除
ownerStage.setTitle(ownerStage.getTitle().replace("*", ""));
try {
TextAreaUtils.saveTextAreaContent(textArea, ownerStage.getTitle());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public static Stage getModelSaveStage(Stage ownerStage){
Stage stage = new Stage();
stage.setScene(SCENE);
stage.setWidth(STAGE_WIDTH);
stage.setHeight(STAGE_HEIGHT);
stage.initOwner(ownerStage);
stage.initModality(Modality.APPLICATION_MODAL);
InputStream rs1 = Thread.currentThread().getContextClassLoader().getResourceAsStream("resource/logo.jpg");
Image image = new Image(rs1, 50, 50, false, true);
stage.setTitle("保存提示");
stage.getIcons().add(image);
stage.setResizable(false);
return stage;
}
public static void showNewFileSavePane(TextArea textArea, Stage ownerStage) {
if (menuBarFactory.originalPath == null && textArea.getText().isEmpty()) {
return;
}
//无原路径,--》有内容:弹出对话框,无内容不做处理
Stage stage = new Stage();
CANCEL_BTN.setOnAction(e -> stage.close());
if (menuBarFactory.originalPath == null) {
//有内容需要弹出对话框提醒保存
INFO_LABEL.setText("你想将更改保存到无标题吗?");
SAVE_BTN.setOnAction(e -> {
//从未保存过,则直接需要再开出一个文件保存的窗口,此时需要更改源地址,String的不可更改性需要去优化
Stage fileStage = new Stage();
FileChooser fc = new FileChooser();
fc.setTitle("保存文件");
///设置默认打开的文件夹路径
fc.setInitialDirectory(new File("C:\\"));
//设置过滤器,指定打开文件的类型
fc.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("文本文件", "*.txt"));
//打开文件选择器并返回返回打开的文件所在的完整路径
File file = fc.showSaveDialog(fileStage);
if (file != null) {
String absolutePath = file.getAbsolutePath();
try {
TextAreaUtils.saveTextAreaContent(textArea, absolutePath);
} catch (Exception ex) {
ex.printStackTrace();
}
ownerStage.setTitle("无标题");
}
stage.close();
});
ABORT_BTN.setOnAction(e -> {
textArea.clear();
stage.close();
});
}else{
//有源路径的新建,弹出对话框,确定就默认保存,舍弃就直接清空textArea,取消就除去面板
INFO_LABEL.setText("你想将更改保存到"+menuBarFactory.originalPath+"吗?");
SAVE_BTN.setOnAction(e->{
//静默保存
ownerStage.setTitle(ownerStage.getTitle().replace("*",""));
try {
TextAreaUtils.saveTextAreaContent(textArea,ownerStage.getTitle());
} catch (Exception ex) {
ex.printStackTrace();
}
//保存之后设置主面板标题,清空文本框、清空源路径
textArea.clear();
stage.close();
menuBarFactory.originalPath = null;
ownerStage.setTitle("无标题");
});
ABORT_BTN.setOnAction(e -> {
textArea.clear();
stage.close();
menuBarFactory.originalPath = null;
ownerStage.setTitle("无标题");
});
}
stage.setScene(SCENE);
stage.setWidth(STAGE_WIDTH);
stage.setHeight(STAGE_HEIGHT);
stage.initOwner(ownerStage);
stage.initModality(Modality.APPLICATION_MODAL);
InputStream rs1 = Thread.currentThread().getContextClassLoader().getResourceAsStream("resource/logo.jpg");
Image image = new Image(rs1, 50, 50, false, true);
stage.setTitle("保存提示");
stage.getIcons().add(image);
stage.setResizable(false);
stage.show();
}
/** 设置打开文件时对现存文件的保存面板
* @description:
* @param: textArea ownerStage
* @return: void
* @author
* @date: 22:18 2022/5/23
*/
public static void showOpenFileSavePane(TextArea textArea, Stage ownerStage){
boolean hasStar = ownerStage.getTitle().contains("*");
StringBuffer tempPath = menuBarFactory.originalPath;
boolean directRead = (tempPath != null && !hasStar) || (tempPath == null && textArea.getText().isEmpty());
if(directRead){
//直接唤醒导入数据面板
callFileImportPane(textArea,ownerStage);
}else{
Stage stage = new Stage();
CANCEL_BTN.setOnAction(e -> stage.close());
ABORT_BTN.setOnAction(e->{
callFileImportPane(textArea,ownerStage);
stage.close();
});
if(tempPath!=null){
//有源路径,如果不含星星直接弹出读文件对话框打开,如果含星星说明更改,弹出保存面板,如果确认,先静默保存文本,再读入文件,如果放弃,直接读入文本
INFO_LABEL.setText("你想将更改保存到"+menuBarFactory.originalPath+"吗?");
SAVE_BTN.setOnAction(e->{
try {
TextAreaUtils.saveTextAreaContent(textArea,ownerStage.getTitle());
} catch (Exception ex) {
ex.printStackTrace();
}
ownerStage.setTitle(ownerStage.getTitle().replace("*",""));
callFileImportPane(textArea,ownerStage);
stage.close();
});
}else{
//无源路径,如果无内容,直接弹出读文件对话框打开,如果有内容,弹出对话框,如果确认,弹出文件保存选择器,保存文本,再读入文件,如果放弃,直接读入文本
INFO_LABEL.setText("你想先将更改保存到无标题吗?");
SAVE_BTN.setOnAction(e -> {
//从未保存过,则直接需要再开出一个文件保存的窗口
Stage fileStage = new Stage();
FileChooser fc = new FileChooser();
fc.setTitle("保存文件");
///设置默认打开的文件夹路径
fc.setInitialDirectory(new File("C:\\"));
//设置过滤器,指定打开文件的类型
fc.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("文本文件", "*.txt"));
//打开文件选择器并返回返回打开的文件所在的完整路径
File file = fc.showSaveDialog(fileStage);
if (file != null) {
String absolutePath = file.getAbsolutePath();
try {
//导入与否,一旦按下保存,那就保存一波
TextAreaUtils.saveTextAreaContent(textArea, absolutePath);
} catch (Exception ex) {
ex.printStackTrace();
}
ownerStage.setTitle(ownerStage.getTitle().replace("*",""));
}
callFileImportPane(textArea,ownerStage);
stage.close();
});
}
stage.setScene(SCENE);
stage.setWidth(STAGE_WIDTH);
stage.setHeight(STAGE_HEIGHT);
stage.initOwner(ownerStage);
stage.initModality(Modality.APPLICATION_MODAL);
InputStream rs1 = Thread.currentThread().getContextClassLoader().getResourceAsStream("resource/logo.jpg");
Image image = new Image(rs1, 50, 50, false, true);
stage.setTitle("保存提示");
stage.getIcons().add(image);
stage.setResizable(false);
stage.show();
}
}
public static void showSaveAsPane(TextArea textArea,Stage ownerStage){
Stage fileStage = new Stage();
FileChooser fc = new FileChooser();
fc.setTitle("保存文件");
///设置默认打开的文件夹路径
fc.setInitialDirectory(new File("C:\\"));
//设置过滤器,指定打开文件的类型
fc.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("文本文件", "*.txt"));
//打开文件选择器并返回返回打开的文件所在的完整路径
File file = fc.showSaveDialog(fileStage);
if (file != null) {
String absolutePath = file.getAbsolutePath();
try {
TextAreaUtils.saveTextAreaContent(textArea, absolutePath);
} catch (Exception ex) {
ex.printStackTrace();
}
ownerStage.setTitle(absolutePath);
menuBarFactory.originalPath = new StringBuffer(absolutePath);
}
}
public static void showExistSavePane(TextArea textArea,Stage ownerStage){
if(!ownerStage.getTitle().contains("*")){
ownerStage.close();
return;
}
String info = menuBarFactory.originalPath == null?"无标题":menuBarFactory.originalPath.toString();
Stage stage = new Stage();
INFO_LABEL.setText("你想将更改保存到"+info+"吗?");
CANCEL_BTN.setOnAction(e -> stage.close());
ABORT_BTN.setOnAction(e->{
stage.close();
ownerStage.close();
});
SAVE_BTN.setOnAction(e->{
showSavePromptPane(textArea,ownerStage,menuBarFactory.originalPath);
stage.close();
ownerStage.close();
});
stage.setScene(SCENE);
stage.setWidth(STAGE_WIDTH);
stage.setHeight(STAGE_HEIGHT);
stage.initOwner(ownerStage);
stage.initModality(Modality.APPLICATION_MODAL);
InputStream rs1 = Thread.currentThread().getContextClassLoader().getResourceAsStream("resource/logo.jpg");
Image image = new Image(rs1, 50, 50, false, true);
stage.setTitle("保存提示");
stage.getIcons().add(image);
stage.setResizable(false);
stage.show();
}
private static void callFileImportPane(TextArea textArea,Stage ownerStage){
Stage stage = new Stage();
FileChooser fc = new FileChooser();
fc.setTitle("打开导入文件");
///设置默认打开的文件夹路径
fc.setInitialDirectory(new File("C:\\"));
//设置过滤器,指定打开文件的类型
fc.getExtensionFilters().add(new FileChooser.ExtensionFilter("文本类型","*.txt"));
//打开文件选择器并返回返回打开的文件所在的完整路径
File file = fc.showOpenDialog(stage);
if(file!=null){
//清除一下
textArea.clear();
String absolutePath = file.getAbsolutePath();
try {
TextAreaUtils.readFileToTextArea(textArea,absolutePath);
} catch (Exception e) {
e.printStackTrace();
}
ownerStage.setTitle(absolutePath);
menuBarFactory.originalPath = new StringBuffer(absolutePath);
}
}
}
三、Utils包
3.1、TextAreaUtils文本框控制工具类
结构
代码
TextAreaUtils.java
package utils;
import javafx.scene.control.Alert;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import java.io.*;
/**
* @ClassName TextAreaUtils
* @Description TODO
* @Author Mr_X
* @Date 2022/5/22 15:44
* @Version 1.0
*/
public class TextAreaUtils {
private static int replacementCount = 0;
private static final int[] FONT_SIZE_OPTIONS = new int[]{4,6,8,10,12,14,
18, 20,22, 25,27,29,33, 35,36, 40,43, 45,47 ,50,53, 55,58, 60,65,69,72};
private static final int DEFAULT_SIZE = 15;
private static int zoomIndex = 7;
/**
* 查找功能的实现,默认向下查找
*
* @description:
* @param: textArea key isCaseIgnored isAllScope isCycle
* @return: void
* @author
* @date: 17:31 2022/5/22
*/
public static void findWords(TextArea textArea, String key, boolean isCaseIgnored, boolean isAllScope, boolean isCycle) {
int findStartIndex = 0;
int keyLength = key.length();
//从光标位置开始查找
int findStartPos = textArea.getCaretPosition();
String src = textArea.getText();
//首先判断是否有这个
boolean hasResult = src.contains(key);
if (!hasResult) {
//没有就直接退出
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("记事本");
alert.setHeaderText("查找结果");
alert.setContentText("找不到" + key);
alert.showAndWait();
return;
}
//范围是循环时,需要重定锚
if (findStartPos >= src.length()) {
if (isCycle) {
findStartPos = 0;
}
}
if (textArea.getSelectedText().isEmpty()) {
if (isCaseIgnored) {
String caseIgnoredStrA = src.toLowerCase();
String caseIgnoredStrB = key.toLowerCase();
findStartIndex = caseIgnoredStrA.indexOf(caseIgnoredStrB, findStartPos);
} else {
findStartIndex = src.indexOf(key, findStartPos);
}
} else {
if (isCaseIgnored) {
String caseIgnoredStrA = src.toLowerCase();
String caseIgnoredStrB = key.toLowerCase();
findStartIndex = caseIgnoredStrA.indexOf(caseIgnoredStrB, findStartPos - caseIgnoredStrB.length() + 1);
} else {
findStartIndex = src.indexOf(key, findStartPos - key.length() + 1);
}
}
if (findStartIndex > -1) {
textArea.selectRange(findStartIndex, findStartIndex + keyLength);
} else {
if (!isCycle) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("记事本");
alert.setHeaderText("查找结果");
alert.setContentText("找不到" + key);
alert.showAndWait();
} else {
//从头找
String s;
String k;
//循环,重定位
if (isCaseIgnored) {
s = src.toLowerCase();
k = key.toLowerCase();
} else {
s = src;
k = key;
}
findStartIndex = s.indexOf(k);
textArea.selectRange(findStartIndex, findStartIndex + keyLength);
}
}
}
/**
* 向上查询的实现
*
* @description:
* @param: textArea key isCaseIgnored isAllScope isCycle
* @return: void
* @author
* @date: 17:34 2022/5/22
*/
public static void backSearch(TextArea textArea, String key, boolean isCaseIgnored, boolean isAllScope, boolean isCycle) {
int findStartIndex = 0;
int keyLength = key.length();
//从光标位置开始查找
int findStartPos = textArea.getCaretPosition();
String src = textArea.getText();
boolean hasResult = src.contains(key);
if (!hasResult) {
//没有就直接退出
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("记事本");
alert.setHeaderText("查找结果");
alert.setContentText("找不到" + key);
alert.showAndWait();
return;
}
//范围是循环时,需要重定锚
if (findStartPos == 0) {
if (isCycle) {
findStartPos = src.length();
}
}
String strTemp = src.substring(0, findStartPos);
if (textArea.getSelectedText().isEmpty()) {
strTemp = src.substring(0, findStartPos);
if (isCaseIgnored) {
String caseIgnoredStrTemp = strTemp.toLowerCase();
String caseIgnoredStrB = key.toLowerCase();
findStartIndex = caseIgnoredStrTemp.lastIndexOf(caseIgnoredStrB);
} else {
findStartIndex = strTemp.lastIndexOf(key);
}
} else {
//如果当前有选中的文本,右移一单位
strTemp = src.substring(0, strTemp.length() - 1);
if (isCaseIgnored) {
String caseIgnoredStrTemp = strTemp.toLowerCase();
String caseIgnoredStrB = key.toLowerCase();
findStartIndex = caseIgnoredStrTemp.lastIndexOf(caseIgnoredStrB);
} else {
findStartIndex = strTemp.lastIndexOf(key);
}
}
//查找失败时索引返回值为-1
if (findStartIndex > -1) {
textArea.selectRange(findStartIndex, findStartIndex + keyLength);
} else {
if (!isCycle) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("记事本");
alert.setHeaderText("查找结果");
alert.setContentText("找不到" + key);
alert.showAndWait();
} else {
String s;
String k;
//循环,重定位
if (isCaseIgnored) {
s = src.toLowerCase();
k = key.toLowerCase();
} else {
s = src;
k = key;
}
findStartIndex = s.lastIndexOf(k);
textArea.selectRange(findStartIndex, findStartIndex + keyLength);
}
}
}
/**
* 替换功能的实现
*
* @description:
* @param: textArea key replacement isCaseIgnored isAllScope isCycle
* @return: void
* @author
* @date: 17:48 2022/5/22
*/
public static void replace(TextArea textArea, String key, String replacement, boolean isCaseIgnored, boolean isAllScope, boolean isCycle) {
int findStartIndex = 0;
int keyLength = key.length();
//从光标位置开始查找
int findStartPos = textArea.getCaretPosition();
String src = textArea.getText();
//范围是循环时,需要重定锚
if (findStartPos >= src.length()) {
if (isCycle) {
findStartPos = 0;
}
}
if (textArea.getSelectedText().isEmpty()) {
if (isCaseIgnored) {
String caseIgnoredStrA = src.toLowerCase();
String caseIgnoredStrB = key.toLowerCase();
findStartIndex = caseIgnoredStrA.indexOf(caseIgnoredStrB, findStartPos);
} else {
findStartIndex = src.indexOf(key, findStartPos);
}
} else {
if (isCaseIgnored) {
String caseIgnoredStrA = src.toLowerCase();
String caseIgnoredStrB = key.toLowerCase();
findStartIndex = caseIgnoredStrA.indexOf(caseIgnoredStrB, findStartPos - caseIgnoredStrB.length() + 1);
} else {
findStartIndex = src.indexOf(key, findStartPos - key.length() + 1);
}
}
//查找失败时索引返回值为-1
if (findStartIndex > -1) {
textArea.positionCaret(findStartIndex);
textArea.selectRange(findStartIndex, findStartIndex + keyLength);
try {
if (replacement.length() == 0 && !textArea.getSelectedText().isEmpty()) {
textArea.replaceSelection("");
}
if (replacement.length() > 0 && !textArea.getSelectedText().isEmpty()) {
textArea.replaceSelection(replacement);
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("记事本");
alert.setHeaderText("查找结果");
alert.setContentText("找不到" + key);
alert.showAndWait();
}
}
/**
* 实现全部替换功能
*
* @description:
* @param: textArea key replacement isCaseIgnored
* @return: void
* @author
* @date: 19:55 2022/5/22
*/
public static void replaceAll(TextArea textArea, String key, String replacement, boolean isCaseIgnored) {
textArea.positionCaret(0);
int findStartIndex = 0;
int keyLength = key.length();
if (!textArea.getText().contains(key)) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("替换");
alert.setHeaderText("替换结果");
alert.setContentText("替换 " + replacementCount + "处!");
alert.showAndWait();
}
while (findStartIndex > -1) {
int findStartPos = textArea.getCaretPosition();
String src = textArea.getText();
if (textArea.getSelectedText().isEmpty()) {
if (isCaseIgnored) {
String caseIgnoredStrA = src.toLowerCase();
String caseIgnoredStrB = key.toLowerCase();
findStartIndex = caseIgnoredStrA.indexOf(caseIgnoredStrB, findStartPos);
} else {
findStartIndex = src.indexOf(key, findStartPos);
}
} else {
if (isCaseIgnored) {
String caseIgnoredStrA = src.toLowerCase();
String caseIgnoredStrB = key.toLowerCase();
findStartIndex = caseIgnoredStrA.indexOf(caseIgnoredStrB, findStartPos - caseIgnoredStrB.length() + 1);
} else {
findStartIndex = src.indexOf(key, findStartPos - key.length() + 1);
}
}
if (findStartIndex > -1) {
textArea.positionCaret(findStartIndex);
textArea.selectRange(findStartIndex, findStartIndex + keyLength);
} else {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("替换");
alert.setHeaderText("替换结果");
alert.setContentText("成功替换 " + replacementCount + "处!");
alert.showAndWait();
//重置
replacementCount = 0;
}
if (replacement.length() == 0 && !textArea.getSelectedText().isEmpty()) {
textArea.replaceSelection("");
replacementCount++;
}
if (replacement.length() > 0 && !textArea.getSelectedText().isEmpty()) {
textArea.replaceSelection(replacement);
replacementCount++;
}
}
}
/**
* 转到指定行
* @description:
* @param: textArea targetLine
* @return: void
* @author
* @date: 22:08 2022/5/22
*/
public static void turnToTargetLine(TextArea textArea, int targetLine) {
int lineCount;
String strA, strB;
String[] strings;
strA = textArea.getText();
strings = strA.split("\n");
lineCount = strings.length + 1;
textArea.positionCaret(0);
if (targetLine >= lineCount) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("跳行");
alert.setHeaderText("");
alert.setContentText("行数超过了总行数");
alert.showAndWait();
return;
}
int i = 0;
int findStartPos = textArea.getCaretPosition();
while (findStartPos <= strA.length()) {
findStartPos = textArea.getCaretPosition();
textArea.selectRange(0, findStartPos);
strB = textArea.getSelectedText();
strings = strB.split("\n");
i = strings.length;
if (i == targetLine) {
textArea.positionCaret(findStartPos);
break;
}
findStartPos++;
textArea.positionCaret(findStartPos);
}
}
/** 放大文本
* @description:
* @param: textArea
* @return: void
* @author
* @date: 16:51 2022/5/23
*/
public static void enLarge(TextArea textArea) {
zoomIndex++;
if (zoomIndex == FONT_SIZE_OPTIONS.length) {
zoomIndex--;
}
textArea.setStyle("-fx-font-size: " + FONT_SIZE_OPTIONS[zoomIndex]);
}
/** 缩小文本
* @description:
* @param: textArea
* @return: void
* @author
* @date: 16:52 2022/5/23
*/
public static void shrink(TextArea textArea) {
zoomIndex--;
if (zoomIndex == -1) {
zoomIndex++;
}
textArea.setStyle("-fx-font-size: " + FONT_SIZE_OPTIONS[zoomIndex]);
}
/** 更新状态栏
* @description:
* @param: textArea statusLabel
* @return: void
* @author
* @date: 17:34 2022/5/23
*/
public static void updateStatusLabel(TextArea textArea, Label statusLabel){
double zoomPercentage= 1;
int columnIndex;
int rowIndex;
int findStartPos;
String src = textArea.getText();
findStartPos = textArea.getCaretPosition();
String aboveContent = src.substring(0, findStartPos);
String[] stringLines = aboveContent.split("\n");
rowIndex = stringLines.length;
if(rowIndex == 0)
{
src += "*";
aboveContent = src.substring(0, findStartPos + 1);
stringLines = aboveContent.split("\n");
rowIndex = stringLines.length;
columnIndex = stringLines[rowIndex - 1].length() - 1;
}
else {
columnIndex = stringLines[rowIndex - 1].length();
}
zoomPercentage = textArea.getFont().getSize()/DEFAULT_SIZE*100;
double floor = Math.floor(zoomPercentage);
statusLabel.setText(" 第 " + rowIndex + " 行, 第 " + (columnIndex + 1) + " 列 " + " 共 " + src.length() + " 字 " + " 缩放:" + floor + "%");
}
/** 将指定的文本框内容写入到指定的文件路径进行保存操作
* @description:
* @param: textArea path
* @return: void
* @author
* @date: 18:47 2022/5/23
*/
public static void saveTextAreaContent(TextArea textArea,String path) throws Exception {
String tempPath = path;
if(path.contains("*")){
tempPath = path.replace("*","");
}
FileWriter fileWriter = new FileWriter(tempPath);
BufferedWriter bw = new BufferedWriter(fileWriter);
String content = textArea.getText();
bw.write(content);
bw.close();
}
/** 读取指定路径文件内容至文本框
* @description:
* @param: textArea path
* @return: void
* @author
* @date: 22:23 2022/5/23
*/
public static void readFileToTextArea(TextArea textArea,String path) throws Exception {
FileReader fileReader = new FileReader(path);
BufferedReader br = new BufferedReader(fileReader);
String line;
while((line = br.readLine())!=null){
textArea.appendText(line);
textArea.appendText("\n");
}
br.close();
}
}
四、Resource包
logo.jpg
注意:如果出现资源路径错误(程序报错logo.jpg找不到路径),那就将所有代码中出现的下列语句及其相关变量(rs1
)删除即可,或者将路径变为图片的绝对路径即可
InputStream rs1 = Thread.currentThread().getContextClassLoader().getResourceAsStream("resource/logo.jpg");
五、记事本入口
Main类
代码
package main;
import domain.FileSavePromptFactory;
import domain.StatusLabelFactory;
import domain.MenuBarFactory;
import domain.TextAreaFactory;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.InputStream;
/**
* @ClassName ShowPaneTest
* @Description TODO
* @Author Mr_X
* @Date 2022/5/20 9:16
* @Version 1.0
*/
public class Main extends Application {
private final static double STAGE_WIDTH = 600;
private final static double STAGE_HEIGHT = 600;
private final MenuBarFactory menuBarFactory = new MenuBarFactory();
private final TextAreaFactory textAreaFactory = new TextAreaFactory();
private final StatusLabelFactory statusLabelFactory = new StatusLabelFactory();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
AnchorPane menuAnc = new AnchorPane();
menuAnc.setStyle(
"-fx-background-color:#c7c3c3;"
);
menuBarFactory.setOwnerAnc(menuAnc);
menuAnc.setPrefWidth(STAGE_WIDTH);
AnchorPane contentAnc = new AnchorPane();
textAreaFactory.setOwnerAnc(contentAnc);
Label statusLabel = statusLabelFactory.getLabel(STAGE_WIDTH,textAreaFactory.textArea);
textAreaFactory.bindTextAreaLabel(statusLabel);
BorderPane borderPane = new BorderPane(contentAnc, menuAnc, null, statusLabel, null);
BorderPane.setAlignment(contentAnc, Pos.CENTER);
BorderPane.setAlignment(menuAnc, Pos.CENTER);
BorderPane.setAlignment(statusLabel, Pos.CENTER);
borderPane.setPadding(new Insets(0.0));
Scene scene = new Scene(borderPane);
primaryStage.setScene(scene);
primaryStage.setTitle("无标题");
primaryStage.setHeight(STAGE_HEIGHT);
primaryStage.setWidth(STAGE_WIDTH);
InputStream rs1 = Thread.currentThread().getContextClassLoader().getResourceAsStream("resource/logo.jpg");
Image image = new Image(rs1, 50, 50, false, true);
primaryStage.getIcons().add(image);
//核心绑定
menuBarFactory.setTextFactory(textAreaFactory,primaryStage);
primaryStage.show();
primaryStage.setOnCloseRequest(event -> {
//如果带星直接关闭,否则进行保存操作
event.consume();
FileSavePromptFactory.showExistSavePane(textAreaFactory.textArea,primaryStage);
});
}
}
最终效果
写这个的时候陪着我的那个她,还是走了。 --2022.7.9