你好,欢迎访问我的博客!登录
当前位置:首页 - JavaFX - 正文 求知成瘾,却无作品!

JavaFX使用html+javascript+css替代fxml布局文件(1)

2019-04-14JavaFX攻城狮35831°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项目:

1.png


在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类,运行看效果如下:

2.png

可以看到,程序中已经使用了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;
    }

}

以上代码注入对象的 方式是错误的,虽然代码不会报错,可是注入的对象在网页中无法使用!


到这里这个简单的应用程序就已经可以跑起来,并且完成简单的登录,下一篇文章我们继续介绍这种方式开发应用程序。

未定义标签

发表评论

必填

选填

选填

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。


  登录