鸿蒙开发5.0【基于webview拉起自定义键盘】

场景描述

在特殊的H5场景下需要应用拉起自定义键盘进行输入。

场景一:使用jsBridge拉起自定义弹窗写自定义键盘,再通过jsBridge传参实现输入。

场景二:使用web的同层渲染将原生textInput组件渲染到页面上。

方案描述

通过注册一个js代理对象被web的registerJavaScriptProxy方法调用拉起CustomDialog,在CustomDialog上放置一个customkeyboard

场景一: 通过jsBridge拉起自定义弹窗,在自定义弹窗上放置自定义键盘,例如需要输入密码时的安全键盘

效果图

1

方案

通过注册一个js代理对象被web的registJavaScriptProxy方法调用拉起CustomDialog,在CustomDialog上放置一个自定义键盘组件,通过在H5上input标签的readonly属性和注册的js方法changeNumbers实现在原生端输入数字传到H5上,他们之间通过@Link装饰器绑定的变量进行传值,所以点击删除输入的内容也是可以在H5上实现的。

核心代码

  1. 通过javaScriptProxy方法拉起自定义弹窗,在H5上的input标签绑定一个onclick事件,当点击输入框后会调用从原生注册过来的js代理方法openWindow。

    <input type="text" name="number_info" readonly onclick="openWindow()" value="" style="width: 500px;height: 100px;font-size:50px;border:1px solid # f00;">
    
    <script>
    
    function openWindow() {
    
    let value = document.getElementsByName("number_info")[0].value;
    
    window.myJsb.openDialog(value)
    
    }
    
    </script>
    
  2. 当H5上openWindow方法被调用后会通过jsBridge调用以下两个js代理方法打开自定义弹窗。

    jsbObject: JsbObject = {
    
    openDialog: (value: string) => {
    
    this.showDialog(this, value);
    
    }
    
    }
    
    showDialog(context: object, value: string) {
    
    // 把自定义弹窗调出来
    
    this.currentData = value;
    
    this.dialogController.open()
    
    }
    
    
    
    Web({ src: "resource://rawfile/web_test.html", controller: this.webviewController })
    
    .javaScriptAccess(true)
    
    .javaScriptProxy({
    
    name: "myJsb",
    
    object: this.jsbObject,
    
    methodList: ["openDialog"],
    
    controller: this.webviewController
    
    })
    
  3. 将自定义键盘放置在自定义弹窗上。

    @CustomDialog
    
    struct CustomDialogExample {
    
    @Link currentData: string
    
    dialogControllerTwo: CustomDialogController | null = new CustomDialogController({
    
    builder: CustomDialogExample({ currentData: $currentData }),
    
    alignment: DialogAlignment.Bottom,
    
    offset: { dx: 0, dy: -25 }
    
    })
    
    controller?: CustomDialogController
    
    
    
    build() {
    
    Column() {
    
    Button('x').onClick(() => {
    
    // 关闭自定义键盘
    
    if (this.controller != undefined) {
    
    this.controller.close()
    
    }
    
    })
    
    Grid() {
    
    ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, '*', 0, '删除'], (item: number | string) => {
    
    GridItem() {
    
    Button(item + "")
    
    .width(110).onClick(() => {
    
    if (item == '删除') {
    
    if (this.currentData.length > 0) {
    
    this.currentData = this.currentData.substring(0, this.currentData.length - 1);
    
    }
    
    } else {
    
    this.currentData += item
    
    }
    
    })
    
    }
    
    })
    
    }.maxCount(3).columnsGap(10).rowsGap(10).padding(5)
    
    }.backgroundColor(Color.Gray)
    
    }
    
    }
    
  4. 在自定义键盘上输入内容的时候会调用onChangeInputValue方法,通过里面的runJavaScript调用H5上的js方法changeNumber传值到H5的输入框中。

    onChangeInputValue(stateName: string){
    
    console.log('this.currentData:' + this.currentData)
    
    this.webviewController.runJavaScript('changeNumber("'+ this.currentData +'")')
    
    .then((result) => {
    
    console.log('result: ' + result);
    
    })
    
    }
    
    
    <<input type="text" name="number_info" readonly onclick="openWindow()" value="" style="width: 500px;height: 100px;font-size:50px;border:1px solid # f00;" />
    
    <script>
    
    function changeNumber(value){
    
    document.getElementsByName("number_info")[0].value = value;
    
    }
    
    </script>
    

