
回复
上篇总结了Web组件的基础属性和事件,这篇通过一个简单案例,学习原生与web的交互。涉及到JS注入,原生调用JS方法,JS调用原生方法。
实现效果:
实现目标:
1.打开目标页面,当有图片或gif预览时,按返回键拦截,关闭预览可以返回到上一页
2.从目标页面跳转到其他页后,按返回键返回到上一页,直到返回目标页时才能退出
实现思路:
1.需要给目标web页面注入一个JS方法,监听是否有图片或者gif预览,调试发现,当有预览时,html的body的clss会变成van-overflow-hidden,所以,只需要监听body的class值的变化,就知道当前页面是否有预览。不同的html可能不同,这里只是测试。
2.设置本地接收JS调用的方法,当JS监听到变化时,调用原生Native方法
3.拦截onBackPress返回键方法,判断当前页面是否有预览,如果有则拦截,判断当前页面是否有后退页面,如果有则后退一级,否则退出
实现过程:
编写JS注入代码:
// 检查当前是否有打开的图片预览
function checkImageViewerAndCallNative() {
let observer = new MutationObserver(mutations => {
if (mutations?.length) {
const mutation = mutations[0]
const currentValue = mutation.target.getAttribute(mutation.attributeName)
let hasOpenViewer = currentValue === 'van-overflow-hidden'
nativeJSCallBack.print(hasOpenViewer);
// 如果存在打开的预览,调用原生方法
if (hasOpenViewer) {
nativeJSCallBack.sourcePreview(true);
}else{
nativeJSCallBack.sourcePreview(false);
}
}
})
observer.observe(document.body, {
attributes: true,
attributeFilter: ['class'], // 明确指定只监听此属性
attributeOldValue: true, // 记录旧值用于比对
subtree: false
})
}
原生注册的方法:
1.先定义一个Class,定义供JS调用的方法和参数
2.将定义的JS方法注入到web中
3.调用这个方法
4.页面销毁时取消注册
//定义原生监听js调用的方法
class NativeJSCallBack{
isPreview:boolean
constructor() {
this.isPreview = false
}
sourcePreview(preview:boolean):void{
console.log('NativeJSCallBack: 收到web调用'+preview.valueOf())
this.isPreview = preview.valueOf()
}
print(str:string):void{
console.log('NativeJSCallBack:'+str)
}
}
//
// 注册同步函数
//参数:参与注册的应用侧JavaScript对象,自定义注册对象的名称,参与注册的应用侧JavaScript对象的同步方法
this.webviewController.registerJavaScriptProxy(this.nativeJSCallBack, "nativeJSCallBack", ["sourcePreview","print"]);
this.webviewController.refresh();
//注册js 并调用
this.webviewController.runJavaScript(previewJS)
this.webviewController.runJavaScript('checkImageViewerAndCallNative()')
全部源码:
import ApiConstants from '../net/ApiConstants'
import { webview } from '@kit.ArkWeb'
let previewJS:string = ' // 检查当前是否有打开的图片预览\n' +
' function checkImageViewerAndCallNative() {\n' +
' let observer = new MutationObserver(mutations => {\n' +
' if (mutations?.length) {\n' +
' const mutation = mutations[0]\n' +
' const currentValue = mutation.target.getAttribute(mutation.attributeName)\n' +
' let hasOpenViewer = currentValue === \'van-overflow-hidden\'\n' +
' nativeJSCallBack.print(hasOpenViewer);\n' +
' // 如果存在打开的预览,调用原生方法\n' +
' if (hasOpenViewer) {\n' +
' nativeJSCallBack.sourcePreview(true);\n' +
' }else{\n' +
' nativeJSCallBack.sourcePreview(false);\n' +
' }\n' +
' }\n' +
' })\n' +
' observer.observe(document.body, {\n' +
' attributes: true,\n' +
' attributeFilter: [\'class\'], // 明确指定只监听此属性\n' +
' attributeOldValue: true, // 记录旧值用于比对\n' +
' subtree: false\n' +
' })\n' +
' }'
class NativeJSCallBack{
isPreview:boolean
constructor() {
this.isPreview = false
}
sourcePreview(preview:boolean):void{
console.log('NativeJSCallBack: 收到web调用'+preview.valueOf())
this.isPreview = preview.valueOf()
}
print(str:string):void{
console.log('NativeJSCallBack:'+str)
}
}
@Entry
@ComponentV2
struct WebViewTest {
private webviewController: WebviewController = new webview.WebviewController()
@Local nativeJSCallBack:NativeJSCallBack = new NativeJSCallBack()
build() {
Column() {
Web({
src: ApiConstants.WEB_URL,
controller: this.webviewController,
renderMode: RenderMode.ASYNC_RENDER // 设置渲染模式
})//Web组件网页正常加载过程所涉及的状态
//该回调调用时网页还未加载,因此无法在回调中使用有关操作网页的接口
.onControllerAttached(() => {
// 推荐在此loadUrl、设置自定义用户代理、注入JS对象等
console.log('onControllerAttached execute')
// 注册同步函数
//参数:参与注册的应用侧JavaScript对象,自定义注册对象的名称,参与注册的应用侧JavaScript对象的同步方法
this.webviewController.registerJavaScriptProxy(this.nativeJSCallBack, "nativeJSCallBack", ["sourcePreview","print"]);
this.webviewController.refresh();
})
.onPageEnd((event) => {
// 推荐在此事件中执行JavaScript脚本
if (event) {
console.log('onPageEnd url:' + event.url);
}
this.webviewController.runJavaScript(previewJS)
this.webviewController.runJavaScript('checkImageViewerAndCallNative()')
})
//组件卸载消失时触发此回调
.onDisAppear(() => {
this.webviewController.deleteJavaScriptRegister("nativeJSCallBack");
})
}
}
onBackPress(): boolean | void {
if (this.nativeJSCallBack.isPreview) {
return true
}
if (this.webviewController.accessBackward()) {
this.webviewController.backward()
return true
}
return false
}
}