场景描述
在特殊的H5场景下需要应用拉起自定义键盘进行输入。
场景一:使用jsBridge拉起自定义弹窗写自定义键盘,再通过jsBridge传参实现输入。
场景二:使用web的同层渲染将原生textInput组件渲染到页面上。
方案描述
通过注册一个js代理对象被web的registerJavaScriptProxy方法调用拉起CustomDialog,在CustomDialog上放置一个customkeyboard
场景一: 通过jsBridge拉起自定义弹窗,在自定义弹窗上放置自定义键盘,例如需要输入密码时的安全键盘。
效果图
方案
通过注册一个js代理对象被web的registJavaScriptProxy方法调用拉起CustomDialog,在CustomDialog上放置一个自定义键盘组件,通过在H5上input标签的readonly属性和注册的js方法changeNumbers实现在原生端输入数字传到H5上,他们之间通过@Link装饰器绑定的变量进行传值,所以点击删除输入的内容也是可以在H5上实现的。
核心代码
- 通过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>
- 当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
})
- 将自定义键盘放置在自定义弹窗上。
@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)
}
}
- 在自定义键盘上输入内容的时候会调用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>
场景二: 通过同层渲染渲染一个原生的自定义键盘
效果图
方案
整体实现效果为:通过web的同层渲染功能实现将原生TextInput组件渲染到H5需要使用自定义键盘的页面中,这样就可以实现在H5拉起自定义键盘,并且使用它的全部功能。
核心代码
- 创建一个自定义键盘并绑定到原生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)
}
}
- 将原生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>
鸿蒙全栈开发全新学习指南
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以要有一份实用的鸿蒙(HarmonyOS NEXT)学习路线与学习文档用来跟着学习是非常有必要的。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。
本路线共分为四个阶段:
第一阶段:鸿蒙初中级开发必备技能
第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH
第三阶段:应用开发中高级就业技术
第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/MNxiaona/733GH
《鸿蒙 (Harmony OS)开发学习手册》(共计892页)
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:gitee.com/MNxiaona/733GH
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH
鸿蒙入门教学视频:
美团APP实战开发教学:gitee.com/MNxiaona/733GH
写在最后
- 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:
gitee.com/MNxiaona/733GH