🙌開源項目地址
🌍 GitHub 開源地址(YtyMark-java)
歡迎提交 PR、Issue、Star ⭐️!
📌1. 簡述
YtyMark-java項目分為兩大模塊:
-
UI界面(ytyedit-mark)
-
markdown文本解析和渲染(ytymark)
本文主要內容為UI界面相關功能。
關於markdown文本解析器UI界面的實現。在這整個流程中,如果通過設計模式實現高內聚低耦合,可重用,易於閲讀,易於擴展,易於維護等。
YtyMark-java
├── ytyedit-mark/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ ├── editor/ # JavaFX UI 界面
│ │ │ │ ├── enums/ # Icon圖標等
│ │ │ │ ├── utils/ # 資源讀取等
│ │ │ │ ├── window/ # 自定義窗口(主窗口、彈框)
│ │ │ │ ├── RenderMarkdown # 解析和渲染
│ │ │ │ ├── YtyEditApplication # 主程序入口
│ │ │ └── resources/
│ │ │ └── css/ # 主題樣式(CSS)
│ │ │ └── fonts/ # 字體集
│ │ │ └── images/ # 圖片
│ ├── README.md
│ └── pom.xml
🧩2. JavaFX 用户界面
目標:為用户提供可視化的文本輸入、實時預覽、編輯、保存、導出(PDF/HTML)和主題切換等功能。
使用到的設計模式:
-
工廠模式:樣式的創建通過工廠模式來完成。
-
策略模式:動態選擇工具界面樣式,完成UI界面的樣式切換。
-
觀察者模式:識別到主題發生變化時執行重新渲染操作;樣式切換後,渲染的文字樣式也需要同步調整,再結合監聽器(觀察者模式)來實現主題變化後重新渲染文本內容,除此之外JavaFX使用了大量的監聽器。
-
單例模式:主題樣式管理器統一管理全局樣式,並提供統一訪問入口。
-
裝飾模式:對自定義基礎彈框做定製化的擴展,實現不同場景所需的彈框。
-
命令模式:封裝工具界面中的功能點及快捷鍵命令。
-
備忘錄模式:負責實現撤銷和恢復功能,實現精細到單字符的撤銷/恢復機制。
2.1. 工廠模式
通過工廠創建樣式,將默認的樣式創建好,統一放入Map中保存,需要時直接從工廠中獲取。
public class StyleFactory {
private static final Map<Key, Style> STYLE_MAP = new HashMap<>();
// 預先註冊默認樣式
static {
registerStyle(WindowType.MAIN_WINDOW, ThemeType.LIGHT, new MainWindowLightStyle());
registerStyle(WindowType.MAIN_WINDOW, ThemeType.DARK, new MainWindowDarkStyle());
registerStyle(WindowType.DIALOG_WINDOW, ThemeType.LIGHT, new DialogWindowLightStyle());
registerStyle(WindowType.DIALOG_WINDOW, ThemeType.DARK, new DialogWindowDarkStyle());
}
public static void registerStyle(WindowType type, ThemeType theme, Style style) {
STYLE_MAP.put(new Key(type, theme), style);
}
public static Style getStyle(WindowType type, ThemeType theme) {
Style style = STYLE_MAP.get(new Key(type, theme));
if (style == null) {
throw new RuntimeException("窗口類型或主題類型不支持: " + type + " - " + theme);
}
return style;
}
...
}
2.2. 策略模式
動態選擇工具界面樣式,完成UI界面的樣式切換。並支持對已有窗口樣式的自由組合,比如深色的主窗口+淺色的彈框。
使用setTheme(ThemeType)方法可以快速切換已經搭配好的主題;使用setStyle可以靈活指定不同窗口的樣式。
public class ThemeContext {
...
// 設置樣式
public void setTheme(ThemeType theme) {
WindowType[] values = WindowType.values();
for (WindowType windowType : values) {
this.themeManager.setStyle(windowType, theme);
}
}
// 自定義設置不同窗體不同樣式
public void setStyle(WindowType type, ThemeType theme) {
this.themeManager.setStyle(type, theme);
}
// 主題切換
public void switchTheme() {
// 清空之前的樣式
scene.getStylesheets().clear();
this.themeManager.applyStyle(WindowType.MAIN_WINDOW, scene);
}
}
2.3. 觀察者模式和單例模式
識別到主題發生變化時執行重新渲染操作。樣式切換後,渲染的文字樣式也需要同步調整,結合監聽器(觀察者模式)來實現主題變化後重新渲染文本內容,除此之外JavaFX使用了大量的監聽器。
自定義的主題監聽器接口:
public interface ThemeChangeListener {
void onThemeChanged();
}
主題監聽器將有主題管理類來統一管理,並結合單例模式,使得主題管理器全局唯一,並提供統一訪問入口ThemeManager.getInstance()。
public class ThemeManager {
private static ThemeManager instance;
private final Map<WindowType, Style> currentStyles = new HashMap<>();
private final List<ThemeChangeListener> listeners = new ArrayList<>();
private ThemeManager() {
// 默認主窗口白天模式,彈窗白色模式
currentStyles.put(WindowType.MAIN_WINDOW, new MainWindowLightStyle());
currentStyles.put(WindowType.DIALOG_WINDOW, new DialogWindowLightStyle());
}
public static ThemeManager getInstance() {
if (instance == null) {
instance = new ThemeManager();
}
return instance;
}
public void setStyle(WindowType type, ThemeType theme) {
currentStyles.put(type, StyleFactory.getStyle(type, theme));
// 通知所有觀察者主題已變更
this.notifyThemeChanged();
}
...
// 註冊觀察者
public void addThemeChangeListener(ThemeChangeListener listener) {
listeners.add(listener);
}
// 註銷觀察者
public void removeThemeChangeListener(ThemeChangeListener listener) {
listeners.remove(listener);
}
// 通知所有觀察者
private void notifyThemeChanged() {
for (ThemeChangeListener listener : listeners) {
listener.onThemeChanged();
}
}
}
通用彈框和渲染處理類實現了監聽器接口,在創建時註冊到監聽器中
public class GenericDialog implements ThemeChangeListener {
public GenericDialog(Stage owner) {
...
this.themeManager.addThemeChangeListener(this);
...
}
/**
* 訂閲主題樣式變更,主題變更後自動切換彈窗樣式
*/
@Override
public void onThemeChanged() {
// 清空之前的樣式
dialogScene.getStylesheets().clear();
themeManager.applyStyle(WindowType.DIALOG_WINDOW,dialogScene);
}
}
渲染處理類監聽相關源碼
public class RenderMarkdown implements ThemeChangeListener {
public RenderMarkdown(Tab tab) {
...
// 註冊自己為 ThemeContext 的監聽者
ThemeManager.getInstance().addThemeChangeListener(this);
...
}
/**
* 訂閲主題樣式變更,主題變更後自動重新渲染內容
*/
@Override
public void onThemeChanged() {
// 主題變更後自動渲染
renderMarkdown(null);
}
}
2.4. 裝飾模式
對自定義基礎彈框做定製化的擴展,實現不同場景所需的彈框。彈框裝飾類通過組合的方式,對通用彈框做定製化設置
public abstract class DialogDecorator {
private GenericDialog dialog;
public DialogDecorator(GenericDialog dialog) {
this.dialog = dialog;
}
public void removeLogo(boolean flag){
dialog.removeLogo(flag);
}
// 委託設置標題
public void setTitle(String title) {
dialog.setTitle(title);
}
// 委託設置主體內容
public void setContent(Node content) {
dialog.setContent(content);
}
// 委託添加內容
public void addContent(Node node) {
dialog.addContent(node);
}
// 委託設置底部按鈕區域
public void setFooter(Node node) {
dialog.setFooter(node);
}
// 委託添加底部按鈕
public void addFooter(Node node) {
dialog.addFooter(node);
}
// 顯示並阻塞,返回用户操作結果
public void showAndWait() {
dialog.showAndWait();
}
}
2.5. 命令模式和備忘錄模式
封裝工具界面中的功能點及快捷鍵命令。備忘錄模式負責實現撤銷和恢復功能,實現精細到單字符的撤銷/恢復機制,主要涉及的類:管理文本狀態TextEditorOriginator、管理撤銷和恢復的棧UndoRedoCaretaker。
public class UndoRedoCaretaker {
private Deque<TextMemento> undoStack = new ArrayDeque<>();
private Deque<TextMemento> redoStack = new ArrayDeque<>();
private TextEditorOriginator originator;
private boolean isUndoRedo = false;
private static final int MAX_HISTORY = 1000; // 最多保存 1000 步
public UndoRedoCaretaker(TextEditorOriginator originator) {
this.originator = originator;
// 存入初始狀態,確保撤銷可用
undoStack.push(originator.save());
}
// 保存
public void saveState(String text, int caretPosition) {
...
}
// 撤銷
public void undo() {
...
}
// 恢復
public void redo() {
...
}
public boolean isUndoRedo() {
return isUndoRedo;
}
public void clear() {
undoStack.clear();
redoStack.clear();
}
}
📸 3. 界面截圖預覽
白天模式的截圖:
夜間模式的截圖:
✏️4. 總結
YtyMark 編輯器界面UI相關功能,將多種設計模式融入到實際應用中,從實踐中積累程序設計經驗。
更多詳細內容可以前往筆者微信公眾號回覆:設計模式,來獲取,後續有關設計模式的新資料都可以從這個入口獲取到。
-
秘籍1設計模式手冊:《掌握設計模式:23種經典模式實踐、選擇、價值與思想》
-
秘籍2練手項目:設計模式實戰項目--markdown文本編輯器軟件開發(已開源)
查看往期設計模式文章的:設計模式
超實用的SpringAOP實戰之日誌記錄
2023年下半年軟考考試重磅消息
通過軟考後卻領取不到實體證書?
計算機算法設計與分析(第5版)
Java全棧學習路線、學習資源和麪試題一條龍
軟考證書=職稱證書?
軟考中級--軟件設計師毫無保留的備考分享
三連支持!!!