前提

某一天點開掘金的寫作界面的時候,發現了內置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. [轉]通過Mesos、Docker和Go,使用300行代碼創建一個分布式系統

    http://www.csdn.net/article/2015-07-31/2825348 [編者按]時下,對於大部分IT玩家來說,Docker和Mesos都是熟悉和陌生的:熟悉在於這兩個詞無疑已成 ...

  9. 幹貨分享:用一百行代碼做一個C/C++錶白小程序,程序員的浪漫!

    前言:很多時候,當別人聽到你是程序員的時候.第一印象就是,格子衫.不浪漫.直男.但是程序員一旦浪漫起來,真的沒其他人什麼事了.什麼紀念日,生日,情人節,禮物怎麼送? 做一個浪漫的程序給她,放上你們照片 ...

  10. 不到50行代碼實現一個能對請求並發數做限制的通用RequestDecorator

    使用場景 在開發中,我們可能會遇到一些對异步請求數做並發量限制的場景,比如說微信小程序的request並發最多為5個,又或者我們需要做一些批量處理的工作,可是我們又不想同時對服務器發出太多請求(可能會 ...

隨機推薦

  1. Singleton in C++11 style

    #include <iostream> #include <memory> #include <mutex> class SingletonOld { static ...

  2. MySQL Create Table創建錶

    錶的創建命令需要: 錶的名稱 字段名稱 定義每個字段(類型.長度等) 語法 下面是通用的SQL語法用來創建MySQL錶: CREATE TABLE table_name (column_name co ...

  3. jquery怎麼跳出當前的each循環

    有些朋友可能會以為在jquery跳出循環可以直接使用continue和break了,但是使用之後沒有效果,因為在jquery中沒有這兩條命令. 後來上網查了下,得到了結果: return false; ...

  4. Cocos2dx遊戲源碼合集(BY懶骨頭+持續更新+2014.02.21)

    轉自:http://blog.csdn.net/iamlazybone/article/details/19612941 聲明: <蘿莉快跑><喵汪大戰>兩個demo的原作者b ...

  5. Codeforces Round #310 (Div. 2)--A(簡單題)

    http://codeforces.com/problemset/problem/556/A 題意:給一個01字符串,把所有相鄰的0和1去掉,問還剩下幾個0和1. 題解:統計所有的0有多少個,1有多少 ...

  6. [Python接口自動化]從零開始學習python自動化(1):環境搭建

    第一步:安裝python編譯環境 安裝python編譯環境之前,必須保證已安裝jdk哈,如果為安裝,請參考https://jingyan.baidu.com/article/6dad5075d1dc4 ...

  7. 更換jupyter notebook風格主題、修改默認工作路徑(Ubuntu系統和Win系統)

    默認的風格對代碼關鍵詞的顏色提醒很不明顯,而且白色背景長久使用非常刺眼,所以考慮更換主題. 在更換途中,發現代碼輸出行,前幾個字符被遮擋顯示不出來,找了很久才解决,備忘一些要點. 1:替換主題方法 h ...

  8. python之數據類型

    1.整數(int)integer 直接寫出數字就是整數例: a = 0#查看變量的數據類型 type() -> #<class 'int'> class類,類型,類別print(10 ...

  9. (轉)在.net中檢索HTTP請求

    原文轉載:https://www.west-wind.com/presentations/dotnetWebRequest/dotnetWebRequest.htm HTTP內容檢索是應用程序的重要組 ...

  10. MongoDB(課時3 MongoDB基本操作)

    3.3 MongoDB的基本操作 在MongoDB數據庫裏面存在數據庫的概念,但沒有模式(所有的信息都是按照文檔保存的),保存數據的結構是BSON結構,只不過在進行一些數據處理的時候才會使用到Mong ...