JavaFX使用html+javascript+css替代fxml布局文件(1)
2019-04-14JavaFX攻城狮5130°c
A+ A-文章加以记录一些需要注意的地方,以备日后自己查阅。
开发工具及其版本:
Eclipse Java EE IDE for Web Developers. Version: Neon.2 Release (4.6.2) Build id: 20161208-0600
java版本:jdk1.8 64bit
创建一个普通java项目:
在src下面新建包: com.atnoce.blogexample
然后在com.atnoce.blogexample包下面新建启动类Start,代码如下:
package com.atnoce.blogexample; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.stage.Stage; public class Start extends Application{ public static void main(String[] args) { // TODO Auto-generated method stub } @Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("blogExample"); Browser browser = new Browser(this); Scene scene=new Scene(browser,500,400,Color.web("#666970")); primaryStage.setScene(scene); primaryStage.show(); } }
代码当中用到了一个Browser类,下面是Browser类代码:
package com.atnoce.blogexample; import javafx.geometry.HPos; import javafx.geometry.VPos; import javafx.scene.layout.Region; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; public class Browser extends Region{ WebView webView=new WebView(); WebEngine webEnging=webView.getEngine(); public Browser(Start start) { webEnging.load(Start.class.getResource("web/login.html").toExternalForm()); getChildren().add(webView); } @Override protected void layoutChildren() { double w = getWidth(); double h = getHeight(); layoutInArea(webView,0,0,w,h,0, HPos.CENTER, VPos.CENTER); } @Override protected double computePrefWidth(double width) { return 800; } @Override protected double computePrefHeight(double height) { return 600; } }
在Browser构造方法中,为浏览器提供了启动后要加载的页面,位于com.atnoce.blogexample.web包下面,下面是login.html页面的代码:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>login</title> </head> <body> <div align="center"> <table border="1"> <tr> <td>用户名:</td> <td> <input type="text" id="username"/> </td> </tr> <tr> <td>密码:</td> <td> <input type="password" id="password"/> </td> </tr> <tr> <td align="center" colspan="2"> <input type="button" value="login"/> </td> </tr> </table> </div> </body> </html>
加载的html页面既可以是远程服务器的也可以是本地的,各有各的好处, 根据自己的需求,选择不同的方式即可。
加载本地html,速度快,不需要等待网络传输,不依赖服务器带宽和性能,但是如果界面要调整,只能发布升级包; 加载远程html,速度收网络的影响,快慢不一定,依赖于本机和服务器的带宽,但是如果要调整界面,只要在服务器更改了页面,所有客户端看到的界面都会改变;
至此,我们的应用程序可以直接右键Start类,运行看效果如下:
可以看到,程序中已经使用了login.html页面作为应用程序的登录页面,也就是说既然是网页,那么你可以任意更改login.html的代码来让这个页面变得漂亮。
下一步,为登录界面赋予可用的功能,点击login按钮可以完成登录。
要想做这个登录有两种不同的方式,根据应用程序的不同,使用的方法也不同,如果应用程序不需要依靠服务器来完成登录认证,那么可以在点击login按钮后调用java代码完成登录逻辑;另一种就是需要服务器来完成登录认证,那么你可以跟平时做网页一样,直接用一个ajax请求服务器后台即可,也可以调用java代码,由java代码发起请求并转发请求结果,都是可以的,相当的灵活;
ajax不在赘述,下面介绍调用本地java代码的方式。
要想在login.html中调用java代码需要在应用程序加载完页面后,注入java对象,使用注入的java对象即可完成java代码的调用,注入的java对象的时机得是在网页加载完毕后,否则其他时间注入,很有可能注入失败。
所以更改Browser类代码,添加一个监听,当页面加载成功后,注入对象,更改后的Browser代码如下:
package com.atnoce.blogexample; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.concurrent.Worker; import javafx.concurrent.Worker.State; import javafx.geometry.HPos; import javafx.geometry.VPos; import javafx.scene.layout.Region; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import netscape.javascript.JSObject; public class Browser extends Region{ WebView webView=new WebView(); WebEngine webEnging=webView.getEngine(); public Browser(Start start) { App app = new App(); webEnging.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) { if(newValue==Worker.State.SUCCEEDED){ JSObject win=(JSObject)webEnging.executeScript("window"); win.setMember("app", app); } } }); webEnging.load(Start.class.getResource("web/login.html").toExternalForm()); getChildren().add(webView); } @Override protected void layoutChildren() { double w = getWidth(); double h = getHeight(); layoutInArea(webView,0,0,w,h,0, HPos.CENTER, VPos.CENTER); } @Override protected double computePrefWidth(double width) { return 800; } @Override protected double computePrefHeight(double height) { return 600; } }
我们创建了一个App对象,并且在网页加载完毕的时候注入了此对象,App类就是一个普通的java类,在这个java类中,我们完成登录认证操作,代码如下:
package com.atnoce.blogexample; public class App { public String login(String username,String password){ String ret = ""; if(username.equals("admin") && password.equals("admin")){ ret = "success"; }else{ ret = "用户名或密码错误!"; } return ret; } }
很简单的一个登录方法,下面更改login.html页面,为登录按钮添加登录处理事件:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>login</title> <script type="text/javascript"> function login(){ var username = document.getElementById("username").value; var password = document.getElementById("password").value; if(username && password){ var ret = app.login(username,password); if(ret == "success"){ window.location='index.html'; }else{ document.getElementById("errorMsg").innerText=ret; } }else{ document.getElementById("errorMsg").innerText="用户名、密码不能为空!"; } } </script> </head> <body> <div align="center"> <table border="1"> <tr> <td>用户名:</td> <td> <input type="text" id="username"/> </td> </tr> <tr> <td>密码:</td> <td> <input type="password" id="password"/> </td> </tr> <tr> <td align="center" colspan="2"> <input type="button" onclick="login();" value="login"/><span id="errorMsg"></span> </td> </tr> </table> </div> </body> </html>
我们为登录按钮添加了登录事件,并且简单做了数据校验,登录成功后跳转页面到index.html,在登录按钮的处理逻辑中,我们就使用了app.login方法,调用了后台App类中的login方法来完成登录逻辑,这里有几个地方要注意:
1、app被注入后实际上就变为window对象的一个属性;
2、java代码要提供给网页里面调用的方法必须是public 的,有没有返回值根据自己的需要可有可无;
3、java代码提供给网页调用的方法的返回值可以任意java类型,但是在网页中的使用方法就会变的跟java一样,相当于在JavaScript中要写java代码,会变得混淆,所以建议返回一律用String类型,到后期我们会完善这个返回值,使网页和java代码之间的交互约定为JSON格式,这样比较明确也方便使用。
4、被注入的App对象一定要在构造方法里面创建,而不是在注入的时候临时创建,本例子中Browser里面注入方式为正确方式,错误的方式如下:
package com.atnoce.blogexample; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.concurrent.Worker; import javafx.concurrent.Worker.State; import javafx.geometry.HPos; import javafx.geometry.VPos; import javafx.scene.layout.Region; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import netscape.javascript.JSObject; public class Browser extends Region{ WebView webView=new WebView(); WebEngine webEnging=webView.getEngine(); public Browser(Start start) { webEnging.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) { if(newValue==Worker.State.SUCCEEDED){ JSObject win=(JSObject)webEnging.executeScript("window"); win.setMember("app", new App());//这里是重点,不能用这种方式注入对象 } } }); webEnging.load(Start.class.getResource("web/login.html").toExternalForm()); getChildren().add(webView); } @Override protected void layoutChildren() { double w = getWidth(); double h = getHeight(); layoutInArea(webView,0,0,w,h,0, HPos.CENTER, VPos.CENTER); } @Override protected double computePrefWidth(double width) { return 800; } @Override protected double computePrefHeight(double height) { return 600; } }
以上代码注入对象的 方式是错误的,虽然代码不会报错,可是注入的对象在网页中无法使用!
到这里这个简单的应用程序就已经可以跑起来,并且完成简单的登录,下一篇文章我们继续介绍这种方式开发应用程序。