OpenHarmony开发:常用业务组件封装
App 与 H5 通信-JsBridge 封装
ets
/*
* Index.ets
* 网页与App交互的示例
* 封装JSBridge
*/
import WebView from '@ohos.web.webview';
import JSBridge from './JsBridge';
@Entry
@Component
struct WebJSBridgeExample {
webController: WebView.WebviewController = new WebView.WebviewController();
private jsBridge: JSBridge = new JSBridge(this.webController);
build() {
Column() {
Web({
src: $rawfile('MainPage.html'),
controller: this.webController
})
.javaScriptAccess(true)
.javaScriptProxy(this.jsBridge.javaScriptProxy)
.onPageBegin(() => {
// 初始化JsBridge,向网页注入脚本
this.jsBridge.initJsBridge();
})
}
}
}
ets
// JsBridge.ets
import WebView from '@ohos.web.webview';
import promptAction from '@ohos.promptAction';
import hilog from '@ohos.hilog';
export interface CallType {
call: (func: string, params: string) => void;
}
export interface JavaScriptItem {
object: CallType;
name: string;
methodList: Array<string>;
controller: WebviewController;
}
export interface ParamsItem {
callID: number;
data: ParamsDataItem;
}
export interface ParamsDataItem {
name?: string;
tel?: string;
}
/**
* 定义需要注入到网页上的脚本
*/
export const code = `
const JSBridgeMap = {};
let callID = 0;
function JSBridgeCallback (id, params){
JSBridgeMap[id](params);
JSBridgeMap[id] = null;
delete JSBridgeMap[id];
}
window.ohosCallNative = {
callNative(method, params, callback){
const id = callID++;
const paramsObj = {
callID: id,
data: params || null
}
JSBridgeMap[id] = callback || (() => {}); // 注册回调
JSBridgeHandle.call(method, JSON.stringify(paramsObj));
}
}
`;
/**
* Define bridge class connect WebView and ArkTS.
*/
export default class JsBridge {
controller: WebView.WebviewController;
constructor(controller: WebView.WebviewController) {
this.controller = controller;
}
/**
* Injects the JavaScript object into window and invoke the function in window.
*
* @returns javaScriptProxy object.
*/
get javaScriptProxy(): JavaScriptItem {
let result: JavaScriptItem = {
object: { // 参与注册的对象。只能声明方法,不能声明属性。
call: this.call
},
name: "JSBridgeHandle", // 注册对象的名称,与window中调用的对象名一致。
methodList: ['call'], // 参与注册的应用侧JavaScript对象的方法。
controller: this.controller // 组件的控制器
}
return result;
}
initJsBridge(): void {
this.controller.runJavaScript(code);
}
/**
* 例子:执行网页上的方法
*/
runWebFunction() {
const code = `webFunction()`; // 要在网页上执行的代码
this.controller.runJavaScript(code, (error, result) => {
if (error) {
console.info(`run JavaScript error: ` + JSON.stringify(error))
}
if (result) {
console.info(`The webFunction() return value is: ${result}`)
promptAction.showToast({
message: `The webFunction() return value is: ${result}`
})
}
});
}
/**
* 接收网页端的调用,通过方法名(func)分发
*/
call = (func: string, params: string): void => {
hilog.debug(0x0000, "call_func", func)
hilog.debug(0x0000, "call_params", params)
const paramsObject: ParamsItem = JSON.parse(params);
let result: Promise<string> = new Promise((resolve) => resolve(''));
switch (func) {
default:
break;
}
result.then((data: string) => {
// callID 用于映射回调函数
this.callback(paramsObject?.callID, data);
})
}
/**
* The ArkTS invoke the WebView by using runJavaScript.
*/
callback = (id: number, data: string): void => {
this.controller.runJavaScript(`JSBridgeCallback("${id}", ${JSON.stringify(data)})`);
}
}
静态文件
html
<!-- rawfile/MainPage.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>testApp</title>
</head>
<body>
<div class="container">
<h1>Web Page</h1>
<div id="text"></div>
<div id="text2"></div>
</div>
<script src="./js/mainPage.js"></script>
</body>
</html>
js
// rawfile/js/mainPage.js
function runAppFunctionExample() {
// 调用App注入的方法
window.ohosCallNative.callNative('yourFunctionName', {}, (data) => {
// 执行App的回调
document.getElementById('text2').innerText = JSON.stringify(data);
})
}
function webFunction() {
document.getElementById('text').innerText = '网页上的方法被调用了'
return '返回值'
}
document.getElementById('text').innerText = 'test'
document.getElementById('text2').innerText = '3秒后自动调用App的方法'
setTimeout(() => {
runAppFunctionExample()
}, 3000)
全局弹窗封装
CustomDialog 方案
创建自定义弹窗组件
ets
@CustomDialog
export struct CustomDialogView {
@Link visible: boolean;
controller: CustomDialogController;
// 弹窗交互事件参数,点击确认和取消按钮时的回调函数
onCancel?: () => void;
onConfirm?: () => void;
build() {
Row() {
Button()
.onClick(() => {
this.visible = false;
this.onCancel?.();
})
}
}
}
自定义组件 Dialog,对自定义弹窗组件进行二次封装。
ets
@Component
export struct Dialog {
// 监听外部传入的visible变量,visible值发生变化时触发onChange回调函数
@Watch("onChange") @Link visible: boolean;
onCancel?: () => void;
onConfirm?: () => void;
// 通过CustomDialogController的builder参数绑定弹窗组件CustomDialogView
private controller = new CustomDialogController({
builder: CustomDialogView({
visible: $visible,
onCancel: this.onCancel,
onConfirm: this.onConfirm,
}),
})
/**
* 当visible的值变化时触发回调
*/
onChange(): void{
if (this.visible) {
this.controller.open();
} else {
this.controller.close();
}
}
// 二次封装的Dialog组件主要通过控制器控制弹窗,不需要任何界面
build() {
}
}
使用方导入自定义组件 Dialog 并传入相应入参。
ets
@Component
export struct CustomDialog {
// 外部定义visible变量作为弹窗组件入参,控制弹窗显隐
@State visible: boolean = false;
build() {
Column({ space: 20 }) {
Button()
// 点击修改visible变量后,visible的值可以被Dialog组件监听并响应
.onClick(() => this.visible = !this.visible)
// 通过双向绑定visible变量,实现外部控制弹窗
Dialog({
visible: $visible,
})
}
}
}
其它方案(子窗口)
https://gitee.com/wolfx/ohos_libs/tree/dev/package_utils/src/main/ets/common
树状递归组件示例
数据结构
ets
@Observed
class TreeNode<T> {
id: number;
detail: T;
parent: TreeNode<T> | null;
children: TreeNode<T>[] = [];
constructor(id: number, detail: T, parent: TreeNode<T> | null) {
this.id = id;
this.detail = detail;
this.parent = parent;
}
addChildren(children: TreeNode<T>[]) {
children.forEach(child => {
this.children.push(child);
});
}
}
@Component
struct TreeView {
@ObjectLink tree: TreeNode<string>
build() {
Column() {
Text(this.tree.detail)
.fontSize(40)
.onClick(() => {
this.tree.detail += '1'
})
Column() {
ForEach(this.tree.children, (child: TreeNode<string>) => {
TreeView({ tree: child })
}, (child: TreeNode<string>) => child.id.toString())
}
.padding({ left: 40 })
.alignItems(HorizontalAlign.Start)
}
.alignItems(HorizontalAlign.Start)
}
}
组件示例
ets
@Entry
@Component
struct Index {
@State tree: TreeNode<string> = new TreeNode(0, 'root', null);
aboutToAppear(): void {
let node3 = new TreeNode(3, 'child3', this.tree)
node3.addChildren([
new TreeNode(44, 'child4', node3),
new TreeNode(55, 'child5', node3),
]);
this.tree.addChildren([
new TreeNode(1, 'child1', this.tree),
new TreeNode(2, 'child2', this.tree),
node3,
]);
}
build() {
Row() {
Column() {
Scroll() {
TreeView({ tree: this.tree })
}
.scrollable(ScrollDirection.Vertical) // 滚动方向纵向
}
.width('100%')
.height(200)
.backgroundColor(Color.Gray)
}
.height('100%')
}
}
自定义渲染节点
ets
import { BuilderNode, FrameNode, NodeController, Size } from '@ohos.arkui.node';
class Params {
text: string = "this is a text"
}
@Builder
function buttonBuilder(params: Params) {
Column() {
Button(params.text)
.fontSize(12)
.borderRadius(8)
.borderWidth(2)
.backgroundColor(Color.Orange)
}
}
export class MyNodeController extends NodeController {
private buttonNode: BuilderNode<[Params]> | null = null;
private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(buttonBuilder);
makeNode(uiContext: UIContext): FrameNode {
console.log("makeNode")
if (this.buttonNode == null) {
this.buttonNode = new BuilderNode(uiContext);
this.buttonNode.build(this.wrapBuilder, { text: 'This is a Button' })
}
return this.buttonNode!.getFrameNode()!;
}
aboutToResize(size: Size) {
console.log("aboutToResize width : " + size.width + " height : " + size.height)
}
changeText(text: string) {
this.buttonNode?.update({ text } as Params)
// this.rebuild()
}
aboutToAppear() {
console.log("aboutToAppear")
}
aboutToDisappear() {
console.log("aboutToDisappear");
}
onTouchEvent(event:TouchEvent) {
console.log("onTouchEvent");
}
}
ets
private myNodeController: MyNodeController = new MyNodeController();
// ...
Column() {
NodeContainer(this.myNodeController).onClick(() => {
// promptAction.showToast({message: '111'})
this.myNodeController.changeText('111')
})
Text(JSON.stringify(this.obj))
Text(this.msg).fontSize(20).fontColor(Color.White)
}