zoukankan      html  css  js  c++  java
  • 基于Java和Bytemd用120行代码实现一个桌面版Markdown编辑器

    前提

    某一天点开掘金的写作界面的时候,发现了内置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>
    " +
                "<html lang="en">
    " +
                "<head>
    " +
                "    <meta charset="UTF-8"/>
    " +
                "    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    " +
                "    <title>ByteMD example</title>
    " +
                "    <link rel="stylesheet" href="https://unpkg.com/bytemd/dist/index.min.css"/>
    " +
                "    <link rel="stylesheet" href="https://unpkg.com/github-markdown-css"/>
    " +
                "    <script src="https://unpkg.com/bytemd"></script>
    " +
                "    <script src="https://unpkg.com/@bytemd/plugin-gfm"></script>
    " +
                "    <script src="https://unpkg.com/@bytemd/plugin-highlight"></script>
    " +
                "    <style>
    " +
                "        .bytemd {
    " +
                "            height: calc(100vh - 50px);
    " +
                "        }
    " +
                "
    " +
                "        .footer {
    " +
                "             100%;
    " +
                "            height: 30px;
    " +
                "            left: 0;
    " +
                "            position: absolute;
    " +
                "            bottom: 0;
    " +
                "            text-align: center;
    " +
                "        }
    " +
                "    </style>
    " +
                "</head>
    " +
                "<body>
    " +
                "<div class="footer">
    " +
                "    <a href="https://github.com/bytedance/bytemd">bytemd</a>
    " +
                "</div>
    " +
                "<script>
    " +
                "    const plugins = [bytemdPluginGfm(), bytemdPluginHighlight()];
    " +
                "    const editor = new bytemd.Editor({
    " +
                "        target: document.body,
    " +
                "        props: {
    " +
                "            value: '# heading\n\nparagraph\n\n> blockquote',
    " +
                "            plugins,
    " +
                "        },
    " +
                "    });
    " +
                "    editor.$on('change', (e) => {
    " +
                "        editor.$set({value: e.detail.value});
    " +
                "    });
    " +
                "</script>
    " +
                "</body>
    " +
                "</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)

  • 相关阅读:
    Vue.js+express建站
    单页应用(SPA)简介
    Z形记之CentOS7
    Z形记之比较两个目录下文件异同
    Z形记之Linux的那些事:安装Nginx
    SQL之修改字段类型
    Scrapy系列之爬取豆瓣电影
    Scrapy和MongoDB的应用---爬取
    BigDecimal类的常用算法
    回到顶部功能
  • 原文地址:https://www.cnblogs.com/throwable/p/15143795.html
Copyright © 2011-2022 走看看