前提

某一天点开掘金的写作界面的时候,发现了内置Markdown编辑器有一个Github的图标,点进去就是一个开源的Markdown编辑器项目bytemdhttps://github.com/bytedance/bytemd):

这是一个NodeJs项目,由字节跳动提供。联想到之前业余的时候做过一些Swing或者JavaFxDemo,记得JavaFx中有一个组件WebView已经支持Html5CSS3ES5,这个组件作为一个嵌入式浏览器,可以轻松地渲染一个URL里面的文本内容或者直接渲染一个原始的Html字符串。另外,由于原生的JavaFx的视觉效果比较丑,可以考虑引入Swing配合IntelliJ IDEA的主题提供更好的视觉效果。本文的代码基于JDK11开发。

引入依赖

很多人吐槽过Swing组件的视觉效果比较差,原因有几个:

  • 技术小众,现在有更好的组件进行混合开发和跨平台开发
  • 基于上一点原因,导致很少人会去开发Swing组件的UI,其实Swing的每个组件都可以重新实现UI的表现效果
  • compose-jbJetBrains)组件很晚才发布出来,刚好碰上Swing官方停止维护,后面应该更加少人会使用SwingGUI开发

使用Swing并且成功了的方案最知名的就是JetBrains全家桶。目前来看,为了解决这个"丑"的问题,现在有比较简单的处理方案:

  • 方案一:使用compose-jb(名字有点不好听,官方仓库为https://github.com/JetBrains/compose-jb)开发,这个是JetBrains系列的通用组件,基于Swing做二次封装,不过必须使用语言Kotlin,有点强买强卖的嫌疑,这列贴两个官方的图参考一下:

  • 方案二:FormDev(之前推出过Swing布局器的开发商,官网https://www.formdev.com/flatlaf)提供的FlatLafFlat Look and Feel),提供了Light Dark IntelliJ and Darcula themes,而且依赖少,使用起来十分简单,个人认为当前这个是Swing UI组件视觉效果首选

引入FlatLafOpenFx的依赖:

<dependency>
<groupId>com.formdev</groupId>
<artifactId>flatlaf</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>com.formdev</groupId>
<artifactId>flatlaf-intellij-themes</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>11.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>11.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>11.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
<version>11.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>11.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>11.0.2</version>
</dependency>

布局和实现

布局的实现比较简单:

最终的H5文本渲染在WebView组件中(JFXPanelJavaFx => Swing的适配器,WebViewJavaFx的组件,但是这里使用的外层容器都是Swing组件),具体的编码实现如下:

public class MarkdownEditor {

    private static final int W = 1200;
private static final int H = 1000;
private static final String TITLE = "markdown editor"; public static String CONTENT = "<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\"/>\n" +
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\n" +
" <title>ByteMD example</title>\n" +
" <link rel=\"stylesheet\" href=\"https://unpkg.com/bytemd/dist/index.min.css\"/>\n" +
" <link rel=\"stylesheet\" href=\"https://unpkg.com/github-markdown-css\"/>\n" +
" <script src=\"https://unpkg.com/bytemd\"></script>\n" +
" <script src=\"https://unpkg.com/@bytemd/plugin-gfm\"></script>\n" +
" <script src=\"https://unpkg.com/@bytemd/plugin-highlight\"></script>\n" +
" <style>\n" +
" .bytemd {\n" +
" height: calc(100vh - 50px);\n" +
" }\n" +
"\n" +
" .footer {\n" +
" width: 100%;\n" +
" height: 30px;\n" +
" left: 0;\n" +
" position: absolute;\n" +
" bottom: 0;\n" +
" text-align: center;\n" +
" }\n" +
" </style>\n" +
"</head>\n" +
"<body>\n" +
"<div class=\"footer\">\n" +
" <a href=\"https://github.com/bytedance/bytemd\">bytemd</a>\n" +
"</div>\n" +
"<script>\n" +
" const plugins = [bytemdPluginGfm(), bytemdPluginHighlight()];\n" +
" const editor = new bytemd.Editor({\n" +
" target: document.body,\n" +
" props: {\n" +
" value: '# heading\\n\\nparagraph\\n\\n> blockquote',\n" +
" plugins,\n" +
" },\n" +
" });\n" +
" editor.$on('change', (e) => {\n" +
" editor.$set({value: e.detail.value});\n" +
" });\n" +
"</script>\n" +
"</body>\n" +
"</html>"; static {
// 初始化主题
try {
UIManager.setLookAndFeel(FlatIntelliJLaf.class.getName());
} catch (Exception e) {
throw new IllegalStateException("theme init error", e);
}
} private static JFrame buildFrame(int w, int h, LayoutManager layoutManager) {
JFrame frame = new JFrame();
frame.setLayout(layoutManager);
frame.setTitle(TITLE);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(w, h);
Toolkit toolkit = Toolkit.getDefaultToolkit();
int x = (int) (toolkit.getScreenSize().getWidth() - frame.getWidth()) / 2;
int y = (int) (toolkit.getScreenSize().getHeight() - frame.getHeight()) / 2;
frame.setLocation(x, y);
return frame;
} private static void initAndDisplay() {
// 构建窗体
JFrame frame = buildFrame(W, H, new BorderLayout());
JFXPanel panel = new JFXPanel();
Platform.runLater(() -> {
panel.setSize(W, H);
initWebView(panel, CONTENT);
frame.getContentPane().add(panel);
});
frame.setVisible(true);
} public static void main(String[] args) {
SwingUtilities.invokeLater(MarkdownEditor::initAndDisplay);
} private static void initWebView(JFXPanel fxPanel, String content) {
StackPane root = new StackPane();
Scene scene = new Scene(root);
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
webEngine.setJavaScriptEnabled(true);
webEngine.loadContent(content);
root.getChildren().add(webView);
fxPanel.setScene(scene);
}
}

H5文本来源于bytemd的原生JS实现例子:

所有代码加上注释大概120多行。使用JDK11运行,结果如下:

目前有2个没有解决的问题(也有可能是):

  • JS的动作触发有轻微延迟
  • WebView组件初始化比较慢

小结

Oracle JDK官方已经宣布不再维护Swing项目,按照一般的尿性后面有可能从JDK中移除,也许是因为它体现不了自身的价值(低情商:不赚钱)。Swing的开发中布局是比较反人类的,一般可能一个Swing项目布局会耗费90%以上的时间,原生组件的UI设计看上去比较"丑",没有丰富的扩展组件和活跃的社区,加上现在有其他更好的跨平台开发方案如QtReact NativeFlutter等等,Swing被遗忘是一个既定的结局。往后除了一枝独秀的JetBrainsSwing的结局就是成为少数人业务的爱好,成为JDK GUI编程爱好者的收藏品。

Demo源码:

(本文完 e-a-20210815 c-1-d)

基于Java和Bytemd用120行代码实现一个桌面版Markdown编辑器的更多相关文章

  1. 转载 基于JAVA每月运势api调用代码实例

    代码描述:基于JAVA每月运势api调用代码实例 接口地址:http://www.juhe.cn/docs/api/id/58 原文链接:http://outofmemory.cn/code-snip ...

  2. 通过 Mesos、Docker 和 Go,使用 300 行代码创建一个分布式系统

    [摘要]虽然 Docker 和 Mesos 已成为不折不扣的 Buzzwords ,但是对于大部分人来说它们仍然是陌生的,下面我们就一起领略 Mesos .Docker 和 Go 配合带来的强大破坏力 ...

  3. 通过Mesos、Docker和Go,使用300行代码创建一个分布式系统

    [摘要]虽然 Docker 和 Mesos 已成为不折不扣的 Buzzwords ,但是对于大部分人来说它们仍然是陌生的,下面我们就一起领略 Mesos .Docker 和 Go 配合带来的强大破坏力 ...

  4. 120行代码打造.netcore生产力工具-小而美的后台异步组件

    相信绝大部分开发者都接触过用户注册的流程,通常情况下大概的流程如下所示: 接收用户提交注册信息 持久化注册信息(数据库+redis) 发送注册成功短信(邮件) 写操作日志(可选) 伪代码如下: pub ...

  5. 37行代码实现一个简单的打游戏AI

    不废话,直接上码,跟神经网络一点关系都没有,这37行代码只能保证电脑的对敌牺牲率是1:10左右,如果想手动操控,注释掉autopilot后边的代码即可. 哪个大神有兴趣可以用tensorflow或者s ...

  6. Html5游戏开发-145行代码完成一个RPG小Demo

    lufy前辈写过<[代码艺术]17行代码的贪吃蛇小游戏>一文,忽悠了不少求知的兄弟进去阅读,阅读量当然是相当的大.今天我不仿也搞一个这样的教程,目地不在于忽悠人,而在于帮助他人. 先看de ...

  7. SpringBoot,用200行代码完成一个一二级分布式缓存

    缓存系统的用来代替直接访问数据库,用来提升系统性能,减小数据库复杂.早期缓存跟系统在一个虚拟机里,这样内存访问,速度最快. 后来应用系统水平扩展,缓存作为一个独立系统存在,如redis,但是每次从缓存 ...

  8. 【编程教室】PONG - 100行代码写一个弹球游戏

    大家好,欢迎来到 Crossin的编程教室 ! 今天跟大家讲一讲:如何做游戏 游戏的主题是弹球游戏<PONG>,它是史上第一款街机游戏.因此选它作为我这个游戏开发系列的第一期主题. 游戏引 ...

  9. [转]通过Mesos、Docker和Go,使用300行代码创建一个分布式系统

    http://www.csdn.net/article/2015-07-31/2825348 [编者按]时下,对于大部分IT玩家来说,Docker和Mesos都是熟悉和陌生的:熟悉在于这两个词无疑已成 ...

随机推荐

  1. Solon 1.5.11 发布,增加国际化插件

    Solon 是一个轻量的Java基础开发框架.强调,克制 + 简洁 + 开放的原则:力求,更小.更快.更自由的体验.支持:RPC.REST API.MVC.Job.Micro service.WebS ...

  2. 『无为则无心』Python函数 — 25、Python中的函数

    目录 1.函数的使用 (1)定义函数 (2)调用函数 (3)使用函数的注意事项 2.函数的参数 3.实参的类型 Python函数的说明: Python中函数的应用非常广泛,前面章节中我们已经接触过多个 ...

  3. Redis:Redis的安装

    Redis优势 性能极高             – Redis能读的速度是110000次/s,写的速度是81000次/s . 丰富的数据类型  – Redis支持二进制案例的 Strings, Li ...

  4. XCTF(MISC) give_you_flag

    题目描述:菜狗找到了文件中的彩蛋很开心,给菜猫发了个表情包 1.下载附件,点击查看 发现在数完钱后,有出现一个二维码的东西. 2.使用stegsolv工具,进行逐帧查看. 说个题外话,stegsolv ...

  5. 第二届 BJD wp(reverse和crypto)

    re 1.第一题拖入ida,flag就是直接明文摆着 2.第二题是8086的程序,拖入ida,发现有个jmp无限跳转,可能是段寄存器被修改了,ida无法将后面的汇编识别出来,所以后面才有很多无效数据, ...

  6. webpack(10)webpack-dev-server搭建本地服务器

    前言 当我们使用webpack打包时,发现每次更新了一点代码,都需要重新打包,这样很麻烦,我们希望本地能搭建一个服务器,然后写入新的代码能够自动检测出来,这时候就需要用到webpack-dev-ser ...

  7. MQTT介绍与使用(转载)

    物联网是新一代信息技术的重要组成部分,也是"信息化"时代的重要发展阶段.其英文名称是:"Internet of things(IoT)".顾名思义,物联网就是物 ...

  8. python django与celery的集成

    一.celery与django 关于celery介绍和使用可以查看上篇Python中任务队列-芹菜celery的使用 关于django的介绍和使用可查看python django框架+vue.js前后 ...

  9. Oracle_RAC共享存储

    <<ASM.sh>> 第2-3步可以使用"附件ASM.sh"脚本执行 此操作说明,此操作在2个节点都执行(可以复制) 编辑/etc/scsi_id.conf ...

  10. 「AGC020F」 Arcs on a Circle

    「AGC020F」 Arcs on a Circle Link 这个题非常 Amazing 啊.果然AtCoder全是智商题 首先你可以注意到数据范围真的是小得离谱,让你想要爆搜. 然后你发现不可做, ...