场景二: 通过同层渲染渲染一个原生的自定义键盘

效果图

2

方案

整体实现效果为:通过web的同层渲染功能实现将原生TextInput组件渲染到H5需要使用自定义键盘的页面中,这样就可以实现在H5拉起自定义键盘,并且使用它的全部功能。

核心代码

  1. 创建一个自定义键盘并绑定到原生textInput组件上。

    @Component
    
    struct ButtonComponent {
    
    controller1: TextInputController = new TextInputController()
    
    @State inputValue: string = ""
    
    
    
    // 自定义键盘组件
    
    @Builder
    
    CustomKeyboardBuilder() {
    
    Column() {
    
    Button('x').onClick(() => {
    
    // 关闭自定义键盘
    
    this.controller1.stopEditing()
    
    })
    
    Grid() {
    
    ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, '*', 0, '# '], (item: number | string) => {
    
    GridItem() {
    
    Button(item + "")
    
    .width(110).onClick(() => {
    
    this.inputValue += item
    
    })
    
    }
    
    })
    
    }.maxCount(3).columnsGap(10).rowsGap(10).padding(5)
    
    }.backgroundColor(Color.Pink)
    
    }
    
    
    
    @ObjectLink params: Params
    
    @State bkColor: Color = Color.Red
    
    @State outSetValueTwo: number = 40
    
    @State outSetValueOne: number = 40
    
    @State tipsValue: number = 40
    
    controller: web_webview.WebviewController = new web_webview.WebviewController();
    
    
    
    build() {
    
    Column() {
    
    TextInput({ controller: this.controller1, text: this.inputValue })// 绑定自定义键盘
    
    .customKeyboard(this.CustomKeyboardBuilder()).margin(10).border({ width: 1 })
    
    }
    
    .width(this.params.width)
    
    .height(this.params.height)
    
    }
    
    }
    
  2. 将原生textInput组件通过web同层渲染功能渲染到H5上的embed标签上。

    @Entry
    
    @Component
    
    struct WebIndex {
    
    browserTabController: WebviewController = new webview.WebviewController()
    
    
    
    build() {
    
    Column() {
    
    Web({ src: $rawfile("test.html"), controller: this.browserTabController })// 配置同层渲染开关开启。
    
    .enableNativeEmbedMode(true)// 获取embed标签的生命周期变化数据。
    
    .onNativeEmbedLifecycleChange((embed) => {
    
    console.log("NativeEmbed surfaceId" + embed.surfaceId);
    
    // 获取web侧embed元素的id。
    
    const componentId = embed.info?.id?.toString() as string
    
    if (embed.status == NativeEmbedStatus.CREATE) {
    
    console.log("NativeEmbed create" + JSON.stringify(embed.info))
    
    // 创建节点控制器,设置参数并rebuild。
    
    let nodeController = new MyNodeController()
    
    nodeController.setRenderOption({
    
    surfaceId: embed.surfaceId as string,
    
    type: embed.info?.type as string,
    
    renderType: NodeRenderType.RENDER_TYPE_TEXTURE,
    
    embedId: embed.embedId as string,
    
    width: px2vp(embed.info?.width),
    
    height: px2vp(embed.info?.height)
    
    })
    
    nodeController.setDestroy(false);
    
    // 根据web传入的embed的id属性作为key,将nodeController存入map。
    
    this.nodeControllerMap.set(componentId, nodeController)
    
    // 将web传入的embed的id属性存入@State状态数组变量中,用于动态创建nodeContainer节点容器,需要将push动作放在set之后。
    
    this.componentIdArr.push(componentId)
    
    } else if (embed.status == NativeEmbedStatus.UPDATE) {
    
    let nodeController = this.nodeControllerMap.get(componentId)
    
    nodeController?.updateNode({
    
    textOne: 'update',
    
    width: px2vp(embed.info?.width),
    
    height: px2vp(embed.info?.height)
    
    } as ESObject)
    
    } else {
    
    let nodeController = this.nodeControllerMap.get(componentId);
    
    nodeController?.setDestroy(true)
    
    this.nodeControllerMap.clear();
    
    this.componentIdArr.length = 0;
    
    }
    
    })// 获取同层渲染组件触摸事件信息。
    
    .onNativeEmbedGestureEvent((touch) => {
    
    console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
    
    this.componentIdArr.forEach((componentId: string) => {
    
    let nodeController = this.nodeControllerMap.get(componentId)
    
    if (nodeController?.getEmbedId() === touch.embedId) {
    
    let ret = nodeController?.postEvent(touch.touchEvent)
    
    if (ret) {
    
    console.log("onNativeEmbedGestureEvent success " + componentId)
    
    } else {
    
    console.log("onNativeEmbedGestureEvent fail " + componentId)
    
    }
    
    }
    
    })
    
    })
    
    }
    
    }
    
    }
    
    
    <html>
    
    <head>
    
    <title>同层渲染测试html</title>
    
    <meta name="viewport">
    
    </head>
    
    <body>
    
    <div>
    
    <div id="bodyId">
    
    <embed id="nativeTextInput" type="native/TextInput" width="100%" height="100%" src="test?params1=xxx?"
    
    style="background-color:pink"/>
    
    </div>
    
    </div>
    
    </body>
    
    </html>
    

