开源项目地址

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全栈学习路线、学习资源和面试题一条龙

软考证书=职称证书?

软考中级--软件设计师毫无保留的备考分享

三连支持!!!

开源项目YtyMark文本编辑器--UI界面相关功能(关于设计模式的实战运用)的更多相关文章

  1. Quill – 可以灵活自定义的开源的富文本编辑器

    Quill 的建立是为了解决现有的所见即所得(WYSIWYG)的编辑器本身就是所见即所得(指不能再扩张)的问题.如果编辑器不正是你想要的方式,这是很难或不可能对其进行自定义以满足您的需求. Quill ...

  2. vue项目富文本编辑器vue-quill-editor之自定义图片上传

    使用富文本编辑器的第一步肯定是先安装依赖 npm i vue-quill-editor 1.如果按照官网富文本编辑器中的图片上传是将图片转为base64格式的,如果需要上传图片到自己的服务器,需要修改 ...

  3. KindEditor 开源得富文本编辑器

    正常HTML情况写输入长文本需要textarea 标签 .但textarea 标签局限性很大,切只能输入单一的文本,我们大多情况下看到的新闻类文本信息大多是图文混排得,且有的配有视频和音乐. 我们可以 ...

  4. 【Android】7.8 MyDemos项目的结构和主界面相关代码

    分类:C#.Android.VS2015: 创建日期:2016-02-17 一.简介 上一讲已经说过,系统升级为Win10后,重新创建了一个新的项目:MyDemos,并把前7章合并到了这个项目中,这次 ...

  5. python小项目之文本编辑器

    高考完后这么久才想起这系列教程,实在抱歉,现在该来继续教程了. 本节利用前面所学知识,来完成一个小工具--文本编辑器! tkinter 在实现文本编辑器之前,先来了解下tkinter这个python库 ...

  6. bbs项目富文本编辑器实现上传文件到media目录

    media目录是在project的settings中设置的,static目录是django自己使用的静态文件的上传目录,media目录是用户自定义上传文件的目录 # Django用户上传的文件都放在m ...

  7. springboot中使用kindeditor富文本编辑器实现博客功能

    kindeditor在之前已经用过,现在在springboot项目中使用.并且也在里面使用了图片上传以及回显等功能. 其实主要的功能是图片的处理:kindeditor对输入的内容会作为html标签处理 ...

  8. 【代码导读】Github 开源项目——wysihtml5 富编辑器(Bootstrap 风格)【一】

    如果你经常留迹于各大论坛.博客,肯定对它们的富编辑器稍有印象.纯 Javascript 富编辑器可以说是前台 JS 脚本的巅峰作品.一款完整的编辑器,其复杂的功能,会让你遇到各种头痛的浏览器兼容问题, ...

  9. UI界面相关

    在开发中有些控件或者控件显示的属性需要经常设置,但是又是万变不离其中,经常写着一样的代码会显得冗余,不利于阅读.这里做了简化. 1.UI控件 2.颜色管理 3.图片管理 4.字体选择

  10. 使用 Reactjs + Mobx + React-Router 开发项目时 VSCode 编辑器报警 TS 相关的问题(提示experimentalDecorators )

    vscode 对于 JS support 的支持需要配置,在项目根目录下创建一个 jsconfig.json 文件,把以下内容贴入后保存,重启项目即可生效(去掉提示). { "compile ...

随机推荐

  1. Delphi中的变体Variant数组相关函数

    转自:Delphi中的变体Variant数组相关函数 1.VarArrayCreate procedure TForm1.Button1Click(Sender: TObject); var   V, ...

  2. JMeter中使用嵌套变量方法

    JMeter中使用嵌套变量方法 在使用JMeter进行接口测试时,经常遇到需要动态生成变量名并引用其值的情况.在JMeter中,嵌套变量的使用可以帮助我们实现这一目标.本文将详细介绍如何在JMeter ...

  3. 使用MTR网络诊断

    安装 MTR Ubuntu: apt update apt upgrade apt install mtr-tiny CentOS: yum update yum install mtr 使用 MTR ...

  4. 二次剩余和 Cipolla 算法

    首先是素数模同余方程的相关理论. 下设 $p\in $ 是质数,\(f(x)=\sum_{i=0}^n a_ix^i\),\(x\in \Z_p,p\not\mid a_n\). 引理 1 如果 \( ...

  5. C# 钩子函数使用

    1. 什么是钩子 hook(钩子)是windows提供的一种消息处理机制平台,是指在程序正常运行中接受信息之前预先启动的函数,用来检查和修改传给该程序的信息,(钩子)实际上是一个处理消息的程序段,通过 ...

  6. 【收藏】default.rdp配置

    原文链接:  https://www.cnblogs.com/vman/archive/2011/12/05/2276895.html 存储在 Default.rdp 文件中的设置 默认情况下,将在& ...

  7. Google 助手安装

    转自:https://www.jianshu.com/p/6086ec29c173 今天这篇推送可能是很多人都需要的,因为它能帮助你无阻碍的访问Google.Google scholar.Gmail. ...

  8. K230学习记录

    K230学习记录 参考自: # 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源 # 开发板官网:www.lckfb.com # 技术支持常驻论坛,任何技术问题欢迎随时交 ...

  9. wikidata介绍和查询

      Wikidata是一个大型结构化开源知识图,为维基百科等项目提供支持.我们可使用SPARQL(Wikidata官方Tutorial)对其进行查询.SPARQL是一种专为 RDF(Resource ...

  10. FormCreate中在事件中获取api

    form-create中在事件中获取api FormCreate 是一个可以通过 JSON 生成具有动态渲染.数据收集.验证和提交功能的表单生成组件.支持5个UI框架,并且支持生成任何 Vue 组件. ...