Java中使用webview内嵌vue页面
技术服务于业务,在什么情况下我们需要使用webview去内嵌vue页面呢。众所周知webview作为一个组件,可以看成是一个微型浏览器内核。那么如果在我们的程序中集成webview,那么我们的程序是不是也可以看成是一个定制版本的微型浏览器呢。如果只是纯粹的web应用也就是不需要参与过多的与本地化资源交互,我们只需要在浏览器中使用前端就可以了。虽说B/S模式已经大行其道但是毕竟还是得益于浏览器本身丰富的能力,当遇到一些特殊的需求,比如说在浏览器中调用本机程序等等,此时浏览器本身的能力已经无法满足需求的时候,我们就需要去拓展浏览器的能力。虽然现在h5已经足够强大,但是比如说使用h5在不使用第三方支持的情况下实现在线编辑word、excel等功能还是不那么容易的。当然等h6问世的时候默认集成人脸识别也说不定。
一方面可以使用浏览器开发者预留的拓展接口,比如说已经渐渐没落的IE浏览器中的ActiveX,谷歌浏览器45版本以下的npapi都可以看成是浏览器本地能力拓展的一种体现。另一方面我们可以自己整合现有资源去实现一个新“浏览器”。当然,视困难程度我们一般会使用自己最擅长的开发语言去实现。在Java中集成webview有什么好处呢,一方面拥有了使用前端脚本、页面的能力、一方面又拥有了与本地交互的能力。缺点就是一旦这样做实际上就变成了一个C/S结构的程序,就面临着更新升级部署困难等C/S共有的缺点,也只适合在一些特定的应用场景下使用。个人更偏向于使用cdn形式使用vue。话不多说,直接上代码。
LocalMain.java
package org.lbz;
import javafx.application.Application;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebEvent;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import org.lbz.util.JavaCaller;
public class LocalMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
try {
final WebView browser = new WebView();
browser.setMinWidth(1200);
final WebEngine webEngine = browser.getEngine();
ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(browser);
webEngine.getLoadWorker().stateProperty()
.addListener((obs, oldValue, newValue) -> {
if (newValue == Worker.State.SUCCEEDED) {
netscape.javascript.JSObject o = (netscape.javascript.JSObject) webEngine.executeScript("window");
o.setMember("sout",System.out);
o.setMember("javaCaller", JavaCaller.instance);
System.out.println("SUCCEEDED Inject Java Object to browser");
}else{
if(newValue == Worker.State.READY){
netscape.javascript.JSObject o = (netscape.javascript.JSObject) webEngine.executeScript("window");
}
}
});
webEngine.setJavaScriptEnabled(true);
browser.getEngine().setOnAlert((WebEvent<String> wEvent) -> {
System.out.println("Alert Event - Message: " + wEvent.getData());
});
com.sun.javafx.webkit.WebConsoleListener.setDefaultListener(new com.sun.javafx.webkit.WebConsoleListener() {
@Override
public void messageAdded(WebView webView, String message, int lineNumber, String sourceId) {
System.out.println("Console: " + message + " [" + sourceId + ":" + lineNumber + "] ");
}
});
StringBuilder sb = new StringBuilder();
browser.setContextMenuEnabled(true);
webEngine.load("file:///D:\\BaiduNetdiskDownload\\JavaFormFx\\src\\main\\resources\\index2.html");
Scene scene = new Scene(new javafx.scene.Group(),1210,800);
scene.setRoot(scrollPane);
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setTitle("java内嵌vue");
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args){
launch(args);
}
}
JavaCaller.java
package org.lbz.util; import javax.swing.*; import javax.swing.filechooser.FileSystemView; import java.io.File; public class JavaCaller{ public static JavaCaller instance = new JavaCaller(); public void readFile(netscape.javascript.JSObject obj,netscape.javascript.JSObject callback){ String path = this.chooseFile(); callback.call("call",this,path); } public void readDirectory(netscape.javascript.JSObject obj,netscape.javascript.JSObject callback){ String path = this.chooseDirectory(); callback.call("call",this,path); } public String chooseFile(){ int result = 0; File file = null; String path = null; JFileChooser fileChooser = new JFileChooser(); FileSystemView fsv = FileSystemView.getFileSystemView(); fileChooser.setCurrentDirectory(new File("d:\\")); fileChooser.setDialogTitle("请选择要上传的文件..."); fileChooser.setApproveButtonText("确定"); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); result = fileChooser.showOpenDialog(null); if(JFileChooser.APPROVE_OPTION == result) { path=fileChooser.getSelectedFile().getPath(); } return path; } public String chooseDirectory(){ int result = 0; File file = null; String path = null; JFileChooser fileChooser = new JFileChooser(); FileSystemView fsv = FileSystemView.getFileSystemView(); fileChooser.setCurrentDirectory(new File("d:\\")); fileChooser.setDialogTitle("请选择要上传的文件夹..."); fileChooser.setApproveButtonText("确定"); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); result = fileChooser.showOpenDialog(null); if(JFileChooser.APPROVE_OPTION == result) { path=fileChooser.getSelectedFile().getPath(); } return path; } }
index2.html
<html> <head> <link rel="stylesheet" type="text/css" href="elementUI/element.css" /> <meta charset="UTF-8" /> <script type="text/javascript" src="vue.js"></script> <script type="text/javascript" src="elementUI/element.js"></script> <script type="text/javascript" src="jquery-3.4.1.min.js"></script> <script type="text/javascript"> function init(){ //能够导出网站进行测试,ability var DGList = Vue.extend({ template: '<el-form ref="form" :model="form" label-width="80px">\n' + ' <el-form-item label="活动名称">\n' + ' <el-input v-model="form.name"></el-input>\n' + ' </el-form-item>\n' + ' <el-form-item label="活动区域">\n' + ' <el-select v-model="form.region" placeholder="请选择活动区域">\n' + ' <el-option label="区域一" value="shanghai"></el-option>\n' + ' <el-option label="区域二" value="beijing"></el-option>\n' + ' </el-select>\n' + ' </el-form-item>\n' + ' <el-form-item label="活动时间">\n' + ' <el-col :span="11">\n' + ' <el-date-picker type="date" placeholder="选择日期" v-model="form.date1" style="width: 100%;"></el-date-picker>\n' + ' </el-col>\n' + ' <el-col class="line" :span="2">-</el-col>\n' + ' <el-col :span="11">\n' + ' <el-time-picker placeholder="选择时间" v-model="form.date2" style="width: 100%;"></el-time-picker>\n' + ' </el-col>\n' + ' </el-form-item>\n' + ' <el-form-item label="即时配送">\n' + ' <el-switch v-model="form.delivery"></el-switch>\n' + ' </el-form-item>\n' + ' <el-form-item label="活动性质">\n' + ' <el-checkbox-group v-model="form.type">\n' + ' <el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox>\n' + ' <el-checkbox label="地推活动" name="type"></el-checkbox>\n' + ' <el-checkbox label="线下主题活动" name="type"></el-checkbox>\n' + ' <el-checkbox label="单纯品牌曝光" name="type"></el-checkbox>\n' + ' </el-checkbox-group>\n' + ' </el-form-item>\n' + ' <el-form-item label="特殊资源">\n' + ' <el-radio-group v-model="form.resource">\n' + ' <el-radio label="线上品牌商赞助"></el-radio>\n' + ' <el-radio label="线下场地免费"></el-radio>\n' + ' </el-radio-group>\n' + ' </el-form-item>\n' + ' <el-form-item label="活动形式">\n' + ' <el-input type="textarea" v-model="form.desc"></el-input>\n' + ' </el-form-item>\n' + ' <el-form-item>\n' + ' <el-button type="primary" @click="onSubmit">立即创建</el-button>\n' + ' <el-button type="primary" @click="openFile">打开文件</el-button>\n' + ' <el-button>取消</el-button>\n' + ' </el-form-item>\n' + '</el-form>', data: function () { var target = this; return { form: { name: '', region: '', date1: '', date2: '', delivery: false, type: [], resource: '', desc: '' } } }, created: function () { }, mounted: function () { }, methods:{ openFile(){ javaCaller.readFile({},function (a) { console.log('readfile:'+a); }); }, handleClose(done) { }, onSubmit(){ } }, props: ['name'] }); var dl = new DGList({propsData: {name: 'dear_mr'}}).$mount(); //这地方只能用append $('#app').append(dl.$el); } </script> </head> <body οnlοad="init();"> <div id="app"> </div> </body> </html>
运行效果
九日辰: 您有试过es8的版本吗
esas: 赞!如果可以说明多层目录的文件路径是使用\分开就好了!
yongchuanquan: 请问Excel可以用spring boot 的 Druid进行配置吗?
weixin_40194570: 同样,似乎不能这么搞
大数据时代: 您好,按照这个方法编译成功,也生成了dll文件,但是放到mysql的插件目录下,执行Create时报错了Can't open shared library 'MySQLUdfHttpWin.dll',这个是怎么回事啊?