基于Java和Bytemd用120行代码实现一个桌面版Markdown编辑器
原文出自:公众号 Throwable
原文链接:https://mp.weixin.qq.com/s/c9s47Yf_INZD-sKnN4RCBQ前提
某一天点开掘金的写作界面的时候,发现了内置Markdown编辑器有一个Github的图标,点进去就是一个开源的Markdown编辑器项目bytemd(https://github.com/bytedance/bytemd):
这是一个NodeJs项目,由字节跳动提供。联想到之前业余的时候做过一些Swing或者JavaFx的Demo,记得JavaFx中有一个组件WebView已经支持Html5、CSS3和ES5,这个组件作为一个嵌入式浏览器,可以轻松地渲染一个URL里面的文本内容或者直接渲染一个原始的Html字符串。另外,由于原生的JavaFx的视觉效果比较丑,可以考虑引入Swing配合IntelliJ IDEA的主题提供更好的视觉效果。本文的代码基于JDK11开发。 引入依赖
很多人吐槽过Swing组件的视觉效果比较差,原因有几个: 技术小众,现在有更好的组件进行混合开发和跨平台开发 基于上一点原因,导致很少人会去开发Swing组件的UI,其实Swing的每个组件都可以重新实现UI的表现效果 compose-jb(JetBrains)组件很晚才发布出来,刚好碰上Swing官方停止维护,后面应该更加少人会使用Swing做GUI开发
使用Swing并且成功了的方案最知名的就是JetBrains全家桶。目前来看,为了解决这个"丑"的问题,现在有比较简单的处理方案: 方案一:使用compose-jb(名字有点不好听,官方仓库为https://github.com/JetBrains/compose-jb)开发,这个是JetBrains系列的通用组件,基于Swing做二次封装, 「不过必须使用语言Kotlin,有点强买强卖的嫌疑」 ,这列贴两个官方的图参考一下:
方案二:FormDev(之前推出过Swing布局器的开发商,官网https://www.formdev.com/flatlaf)提供的FlatLaf(Flat Look and Feel),提供了Light Dark IntelliJ and Darcula themes,而且依赖少,使用起来十分简单,个人认为当前这个是Swing UI组件视觉效果首选
引入FlatLaf和OpenFx的依赖: com.formdev flatlaf 1.5 com.formdev flatlaf-intellij-themes 1.5 org.openjfx javafx-media 11.0.2 org.openjfx javafx-swing 11.0.2 org.openjfx javafx-web 11.0.2 org.openjfx javafx-base 11.0.2 org.openjfx javafx-graphics 11.0.2 org.openjfx javafx-controls 11.0.2 布局和实现
布局的实现比较简单:
最终的H5文本渲染在WebView组件中(JFXPanel是JavaFx => Swing的适配器,WebView是JavaFx的组件,但是这里使用的外层容器都是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 = " " + " " + " " + " " + " " + " ByteMD example " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " bytemd " + " " + " " + " " + ""; 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设计看上去比较"丑",没有丰富的扩展组件和活跃的社区,加上现在有其他更好的跨平台开发方案如Qt、React Native和Flutter等等,Swing被遗忘是一个既定的结局。往后除了一枝独秀的JetBrains,Swing的结局就是成为少数人业务的爱好,成为JDK GUI编程爱好者的收藏品。
Demo源码: local-markdown-editor(https://gitee.com/throwableDoge/local-markdown-editor)