以上就是本篇文章所带来的鸿蒙开发中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在结尾找我可全部拿到!
下面是鸿蒙的完整学习路线,展示如下:
1

除此之外,根据这个学习鸿蒙全栈学习路线,也附带一整套完整的学习【文档+视频】,内容包含如下

内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开发、鸿蒙项目实战)等技术知识点。帮助大家在学习鸿蒙路上快速成长!

鸿蒙【北向应用开发+南向系统层开发】文档

鸿蒙【基础+实战项目】视频

鸿蒙面经

2

为了避免大家在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢大家观看!
3

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
鸿蒙系统(HarmonyOS)的Webview中遇到JS白屏的问题,通常是由于网络连接、资源加载、脚本执行等方面的原因导致的。下面是一些可能导致Webview JS白屏的具体情况及解决办法: ### 可能原因 1. **网络问题**:网页依赖的数据、图片等资源无法正常加载,可能导致页面内容为空或者只显示一部分。 2. **脚本错误**:JavaScript代码存在语法错误或者逻辑错误,使得某些关键功能未能正常运行。 3. **资源超时**:HTTP请求设置的时间限制导致某些资源加载失败。 4. **跨域问题**:当页面试图从另一个域名下加载资源时,如果没有适当的跨域策略,浏览器可能会阻止资源的加载。 ### 解决方案 1. **检查网络状态**:确保设备连接到稳定的网络环境,并检查是否有防火墙或其他安全软件拦截了必要的网络请求。 2. **优化JavaScript代码**: - 检查并修复代码中的语法错误。 - 使用try...catch处理可能出现的异常,避免程序崩溃导致页面空白。 - 确保所有的异步操作都有合适的回调函数或者错误处理机制。 3. **调整HTTP请求配置**:如果发现因超时导致加载失败,可以尝试增加请求超时时间,或者优化服务器响应速度。 4. **解决跨域问题**:通过设置合适的`Content-Security-Policy`头部信息、使用CORS策略等方式允许跨源访问。 5. **调试工具**:利用开发者工具查看具体的错误信息,这通常可以帮助快速定位问题所在。 ### 相关问题: 1. 在鸿蒙系统Webview中如何设置HTTP请求的超时时间? 2. 如何在JavaScript中检测并处理跨域请求的问题? 3. 开发者如何有效地使用鸿蒙系统提供的调试工具来诊断Webview相关的性能问题? 请注意,在实际应用中解决问题时,应综合考虑各种因素,包括但不限于硬件兼容性、操作系统版本、用户交互反馈等,以提供更稳定、更流畅的应用体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值