本文介绍如何将 H5 容器组件接入到 HarmonyOS NEXT 客户端。您可以基于已有工程使用 ohpmrc 方式接入 H5 容器 SDK 到客户端。
前置条件
添加 H5 容器 SDK 之前,请您确保已经将工程接入到 mPaaS。更多信息请参见 接入 mPaaS 能力。
引入依赖
在项目的.ohpmrc
文件中添加如下仓库:
@mpaas:registry=https://mpaas-ohpm.oss-cn-hangzhou.aliyuncs.com/meta
添加 SDK
通过 使用 mppm 工具 安装 H5 容器组件。
配置权限
在 module.json5
中配置所需权限。
"requestPermissions":[
{
"name" : "ohos.permission.GET_NETWORK_INFO",
},
{
"name" : "ohos.permission.INTERNET",
}
]
使用 SDK
初始化
在 mPaaS 框架初始化完成之后,初始化 HRiver
。代码如下:
HRiver.init();
使用 SDK 之前必须初始化 mPaaS 框架,设置 userId 和 appsecret,其中 appsecret 从 AppCenter 后台获取,具体查看 获取 HarmonyOS NEXT config 配置文件(beta) 。
代码示例如下:
import AbilityStage from '@ohos.app.ability.AbilityStage';
import { MPFramework } from '@mpaas/framework';
export default class ModuleEntry extends AbilityStage {
async onCreate() {
MPFramework.create(this.context);
MPFramework.instance.userId = 'MPTestCase'
MPFramework.instance.appSecret = "12a711d78980f661aca5401788fdf09a";
}
}
打开离线包
初始化 HRiver
后调用 startApp
打开离线包。
import { HRiver } from '@mpaas/hriver'
/**
* 打开离线包
* @param appId: 离线包id
* @param startParams: 启动参数,可以不传。控制TitleBar、指定页面等
*/
HRiver.startApp(appId: string, startParams?: Map<string, Object>)
代码示例如下:
import { HRiver } from '@mpaas/hriver'
// 示例1:
HRiver.startApp('20190517') // 直接启动离线包
// 示例2:
let startParams: Map<string, Object> = new Map();
startParams.set('defaultTitle', '默认标题') //加载阶段显示默认标题
HRiver.startApp('20190517', startParams)
打开在线页面
初始化 HRiver
之后调用 startUrl
打开在线页面。
import { HRiver } from '@mpaas/hriver'
/**
* 打开在线页面
* @param url: 在线地址
* @param startParams: 启动参数。可以不传,控制TitleBar、指定页面等
*/
HRiver.startUrl(url: string, startParams: Map<string, Object>)
注册自定义 JSAPI
初始化 HRiver
之后调用 registerPlugin
注册自定义 JSAPI。
import { HRiver } from '@mpaas/hriver'
/**
* 注册自定义 JSApi 实现
* @param pluginClass: pluginClass列表,如 registerPlugin({CustomPlugin1, CustomPlugin2}, HRiver.SCOPE_PAGE)
* @param scope: 可以不传。默认HRiver.SCOPE_PAGE。HRiver.SCOPE_APP表示离线包App级别生命周期;HRiver.SCOPE_PAGE表示页面级别生命周期
*/
HRiver.registerPlugin(pluginClass: ESObject, scope: string)
代码示例:
HRiver.registerPlugin({H5CustomPlugin, H5Custom1Plugin})
plugin 实现代码示例如下:
import { HRiver, H5SimplePlugin, H5EventFilter, H5Event, H5BridgeContext } from '@mpaas/hriver'
class H5CustomPlugin extends H5SimplePlugin {
onPrepare(filter: H5EventFilter): void {
filter.addAction('myapi1')
}
handleEvent(event: H5Event, context: H5BridgeContext): Boolean {
if ('myapi1' == event.action) {
context.sendBridgeResult({
success: true,
data: 'myapi1调用成功'
})
return true
}
return super.handleEvent(event, context);
}
}
class H5Custom1Plugin extends H5SimplePlugin {
onPrepare(filter: H5EventFilter): void {
filter.addAction('myapi2')
}
handleEvent(event: H5Event, context: H5BridgeContext): Boolean {
if ('myapi2' == event.action) {
context.sendBridgeResult({
success: true,
data: 'myapi2调用成功'
})
return true
}
return super.handleEvent(event, context);
}
}
Native 调用 H5
Native 调用 H5 有以下两种方法。
在自定义 JSAPI 中通过
H5BridgeContext.sendToWeb
方法调用 H5。import { H5BridgeContext, H5Event, H5EventFilter, H5SimplePlugin, HRiver } from '@mpaas/hriver'; class H5CustomPlugin extends H5SimplePlugin { handleEvent(event: H5Event, context: H5BridgeContext): Boolean { // native 调用 h5 context.sendToWeb('customCallWeb', { data: 'abc' }) ... // 其他代码 } }
在 Native 代码中获取
TopApp
的activityPage
,获得最新的页面,通过页面调用sendToWeb
方法。import { HRiver, XRiverProxy, getProxy, AppManager, AppNode, Page } from '@mpaas/hriver'; { let appManager = getProxy(XRiverProxy.AppManager) as AppManager let appNode: AppNode | null = appManager.findTopApp() if (appNode != null) { let page: Page | null = appNode.getActivePage() if (page != null) { page.sendToWeb('testAction', {data: ''}) } } }
加载内置离线包
加载所有内置离线包,包括内置的公共离线包。
初始化 HRiver
之后调用 loadOfflineResource
加载内置离线包。
import { HRiver } from '@mpaas/hriver'
/**
* 加载内置离线包
* @param jsonFileName: 内置离线包的 h5_json.json 的文件名,放到rawfile目录中。如:h5_json.json。
* @param callback: 格式 (result: string) => {}。内置离线包加载完成回调的 Function
*/
HRiver.loadOfflineResource(jsonFileName: string, callback: Function)
代码示例如下:
在
entry/src/main/resources/rawfile
下添加h5_json.json
(文件从 Appcenter 后台下载即可)。{ "config":{ "updateReqRate":16400, "limitReqRate":13600, "appPoolLimit":3, "versionRefreshRate":86400 }, "data":[ { "app_desc":"离线包1", "app_id":"20180910", "auto_install":1, "fallback_base_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20180910/1.0.0.3_all/nebula/fallback/", "global_pack_url":"", "icon_url":"", "installType":1, "main_url":"/www/index.html", "name":"离线包1", "online":1, "package_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20180910/1.0.0.3_all/nebula/20180910_1.0.0.3.amr", "patch":"", "sub_url":"", "version":"1.0.0.3", "vhost":"https://20180910.h5app.com" }, { "app_desc":"离线包2", "app_id":"20190517", "auto_install":1, "fallback_base_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20190517/1.0.0.0_all/nebula/fallback/", "global_pack_url":"", "icon_url":"", "installType":1, "main_url":"/www/index.html", "name":"离线包2", "online":1, "package_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20190517/1.0.0.0_all/nebula/20190517_1.0.0.0.amr", "patch":"", "sub_url":"", "version":"1.0.0.0", "vhost":"https://20190517.h5app.com" } ], "resultCode":100, "resultMsg":"操作成功", "state":"success" }
下载 amr 并命名为
${appid}_${version}.amr
。调用 API。
import { HRiver } from '@mpaas/hriver' HRiver.loadOfflineResource('h5_json.json', (result: string) => { this.resultText = this.resultText + `\n${result} 预加载成功` })
更新离线包
import { HRiver } from '@mpaas/hriver'
/**
* 批量更新离线包
* @param appIds: 需要更新离线包的appId列表
* @param updateCallback: 格式必须 (result: boolean, code: number) => {}。更新接口返回后回调的Function
*/
HRiver.updateApp(appIds?: Array<string>, updateCallback?: Function)
/**
* 更新所有离线包。
* @param updateCallback: 格式必须 (result: boolean, code: number) => {}。更新接口返回后回调的Function
*/
HRiver.updateAll(updateCallback: Function)
/**
* 批量更新离线包
* @param appIds: 离线包的appId和version的map
* @param updateCallback: 格式必须 (result: boolean, code: number) => {}。更新接口返回后回调的Function
*/
HRiver.updateAppWithVersion(appIds?: Map<string, string>, updateCallback?: Function)
代码示例如下:
import { HRiver } from '@mpaas/hriver'
HRiver.updateApp(['90000002'], (result: boolean, code: number) => {
this.resultText = `90000002更新结果: ${result}`
})
配置公共离线包
设置
H5CommonAppProvider
。import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' /** * 配置公共离线包 Provider,必须在 HRiver.init() 之前调用 */ HRiver.setProvider(H5CommonAppProvider.name, new H5AppCommonProviderImpl())
说明setProvider
必须在HRiver.init()
之前调用。实现
H5AppCommonProviderImpl.ets
的getCommonResourceAppList
方法。import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' export class H5AppCommonProviderImpl extends H5CommonAppProvider { getCommonResourceAppList(): Array<string> { return ['20220719'] // 返回公共离线包id列表 } }
支持 fallback 逻辑
保持和 iOS/Android 一致,离线包没下载成功情况下优先打开 fallback 在线地址。
该功能默认关闭,可以通过以下开关打开:
实现 H5CommonAppProvider 的 configJSON 方法,增加 enableFallback 参数。
export class H5AppCommonProviderImpl extends H5CommonAppProvider {
... // 其他配置
// configJson,配置更新频率等
configJSON(): string {
return JSON.stringify({
enableFallback: 'YES',
xxx // 其他配置
})
}
}
设置 UserAgent
import { HRiver } from '@mpaas/hriver'
HRiver.setUserAgent(userAgent)
setUserAgent
在 HRiver.init()
之后调用,会在默认 UserAgent
之后拼接设置的 useragent
。
设置离线包默认更新频率
设置
H5CommonAppProvider
。import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' /** * 配置公共离线包Provider、离线包更新频率等功能,必须在HRiver.init()之前调用 */ HRiver.setProvider(H5CommonAppProvider.name, new H5AppCommonProviderImpl())
说明setProvider
必须在HRiver.init()
之前调用。实现
H5CommonAppProvider
的configJSON
方法,代码示例如下。import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' export class H5AppCommonProviderImpl extends H5CommonAppProvider { ... // 其他配置 // configJson,配置更新频率等 configJSON(): string { return JSON.stringify({ h5_nbmngconfig: "{\"config\":{\"al\":\"3\",\"pr\":{\"4\":\"86400\",\"common\":\"864000\"},\"ur\":\"1\",\"fpr\":{\"common\":\"3888000\"}},\"switch\":\"yes\"}" }) } }
具体参数如下:
h5_nbmngconfig: "{\"config\":{\"al\":\"3\",\"pr\":{\"4\":\"86400\",\"common\":\"864000\"},\"ur\":\"1800\",\"fpr\":{\"common\":\"3888000\"}},\"switch\":\"yes\"}"
其中的
ur: 1800
表示更新频率为 1800 秒,使用时修改ur
的值即可。
配置离线包签名校验
设置
H5CommonAppProvider
。import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' /** * 配置公共离线包Provider、离线包更新频率、签名校验等功能,必须在HRiver.init()之前调用 */ HRiver.setProvider(H5CommonAppProvider.name, new H5AppCommonProviderImpl())
说明setProvider
必须在HRiver.init()
之前调用。实现
H5CommonAppProvider
的shouldVerify
和pubKey
方法,代码示例如下:import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' export class H5AppCommonProviderImpl extends H5CommonAppProvider { ... // 其他配置 /** * 是否开启离线包校验,默认关闭 * return: true表示开启,false表示关闭 */ shouldVerify(): boolean { return false } // 离线包校验的公钥,如果shouldVerify为false 则无需设置,否则必须设置公钥 pubKey(): string { return '' } }
开启调试模式
HRiver.enableDebug(true)
监听页面生命周期
初始化完成后设置 provider。
import {H5PageLifeCycleProvider} from '@mpaas/hriver';
HRiver.setProvider(H5PageLifeCycleProvider.name, new H5PageLifeCycle())
H5PageLifeCycle
import { H5PageLifeCycleProvider, Page } from '@mpaas/hriver';
import { hilog } from '@kit.PerformanceAnalysisKit';
export class H5PageLifeCycle extends H5PageLifeCycleProvider {
onPageShow(routerName: string, page?: Page | undefined): void {
super.onPageShow(routerName, page);
hilog.debug(1, 'H5PageLifeCycle', "pageshow: " + page?.pageUrl)
}
onPageHide(routerName: string, page?: Page | undefined): void {
super.onPageHide(routerName, page);
hilog.debug(1, 'H5PageLifeCycle', "onPageHide: " + page?.pageUrl)
}
onPageCreate(page?: Page | undefined): void {
super.onPageCreate(page);
hilog.debug(1, 'H5PageLifeCycle', "onPageCreate: " + page?.pageUrl)
}
onPageExit(page?: Page | undefined): void {
super.onPageExit(page);
hilog.debug(1, 'H5PageLifeCycle', "onPageExit: " + page?.pageUrl)
}
onBackPress(page?: Page | undefined): boolean {
hilog.debug(1, 'H5PageLifeCycle', "onBackPress: " + page?.pageUrl)
return super.onBackPress(page);
}
}
支持注销 Plugin
通过 Page 的 Page.ets 实现注销。
// 根据action注销
unregisterPluginByAction(action: string);
// 根据pluginName注销
unregisterPluginByPluginName(name: string);
示例如下:
支持页面嵌入模式
页面嵌入模式基于 Navigation 实现。
创建离线包需要的 NavPathStack。
pageInfos: NavPathStack = new NavPathStack()
在页面需要嵌入的位置添加空白组件,以下为示例:
重要mode 必须使用
NavigationMode.Stack
,否则横竖屏/折叠屏切换有问题。Navigation(this.pageInfos) { Column() { // 空白页面用于嵌入离线包页面, 不用填任何内容 }.width('100%') // 宽度根据需要 .backgroundColor(Color.Red) // 只是示例 .height(500) // 高度根据实际 }.navDestination(this.PagesMap) .mode(NavigationMode.Stack) // pagesMap实现: import {HRBuilder, RouterUtils, HRiver, H5RouterNavStackProvider} from '@mpaas/hriver' let mPaaSHRiverBuilder: WrappedBuilder<[string, ESObject]> = wrapBuilder(HRBuilder); @Builder PagesMap(name: string, params: ESObject) { if (RouterUtils.isMPHRiverPage(name)) { mPaaSHRiverBuilder.builder(name, params) } else if (name == 'xxx') { // 其他业务的页面 } }
启动离线包和在线页面时,需要添加第三个参数并传入步骤 1 中创建的 NavPathStack,增加 embedPage 参数用来表示内嵌页面。
let param: Map<string, Object> = new Map<string, Object>() param.set('embedPage', 'YES') HRiver.startUrl('https://www.baidu.com', param, this.pageInfos) // HRiver.startApp('90000000', param, this.pageInfos)
添加返回事件拦截。
HRiver.setProvider(H5PageLifeCycleProvider.name, new H5PageLifeCycle())
import { H5PageLifeCycleProvider, Page } from '@mpaas/hriver'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { router } from '@kit.ArkUI'; export class H5PageLifeCycle extends H5PageLifeCycleProvider { onPageShow(routerName: string, page?: Page | undefined): void { super.onPageShow(routerName, page); hilog.debug(1, 'H5PageLifeCycle', "onPageShow: " + page?.pageUrl) } onPageHide(routerName: string, page?: Page | undefined): void { super.onPageHide(routerName, page); hilog.debug(1, 'H5PageLifeCycle', "onPageHide: " + page?.pageUrl) } onPageCreate(page?: Page | undefined): void { super.onPageCreate(page); hilog.debug(1, 'H5PageLifeCycle', "onPageCreate: " + page?.pageUrl) } onPageExit(page?: Page | undefined): void { super.onPageExit(page); hilog.debug(1, 'H5PageLifeCycle', "onPageExit: " + page?.pageUrl) } onBackPress(page?: Page | undefined): boolean { hilog.debug(1, 'H5PageLifeCycle', "onBackPress: " + page?.pageUrl) let navPathStack: NavPathStack|undefined = page?.getSession()?.getRouter()?.getNavPathStack() if (navPathStack && page && page.embedPage && navPathStack.size() == 1) { router.back() return true; } return super.onBackPress(page); } }
UI 定制
自定义导航栏
初始化完成后通过
provider
设置自定义导航栏。import {HRBuilder, RouterUtils, HRiver, H5CommonAppProvider, H5RouterNavStackProvider, CustomUIBuilderProvider, H5CacheProvider } from '@mpaas/hriver' HRiver.setProvider(CustomUIBuilderProvider.name, new CustomUIBuilderProviderImpl())
实现
CustomUIBuilderProviderImpl
。import { CustomUIBuilderProvider, Page } from '@mpaas/hriver'; import { CustomUIBuilder } from '../pages/CustomTitleBarComponent'; export class CustomUIBuilderProviderImpl extends CustomUIBuilderProvider { getCustomUIBuilder(): WrappedBuilder<[string, Page]> { return wrapBuilder(CustomUIBuilder); } }
其中
CustomUIBuilder
为业务自定义titlebar
组件的全局 Builder。实现参考如下:/** * name: 自定义组件的名称 * page: 自定义titlebar对应的页面Page */ @Builder export function CustomUIBuilder(name: string, p: Page) { if (name === 'titleBar') { CustomTitleBarComponent({page: p, titleBarData: p.titleBarData}) } }
CustomTitleBarComponent
为具体的标题组件,titleBarData
为标题栏所需要的数据,数据发生变化会实时刷新。示例参考如下:import { H5NavMenuItemData, HRiverUtil, Page, TitleBarData } from '@mpaas/hriver' const TAG: string = "CustomTitleBarComponent" const MENU_MARGIN: number = 5 const DEFAULT_MARGIN: number = 12 @Builder export function CustomUIBuilder(name: string, p: Page) { if (name === 'titleBar') { CustomTitleBarComponent({page: p, titleBarData: p.titleBarData}) } } @Component export struct CustomTitleBarComponent { page: Page | null = null @Prop titleBarData: TitleBarData aboutToAppear(): void { } build() { RelativeContainer() { Button() { Image(this.getBackIconImage()) .id('titlebar_back_img') .width(12) .height(20) } .width(48) .height('100%') .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top } }) .id("h5_nav_close") .onClick((event) => { if (this.page != null) { this.page.backClickEvent() } }) Flex({ justifyContent: FlexAlign.Center, direction: FlexDirection.Column }) { //如果设置了图片标题就只显示图片 if (this.titleBarData.titleImage) { Image(this.titleBarData.titleImage) .id('titlebar_title_image') .height(36).onClick(() => { this.onTitleClick() }) } else { Flex({ justifyContent: FlexAlign.Start,direction: FlexDirection.Row }){ Text(this.titleBarData.title) .fontSize(18) .textAlign(TextAlign.Start) .textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(1) .fontColor(this.titleBarData.titleColor) .onClick(() => { this.onTitleClick() }) if(this.titleBarData.showTitleLoading ){ Image(this.getTitleBarLoadingIcon()).width(18).height(18) .id('titlebar_progress_img') .margin({left:5,top:1}) .rotate({ angle: this.titleBarData.loadingRotateAngle }) .animation({ duration:3000, curve: Curve.Linear, delay: 0, iterations: -1, playMode: PlayMode.Normal, }).onAppear(()=>{ this.titleBarData.loadingRotateAngle = 360 }) } } if (this.titleBarData.subtitle) { Text(this.titleBarData.subtitle) .textAlign(TextAlign.Start) .fontColor(this.titleBarData.titleColor) .textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(1) .fontSize(18) .onClick(() => { this.onTitleSubtitleClick() }) } } }.id("h5_tv_title") .height("100%") .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, left: { anchor: 'h5_nav_close', align: HorizontalAlign.End }, right: { anchor: "h5_nav_options", align: HorizontalAlign.Start } }) if (this.titleBarData.optionMenuState) { if (this.titleBarData.menuType == TitleBarData.MENU_TYPE_TITLE) { Text(this.titleBarData.menuTitle) .fontSize(16) .align(Alignment.Center) .textAlign(TextAlign.Center) .fontColor(this.titleBarData.menuColor) .height('100%') .id("h5_nav_options") .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: '__container__', align: HorizontalAlign.End } }) .onClick(() => { this.onMoreClick(false, 0) }) .margin({ right: DEFAULT_MARGIN }) } else if (this.titleBarData.menuType == TitleBarData.MENU_TYPE_ICON) { Button() { Image(HRiverUtil.getIconImage(this.titleBarData.menuIcon)) .id('titlebar_right_icon') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(30) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options") .height("100%") .onClick(() => { this.onMoreClick(false, 0) }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: '__container__', align: HorizontalAlign.End } }) .margin({ right: DEFAULT_MARGIN }) } else if (this.titleBarData.menuType == TitleBarData.MENU_TYPE_MORE) { Button() { Image(HRiverUtil.getIconImage('more')) .id('titlebar_right_more') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(48) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options") .height("100%") .margin({ right: DEFAULT_MARGIN }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: '__container__', align: HorizontalAlign.End } }).onClick(() => { if (!this.titleBarData.preventDefault) { this.titleBarData.customPopup = !this.titleBarData.customPopup } this.onMoreClick(true, 0) }) .bindPopup(this.titleBarData.customPopup , { builder: this.MenuBuilder, placement:Placement.BottomLeft, popupColor:"#fff", onStateChange: (e) => { console.info(JSON.stringify(e.isVisible)) if (!e.isVisible) { this.titleBarData.customPopup = false } } }) } if (this.titleBarData.menuType1 == TitleBarData.MENU_TYPE_TITLE) { Text(this.titleBarData.menuTitle1) .fontSize(16) .align(Alignment.Center) .textAlign(TextAlign.Center) .fontColor(this.titleBarData.menuColor1) .height('100%') .id("h5_nav_options1") .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: 'h5_nav_options', align: HorizontalAlign.Start } }) .margin({ right: MENU_MARGIN }) .onClick(()=> { this.onMoreClick(false, 1) }) } else if (this.titleBarData.menuType1 == TitleBarData.MENU_TYPE_ICON) { Button() { Image(HRiverUtil.getIconImage(this.titleBarData.menuIcon1)) .id('titlebar_right_icon1') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(30) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options1") .height("100%") .onClick(()=> { this.onMoreClick(false, 1) }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: 'h5_nav_options', align: HorizontalAlign.Start } }) .margin({ right: MENU_MARGIN }) } else if (this.titleBarData.menuType1 == TitleBarData.MENU_TYPE_MORE) { Button() { Image(HRiverUtil.getIconImage('more')) .id('titlebar_right_more1') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(48) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options1") .height("100%") .margin({ right: MENU_MARGIN }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: 'h5_nav_options', align: HorizontalAlign.Start } }).onClick(() => { if (!this.titleBarData.preventDefault) { this.titleBarData.customPopup1 = !this.titleBarData.customPopup1 } this.onMoreClick(true, 1) }) .bindPopup(this.titleBarData.customPopup1 , { builder: this.MenuBuilder, placement:Placement.BottomLeft, popupColor:"#fff", onStateChange: (e) => { console.info(JSON.stringify(e.isVisible)) if (!e.isVisible) { this.titleBarData.customPopup1 = false } } }) } } }.height(this.titleBarData.showTitleBar ? 48 : 0) } getTitleBarLoadingIcon(): Resource | string { return $rawfile(`icon/h5_title_bar_progress_bg.webp`) } getIconImage(icon: string): Resource | string { return HRiverUtil.getIconImage(icon) } getBackIconImage(): Resource | string { return $rawfile(`icon/hriverback.webp`) } onTitleClick() { if (this.page != null) { this.page.titleBarClickEvent() } } onTitleSubtitleClick() { if (this.page != null) { this.page.subTitleBarClickEvent() } } onMoreClick(fromMenu:boolean, index: number) { if (this.page != null) { this.page.onMoreClick(fromMenu, index) } } onMoreItemClick(tag: string, name: string,isShowPopMenu:boolean) { if (this.page != null) { this.page.onMoreItemClick(tag, name, isShowPopMenu) } } @Builder MenuBuilder() { Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { ForEach(this.titleBarData.h5NavMenuItemList, (item: H5NavMenuItemData, index) => { Column() { Row() { Image(item.icon).width(20).height(20).margin({ right: 5 }) Text(item.name).fontSize(16) } .width('100%') .height(30) .padding({ left: 10 }) .justifyContent(FlexAlign.Start) .align(Alignment.Center) .onClick(() => { if (item) { this.onMoreItemClick(item.tag || '', item.name || '',false) this.titleBarData.customPopup = false this.titleBarData.customPopup1 = false } }) if (index != this.titleBarData.h5NavMenuItemList.length - 1) { Divider().height(10).width('90%').color('#ccc') } }.padding(5).height(40) }) }.width(150).height(165).backgroundColor("#fff") } }
自定义离线包加载页/错误页
初始化完成之后设置 Provider。
import { CustomLoadingBuilderProvider} from '@mpaas/hriver'; HRiver.setProvider(CustomLoadingBuilderProvider.name, new CustomLoadingBuilderProviderImpl())
实现
CustomLoadingBuilderProviderImpl
类。import { CustomLoadingBuilderProvider, H5Router, HRLoadingData } from '@mpaas/hriver'; import { CustomUIBuilder } from './CustomLoadingComponent'; export class CustomLoadingBuilderProviderImpl extends CustomLoadingBuilderProvider { getCustomUIBuilder(): WrappedBuilder<[string, HRLoadingData, H5Router]> { return wrapBuilder(CustomUIBuilder); } }
实现
CustomLoadingComponent.ets
类,其中通过loadingStatus
控制加载状态。import { H5Router, HRLoadingData, LoadingStatus } from '@mpaas/hriver' const TAG: string = "CustomLoadingComponent" export const Loading_STATE_Init = 0 export const Loading_STATE_Start = 1 export const Loading_STATE_End = 2 export const Loading_STATE_Err = 3 @Builder export function CustomUIBuilder(name: string, loadingData: HRLoadingData, h5Router: H5Router) { if (name === 'loading') { CustomLoadingComponent({loadingStatus: loadingData.loadingStatus, h5Router: h5Router}) } } @Component export struct CustomLoadingComponent { @ObjectLink loadingStatus: LoadingStatus h5Router?: H5Router aboutToAppear(): void { } build() { Row() { Column() { Flex({ direction: FlexDirection.Row }) { //返回按钮 Button() { Image($rawfile("icon/hriverback.webp")) .width(12) .height(20) } .width(48) .height('100%') .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .onClick((event) => { this.h5Router?.routerBack() }) }.width('100%') .height(48) Image(this.loadingStatus.icon ? this.loadingStatus.icon : $rawfile("icon/hriverloading.webp")) .width(40) .height(40) .margin({top: 38}) if (this.loadingStatus.state == Loading_STATE_Err || this.loadingStatus.state == Loading_STATE_Start){ Text(this.loadingStatus.state == Loading_STATE_Err ? `网络不给力,请稍后再试 \n(${this.loadingStatus.code} ${this.loadingStatus.msg})` : this.loadingStatus.title) .fontSize(18) .margin({top: 15}) .textAlign(TextAlign.Center) } } .width('100%') .margin({ top: 48 }) } } }
自定义在线 URL 加载失败的错误页
设置 Provider 拦截网页错误回调。
HRiver.setProvider(H5WebClientProvider.name, new H5WebClientProviderImpl());
在
H5WebClientProviderImpl
中实现onErrorReceive
方法。import { MPFramework } from '@mpaas/framework'; import { H5WebClientProvider, Page } from '@mpaas/hriver'; import { util } from '@kit.ArkTS'; export class H5WebClientProviderImpl extends H5WebClientProvider{ onErrorReceive(page: Page | undefined, request: WebResourceRequest | undefined, err: WebResourceError | undefined): boolean { let errorUrl = request?.getRequestUrl() let errorCode = err?.getErrorCode() if (errorCode == 403 || errorCode == 404) { // keep same with ios,not show errorPage for 404 and 403 // log(TAG, "ignoreErrorPage 404 or 403, return "); return true; } let lastUrl = page?.webcontroller?.getUrl() if (errorUrl == page?.pageUrl || errorUrl == `${page?.pageUrl}/`) { // 从rawfile中读取自定义错误页面demo_custom_err.html let dataBytes = MPFramework.instance.context.resourceManager.getRawFileContentSync('demo_custom_err.html') let textDecoder = new util.TextDecoder("utf-8", { fatal: false, ignoreBOM: false }) let html = textDecoder.decodeWithStream(new Uint8Array(dataBytes), { stream: true }) page?.webcontroller?.loadData(html, "text/html", "utf-8", lastUrl) return true; } return false } }
在
demo_custom_err.html
文件中编写错误页代码。<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="apple-mobile-web-app-capable" content="yes"/> <meta name="apple-mobile-web-app-status-bar-style" content="black"/> <meta name="format-detection" content="telephone=no"/> <meta name="format-detection" content="email=no"/> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=0"/> <title>!!!!</title> <style type="text/css"> body { background-color: #FFF; } .am-page-result { text-align: center; } .am-page-result .am-page-result-pic { width: 135px; height: 135px; margin: 40px auto; } .am-page-result .am-page-result-pic img { width: 100%; height: 100%; } .am-page-result p { margin: 0; font-size: 16px; color: #999; } .am-page-result-button { margin-top: 25px; display: -webkit-box; display: -webkit-flex; display: flex; } .am-button { display: block; margin: 0 10px; padding: 0 10px; height: 42px; text-align: center; font-size: 18px; line-height: 42px; border-radius: 4px; outline: 0; -webkit-appearance: none; -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; width: 50%; } .am-button[am-mode~=white] { border: 1px solid #DDD; color: #666; background-color: #FFF; } .am-button[am-mode~=white]:active { border-color: #D8D8D8; background-color: #F8F8F8; } .am-button[am-mode~=blue] { border: 1px solid #28F; color: #FFF; background-color: #39F; } .am-button[am-mode~=blue]:active { border-color: #17F; background-color: #28F; } .am-button[am-mode~=light], .am-button[am-mode~=light]:active { border: none; color: #39F; background-color: #FFF; } @media screen and (min-device-width: 375px) { .am-page-result .am-page-result-pic { width: 160px; height: 160px; margin: 45px auto; } .am-page-result-button { margin-top: 30px; } .am-button { margin: 0 20px; height: 44px; line-height: 44px; } } @media screen and (min-device-width: 414px) { .am-page-result .am-page-result-pic { width: 180px; height: 180px; margin: 50px auto; } .am-button { margin: 0 24px; height: 50px; line-height: 50px; } } </style> <body> <div class="am-page-result"> <div class="am-page-result-pic"> <img src="data:image/png;base64,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"/> </div> <p>这是自定义错误页面</p> </div> </body> <script> </script> </html>
设置和原生 View 一起滚动
//在启动参数里添加
// scrollForward: 0: SELF_ONLY, 1: SELF_FIRST, 2: PARENT_FIRST, 3 : PARALLEL
// scrollBackward: 0: SELF_ONLY, 1: SELF_FIRST, 2: PARENT_FIRST, 3 : PARALLEL
param.set("scrollForward", 2)
param.set("scrollBackward", 1)
支持限制 Web 组件宽度
通过启动参数控制。
params.set('webWidth', xxx)。 // 参数支持string ('100%'百分比方式) 或者number (例如800表示800px)
动态控制。
page.setWebWidth(webWidth: string | number)
鸿蒙 Web 行为定制
H5WebClientProvider
可以通过实现 H5WebClientProvider
修改鸿蒙 H5 容器 Web 的一些默认行为 API 。
HRiver.setProvider(H5WebClientProvider.name, new H5WebClientProviderImpl())
可定制的 API 如下:
export class H5WebClientProvider {
// 页面http错误回调
onHttpErrorReceive(page: Page | undefined, request: WebResourceRequest | undefined, response: WebResourceResponse | undefined) {
}
// 页面下载回调
onDownloadStart(page: Page | undefined, url: string | undefined, userAgent: string | undefined, contentDisposition: string | undefined,
mimetype: string | undefined, contentLength: number | undefined) {
}
// 页面ssl错误回调
onSslErrorEventReceive(page: Page | undefined, handler: SslErrorHandler, error: SslError) {
}
// 页面错误回调
onErrorReceive(page: Page | undefined, request: WebResourceRequest | undefined, error: WebResourceError | undefined): boolean {
return false
}
// 全屏回调
onFullScreenEnter(page: Page | undefined, handler: FullScreenExitHandler) {
}
// 退出全屏回调
onFullScreenExit(page: Page | undefined) {
}
// web权限申请回调
onPermissionRequest(page: Page | undefined, request: PermissionRequest | undefined) {
}
// web screencapturerequest回调
onScreenCaptureRequest(page: Page | undefined, handler: ScreenCaptureHandler | undefined) {
}
onPageBegin(page: Page | undefined, url: string | undefined) {
}
onAppear(page: Page | undefined) {
}
// web scroll回调
onScroll(x: number, y: number, page: Page | undefined) {
}
getWindow(context: Context, page: Page | undefined): Promise<window.Window> | undefined {
return undefined
}
// web 长按菜单
onContextMenuShow(param: WebContextMenuParam | undefined, result: WebContextMenuResult | undefined) {
return false
}
// web 文件选择回调
onShowFileSelector(fileSelector: FileSelectorParam, result: FileSelectorResult, page: Page | undefined) {
return false
}
// web 定位相关回调
onGeolocationShow(origin: string | undefined, geoLocation: JsGeolocation | undefined, page: Page | undefined) {
}
// web 定位相关回调
onGeolocationHide(page: Page | undefined) {
}
}
H5MixModeSettingProvider
可以通过 H5MixModeSettingProvider
设置 Web 支持 HTTP/HTTPS 的 MixedMode
,具体使用如下:
HRiver.setProvider(H5MixModeSettingProvider.name, new H5MixModeSettingProviderImpl()) // 业务实现H5MixModeSettingProviderImpl
export class H5MixModeSettingProviderImpl extends H5MixModeSettingProvider {
mixMode(page: Page | undefined): MixedMode {
// 根据业务实际情况返回对应的MixedMode
return MixedMode.Compatible
}
}
鸿蒙系统不支持在 HTTPS 页面加载地址为 HTTP 的 IP 链接,要么加载 HTTPS 的 IP 的链接,要么加载 HTTP 的非 IP 链接。
页面路由支持 Navigation
默认页面路由使用 router 方式,鸿蒙 router 方式不支持关闭栈中某个页面,只能一级级回退。离线包支持 Navigation 模式:
支持关闭栈中页面
支持分栏模式
全局 Navigation 模式
HRiver 初始化完成后,在启动离线包之前,需设置
H5RouterNavStackProvider
。HRiver.setProvider(H5RouterNavStackProvider.name, new NavStackProvider(this.pageInfos))
import { H5RouterNavStackProvider } from '@mpaas/hriver'; export class NavStackProvider extends H5RouterNavStackProvider { navStack: NavPathStack // 全局的navPathStack栈 constructor(navStack: NavPathStack) { super(); this.navStack = navStack; } getNavPathStack(): NavPathStack { return this.navStack } }
在
navDestination
中配置builder
,builder
中加载 mPaaS 全局 Builder。import {HRBuilder, RouterUtils, HRiver, H5RouterNavStackProvider} from '@mpaas/hriver' let mPaaSHRiverBuilder: WrappedBuilder<[string, ESObject]> = wrapBuilder(HRBuilder); @Builder PagesMap(name: string, params: ESObject) { if (RouterUtils.isMPHRiverPage(name)) { mPaaSHRiverBuilder.builder(name, params) } else if (name == 'xxx') { // 其他业务的页面 } } build() { Navigation(this.pageInfos) { Row() { Column() { // 业务页面 MainPage() } .width('100%') } }.navDestination(this.PagesMap) }
离线包独立使用 Navigation 模式
HRiver.startApp
/HRiver.startUrl
的第三个参数传入 NavPathStack 即可。参考 全局 Navigation 模式 中的步骤 2。