我們平常對java程序進行問題排查、性能調優時,如果沒有合適的工具,很多時候會事倍功半,甚至無法繼續進行下去。其實,jdk自身已經提供了很多強大的工具供我們使用。本文就對這些工具做一個概覽性的描述。
筆者的開發環境是:OS X EI Captian 10.11.6
JDK版本:
JAVA_HOME/bin下的工具截圖如下:
昨天發布《JavaFX學習入門篇,想用Java寫窗口應用的同學可以看看》的基礎上,今天繼續介紹JavaFX控件綁定及交互事件處理。昨天的文章中已經對新建立的demo.fxml指定了控制器類DemoController,這個控制器類就是今天學習中要用到的主類。在JavaFX中通過注解@FXML來綁定fxml文件中的控件或控件的交互事件。前面已經在場景中添加了菜單組件,菜單組件中默認已經添加了三個菜單,且都有一個子菜單,目前點擊任何一個菜單都不會有響應。
下面介紹如何響應菜單的點擊事件,首先在Netbeans中雙擊demo.fxml,然后在JavaFX Scene Builder中找到場景中的菜單組件。在左側Document節點下Hierarchy中展開菜單,點擊選中要添加事件監聽的MenuItem,然后在右側Inspector中展開Code節點,在On Action中輸入該菜單點擊時回調函數的名稱,這里函數名稱為closeAction,輸入完成按回車并Ctrl+S保存修改。具體操作如下圖所示:
為MenuItem設置Action回調函數
完成以上操作之后回到Netbeans中,在DemoController中添加剛才的函數,注意需要添加@FXML注解,函數參數可以為空也可以是javafx.event.ActionEvent,參數可根據需要添加,但不是什么參數都可以。本例中函數如下:
@FXML
private void closeAction(ActionEvent event){//函數名必需與fxml中的一致
System.out.println("invoke close menu action");
}
到此菜單事件的綁定已經完成了,同樣的道理其他任何交互控件的事件綁定都類似。每個控件可監聽的事件都不太一樣,例如輸入框可以監聽鼠標事件、鍵盤事件、觸屏事件等等,交互事件監聽都可以參考此操作。
接下來繼續介紹控件的綁定,我們開發一個桌面應用表單元素是必不可少的。我們需要知道用戶輸入了什么內容,或者選擇了哪個值。這些就需要用到控件的綁定,控件綁定操作也很簡單,細心的讀者或許已經發現前面的截圖中On Action之前有個fx:id,沒錯這里就是通過這個id進行控件綁定。我們將一個輸入框(TextField)拖放到場景中,并指定fx:id為input,為了觸發顯示我們輸入的值,再監聽這個輸入框的鍵盤按鍵釋放事件On Key Released,函數名為keyRelease,具體設置如下圖:
添加輸入框并設置ID和key release事件監聽
編輯完場景之后保存,回到Netbeans中在DemoController中添加一個TextField變量及一個方法keyRelease,注意變量及方法都需要添加@FXML注解,此處keyRelease的參數可以為空或javafx.scene.input.KeyEvent,其他參數會拋出IllegalArgumentException: argument type mismatch異常。添加代碼如下:
@FXML
private TextField input;//變量名必需與fxml文件中的fx:id值一致
@FXML
private void keyRelease(KeyEvent event){//函數名必需與fxml中的一致
System.out.println(input.getText());
}
到些控件的綁定也介紹完了,此時可以將項目運行起來看看效果。好奇的小伙伴或許對場景中的輸入框文字“這是輸入框”存在疑問,是默認值嗎?這個是JavaFX的Prompt Text等同于HTML中的placeholder,即輸入提示文字,當該控件得到光標時提示文字會消失。具體設置在右側Inspector中的Properties頁面中,本例源碼已上傳到作者github:https://github.com/ajtdnyy/JavaFXDemo。Prompt Text如下圖所示:
設置輸入框的Prompt Text
ebView2 是越來越香了。
WebView2 不但是 Win11 自帶的系統組件,Win10 也已經自動推送安裝。即使是少量沒有安裝 WebView2 的系統 —— 使用 aardio 中的 web.view 也會自動安裝( 不需要寫任何代碼 )。
我用 WebView2 開發了很多項目,不得不說 WebView2 穩定可靠、性能強悍,接口簡潔,是真的讓人省心。
這里介紹一個適合用于 WebView2 的極簡前端組件 htmx.js ,這個組件最大的特色就是簡單,一學就會,也很容易理解。
我們正常瀏覽一個網頁的過程是在瀏覽器里輸入網址,向 HTTP 服務器發送請求。然后服務器返回 HTML 代碼,瀏覽器顯示頁面。
但是 htmx.js 腦洞大開,讓網頁上的每一個 HTML 節點都可以向服務器發送請求并獲取 HTML,并實時更新頁面上指定的節點。而且不需要寫任何 JavaScript 代碼。
首先我們打開 aardio ,創建 WebView2 工程并選擇 htmx.js 模板:
生成的工程如下:
點『運行』可直接測試效果,點『發布』可生成獨立 EXE 文件 。
在工程管理器中右鍵點『網頁』彈出菜單,然后點『用外部編輯器打開』,如果安裝了 VS Code 會使用 VS Code 打開網頁目錄。
在 VS Code 中點擊并打開 index.html 源碼:
打開 index.html ,先看最簡單的 htmx.js 示例:
<button hx-get="/api/index.aardio"
hx-swap="innerHTML"
hx-trigger="click"
hx-target="#info-div" >
點這里發送 GET 請求
</button><br>
<div id="info-div"></div>
注意看凡是 "hx-" 前綴的屬性都是用于 htmx.js 。
hx-trigger 用于指定在什么事件發生時觸發 HTTP 請求,例如:
hx-trigger="click"
表示在 click 單擊事件發生時觸發請求。
hx-trigger 可使用標準網頁事件名,常用事件如下:
事件名后面還可以添加修飾器,例如修飾器 once 表示只允許觸發一次 :
hx-trigger="click once"
其他事件修飾器:
下面的 HTML 使用了多個事件修飾器:
<input type="text"
hx-trigger="keyup changed delay:500ms"
hx-post="/api/index.aardio" >
這表示在按鍵放開( keyup ),文本框的內容發生改變( changed )時觸發,并且延時 500 毫秒再發送請求。
hx-get 則指定要請求的是哪個后端頁面,例如:
hx-get="/api/index.aardio"
表示事件觸發時,請求 "/api/index.aardio" 這個頁面。因為 aardio 在啟動 SPA 應用時自動指定了后端根目錄為 "/web",所以實際請求的是 "/web/api/aardio" 。
而 hx-swap 則指定要將返回的 HTML 寫入到哪里,"innerHTML" 指定是更新網頁節點內部 HTML,"outerHTML" 指定替換目標網頁節點的全部 HTML ,其他還有 "afterbegin" , "beforebegin" , "beforeend" , "afterend" , "none" 。這些看名字就知道是什么作用,就不解釋了。
hx-target 屬性用 CSS 選擇器指定要寫入的網頁節點,例如:
hx-target="#info-div"
指定服務器返回的 HTML 寫入 id 為 "info-div" 的節點。如果省略 hx-target 屬性表示寫入目標是當前節點自身。
hxmx.js 在更新 HTML 時,如果發現新舊 html 中有 id 相同的元素會進行優化并平滑顯示。
看到這里,htmx.js 您已經會用了。
雖然 htmx.js 文檔里有更多花式用法,但一般可能用不上。有些事搞太復雜了也不一定是好事。
aardio 提供了嵌入式 HTTP 服務器,可以直接使用 aardio 代碼寫網頁,支持與 PHP 類似的模板語法。
aardio 的模板語法很簡單,aardio 代碼寫在 <? ?> 內部,而 HTML 代碼寫在 <? ?> 外部就可以了。實際上 <? ?>外部的代碼被轉換為了 aardio 中 print 函數的參數。
例如服務端有下面的 aardio 代碼:
<span>abc</span>
<?
response.write("123")
?>
運行后會自動轉換為純 aardio 代碼如下:
print("<span>abc</span>");
response.write("123");
在 HTTP 后端中,print 函數實際上就是指向用于向 HTTP 客戶端輸出數據的 response.write() 函數。
在 HTTP 后端有兩個最常用的對象,request 對象包含了所有 HTTP 請求信息,而 response 對象為 HTTP 響應對象,用于向客戶端發送數據。
打開 aardio 自帶「工具 > 庫函數文檔」,點擊 fastcgi.client 的文檔可以查看 request, response 對象的所有屬性與方法。aardio 中的所有 HTTP 服務端實現都統一兼容 fastcgi.client 文檔規定的 request, response 用法。
也可以參考 aardio 開始頁的 《 aardio 網站開發、FastCGI開發入門教程 》。至于 aardio 模板語法,請參考 《 aardio 語法與使用手冊 > aardio 語言 > 模板語法 》
aardio 的模板語法不僅僅可以用于寫 HTTP 后端,也不僅僅是可以用于輸出 HTML,實際上可以用于生成任何字符串。aardio 中的很多功能都支持這種模板語法,例如運行時編譯 C# 代碼就支持用 aardio 模板語法生成 C# 代碼。另外 aardio 提供 string.loadcode() 函數可以直接解析 aardio 模板并返回字符串。
這里要注意,上面范例工程默認導入的 HTTP 服務器是:
wsock.tcp.simpleHttpServer;
這是一個多線程的 HTTP 服務端,每次被請求執行的 aardio 代碼都是在后臺線程中運行。aardio 多線程開發要注意的是每個線程都運行在獨立的環境,全局變量是相互隔離的,這個限制實際上讓 aardio 的多線程開發更簡潔,坑更少,具體請參考 aardio 自帶「范例程序 > aardio 語言 > 多線程」。
如果改為 wsock.tcp.asynHttpServer 則是單線程異步的 HTTP 服務器。
下面我們仍然使用默認的 simpleHttpServer 。多線程的好處是耗時操作不會卡界面。后端在進行耗時操作時,網頁前端通常需要顯示一個動畫,htmx.js 做這事就很簡單。
我們只要簡單的修改一下前面講過的網頁代碼如下:
<button hx-get="/api/index.aardio"
hx-indicator="#indicator" >
點這里發送 GET 請求
</button><br>
<img id="indicator"
class="htmx-indicator"
src="/images/loading.gif"/>
主要是增加了 hx-indicator 屬性,該屬性的值用一個 CSS 選擇器指定了發送 HTTP 請求時要顯示的 HTML 元素,這里指定的是 id 為 "indicator" 的元素。
實際上我們可以自定義這個請求動畫的樣式,我們打開樣式文件 index.css 添加下面的樣式:
.htmx-indicator{
display:none;
}
.htmx-request.htmx-indicator{
display:inline;
}
在發送請求時,網頁上被設定的指示元素會自動添加 CSS 類 "htmx-request",HTTP 請求結束會移除該類。
然后我們打開對應的 aardio 后端代碼 /web/api/index.aardio ,輸入以下代碼:
<span>
<?
if( request.method == "GET"){
/*
這是多線程后端,
這里等 2 秒,網頁會顯示加載動畫
*/
sleep(2000);
response.write( time() )
}
?></span>
上面的代碼的作用是:如果收到 GET 請求,線程就休眠 2 秒以模擬耗時操作。然后輸出當前時間。
我們運行一下看看效果:
htmx.js 提交請求的節點如果是一個表單控件,只要指定 name 屬性 —— 就會自動以該名字發送請求參數,參數值就是控件的值。
如果提交請求的節點是表單,則 HTTP 請求參數為表單內所有控件的值。
也可以在節點的 hx-vals 屬性中用一個 JSON 對象指定請求參數,例如網頁這樣寫:
<button hx-get="/api/index.aardio"
hx-vals='{"myVal": "值"}'>
點這里發送 GET 請求
</button><br>
aardio 后端就可以使用:
request.get["myval"]
取到 HTTP 請求參數 myval 的值。
如果使用 POST 發送請求,例如:
<button hx-post="/api/index.aardio"
hx-vals='{"myVal": "值"}'>
點這里發送 GET 請求
</button><br>
那么 aardio 后端可以使用
request.post["myval"]
取到 HTTP 請求參數 myval 的值。
在 aardio 后端使用:
request.query("myval")
可以取到 GET 或 POST 發送的 myval 參數值。
hx-vals 還可以通過加上 javascript: 或者 js: 前綴后使用 JS 對象返回請求參數,例如:
<button hx-get="/api/index.aardio"
hx-vals='javascript:{myVal: "值"}' >
點這里發送 GET 請求
</button>
有趣的是 web.form 也可以支持 htmx.js 。
web.form 是基于系統自帶的 IE 內核控件,注意系統雖然刪除了 IE 瀏覽器,但 IE 控件屬于系統組件,Windows 有說明該控件不會被移除。IE 控件的好處是從 XP 到 Win11 所有操作系統都自帶。
而且 IE 控件很輕量,與本地程序交互的接口也非常方便。Win10 ,Win11 自帶的是 IE11 內核,寫寫一般的網頁還是很好用的。至于 Win7 —— 因為只有極低的份額,一般軟件不用考慮。
htmx.js 最后一個支持 IE 11 的版本是 1.6.1 ,這個足夠用了。前面說過我們需要的只是局部請求刷新的功能,其他功能我們用不上,所以追新無意義。
首先我們打開 aardio 工程向導,選擇「 Web 界面 > Web Form 」然后創建工程即可,新版工程模板默認就是使用 htmx.js 。
其他 HTML 代碼寫法與前面介紹的 WebView2 基本一樣。不過 web.form 本就支持 EXE 內嵌資源文件,所以默認并不會啟動 HTTP 服務器,需要多寫幾句代碼。
打開工程的 webPage.aardio 源碼:
可以看到源碼中是如下啟動 HTTP 服務器的:
import web.form;
var wb = web.form(winform);
//多線程后端
import wsock.tcp.simpleHttpServer;
wsock.tcp.simpleHttpServer.documentBase = "\web"
var indexUrl = wsock.tcp.simpleHttpServer.startUrl("\index.html")
wb.go(indexUrl);
我并沒有把這幾句代碼封裝到 wb.go() 函數中。
有些新手總以為代碼越少越好,其實并非如此,有時候多寫幾句更容易看清楚代碼的思路,更容易理解我們正在使用的技術。
下面我們看下 web.form + htmx.js 范例的運行效果:
上面示例程序的主窗口是使用 win.ui.tabs 做的,只有其中一個標簽頁用到了網頁。
其實一般桌面軟件的界面并不是一定要全部使用網頁實現。有時候我們將界面中適合用網頁呈現的部分用網頁做,可能會更好。
我們在使用任何技術時,都要考慮一下適不適合。沒有一樣技術能適合做所有的事,多個選擇總是好事。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。