本示例基于应用拉起相关能力,实现了Web页面与ArkTS页面的相互拉起、从Web页面拉起系统应用等场景。
1. 案例效果截图
2. 案例运用到的知识点
2.1. 核心知识点
- Web组件:onLoadIntercept拦截器拦截页面加载。
- Navigation:组件导航。
- UIAbility:为对应系统应用配置Want参数,使用startAbility进行拉起。
- 环境变量获取:调用Ability接口直接获取系统环境变量
2.2. 其他知识点
- ArkTS 语言基础
- V2版状态管理:@ComponentV2/@Provider/@Consumer
- 自定义组件和组件生命周期
- 内置组件:Column/Button
- 日志管理类的编写
- 常量与资源分类的访问
- MVVM模式
3. 代码结构
├──entry/src/main/ets // 代码区
│ ├──common
│ | ├──Logger.ets // 日志工具类
│ | └──Constants.ets // 常量
│ ├──entryability
│ | └──EntryAbility.ets
│ ├──entrybackupability
│ | └──EntryBackupAbility.ets
│ └──pages
│ ├──Index.ets // 入口界面
│ └──OriginPage.ets // 原生页面
└──entry/src/main/resources // 应用资源目录
4. 公共文件与资源
本案例涉及到的常量类和工具类代码如下:
4.1. 通用常量类
// entry/src/main/ets/common/utils/CommonConstants.ets
export class Constants {
static readonly ORIGIN_PAGE: string = 'OriginPage'
static readonly FULL_SCREEN: string = '100%'
static readonly WEB_PAGE: string = 'WebPage'
static readonly ORIGIN_PAGE_CHINESE: string = '原生页面'
static readonly WEB_PAGE_CHINESE: string = 'Web页面'
static readonly ORIGIN_WEB_PAGE_LINK: string = 'resource://rawfile/index.html'
static readonly WEB_PAGE_ADDRESS: string = 'resource://rawfile/index1.html'
static readonly SYSTEM_LANGUAGE_KEY: string = 'systemLanguage'
static readonly CHINESE_LANGUAGE: string = 'zh-Hans-CN'
static readonly ENGLISH_LANGUAGE: string = 'en-Latn-CN'
}
4.2. 日志类
// main/ets/common/utils/Logger.ets
import { hilog } from '@kit.PerformanceAnalysisKit'
export class Logger {
private static domain: number = 0x0000
private static prefix: string = 'WebPullOtherApplication'
private static format: string = '%{public}s, %{public}s'
static debug(...args: string[]): void {
hilog.debug(Logger.domain, Logger.prefix, Logger.format, args)
}
static info(...args: string[]): void {
hilog.info(Logger.domain, Logger.prefix, Logger.format, args)
}
static warn(...args: string[]): void {
hilog.warn(Logger.domain, Logger.prefix, Logger.format, args)
}
static error(...args: string[]): void {
hilog.error(Logger.domain, Logger.prefix, Logger.format, args)
}
}
本案例涉及到的资源文件如下:
- string.json
// main/resources/base/element/string.json
{
"string": [
{
"name": "module_desc",
"value": "模块描述"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "Web应用拉起"
},
{
"name": "back_to_web_page",
"value": "跳转回Web页面"
},
{
"name": "original_page",
"value": "原生页面"
}
]
}
2. float.json
// main/resources/base/element/float.json
{
"float": [
{
"name": "page_text_font_size",
"value": "50fp"
},
{
"name": "space_24",
"value": "24vp"
},
{
"name": "space_bottom",
"value": "16vp"
},
{
"name": "button_height",
"value": "40vp"
}
]
}
其他资源请到源码中获取。
5. 应用侧首页面
// entry/src/main/ets/pages/Index.ets
import { webview } from '@kit.ArkWeb'
import { common, Want } from '@kit.AbilityKit'
import { BusinessError } from '@kit.BasicServicesKit'
import { OriginPage } from './OriginPage'
import { Constants } from '../common/Constants'
import { Logger } from '../common/Logger'
import { env } from '../common/Env'
// 正则表达式用于检测资源URL前缀
const regex = /^resource:///
@Entry
@ComponentV2
struct Index {
// 导航路径堆栈,管理页面导航
@Provider('navPathStack') navPathStack: NavPathStack = new NavPathStack()
// WebView 控制器
private controller: WebviewController = new webview.WebviewController()
// UIAbility 上下文对象
private context = getContext(this) as common.UIAbilityContext
// 用于存储不同 URL 对应的处理函数
private functionsMap: Map<string, () => void> = new Map()
// 存储当前应用的包名
private bundleName: string = ''
aboutToAppear(): void {
this.bundleName = this.context.abilityInfo.bundleName // 获取当前应用包名
this.initFunctionsMap() // 初始化 URL 处理函数映射
}
// 处理返回按键事件
onBackPress(): boolean | void {
if (this.controller.accessBackward()) { // 如果 WebView 可以后退
this.controller.backward() // 让 WebView 执行后退操作
return true
}
}
// 页面映射方法,用于导航到不同页面
@Builder
PageMap(name: string) {
if (name === Constants.ORIGIN_PAGE) {
OriginPage() // 跳转到 OriginPage 页面
}
}
// 初始化 URL 处理函数映射
initFunctionsMap() {
// 处理跳转到 OriginPage 的请求
this.functionsMap.set('arkts://pages/toOriginPage',
() => this.navPathStack.pushPath({ name: Constants.ORIGIN_PAGE }))
// 处理跳转到系统 WiFi 设置页的请求
this.functionsMap.set('network://pages/toSystemApp', () => {
const want: Want = {
bundleName: 'com.huawei.hmos.settings',
abilityName: 'com.huawei.hmos.settings.MainAbility',
uri: 'wifi_entry',
}
// 启动系统 WiFi 设置界面
this.context.startAbility(want).then(() => {
Logger.info(`Successfully started Ability.`)
}).catch((err: BusinessError) => {
Logger.error(`Failed to start Ability. Code: ${err.code},`)
Logger.error(`Message: ${err.message}`)
})
})
}
// 组件构建 UI
build() {
Navigation(this.navPathStack) {
Column() {
Web({
src: $rawfile(env.language == Constants.ENGLISH_LANGUAGE
? 'index_en.html' : 'index_cn.html'), // 根据语言加载不同 HTML 文件
controller: this.controller
})
.zoomAccess(false) // 禁用缩放
.onLoadIntercept((event) => { // 拦截页面加载事件
const url: string = event.data.getRequestUrl() // 获取请求的 URL
// 查找对应的处理函数
const callFunc = this.functionsMap.get(url) as () => void
callFunc && callFunc() // 如果存在处理函数,则执行
return !regex.test(url) // 如果 URL 不是资源路径,则拦截
})
}
}
.hideTitleBar(true) // 隐藏标题栏
.navDestination(this.PageMap) // 设置页面导航映射
.mode(NavigationMode.Stack) // 使用堆栈导航模式
}
}
关键代码说明:
- Web组件的onLoadIntercept方法,执行url拦截工作,如果不是以resource://开头的URL地址,则拦截。
- functionsMap对象里,根据不同的请求源,注册相应的功能函数。
- navPathStack.pushPath实现路由跳转,context.startAbility实现Ability跳转。
- env.language根据环境变量的系统语言的值,来载入不同的前端页面。
6. 环境变量
6.1. 定义环境变量类
// entry/src/main/ets/common/utils/Env.ets
import { ConfigurationConstant } from '@kit.AbilityKit'
export class Env {
language: string | undefined
colorMode: ConfigurationConstant.ColorMode | undefined
fontSizeScale: number | undefined
fontWeightScale: number | undefined
}
export let env: Env = new Env()
6.2. 在EntryAbility里获取并设置环境变量
// entry/src/main/ets/entryability/EntryAbility.ets
// ...
import { env } from '../common/Env'
// ...
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// ...
env.language = this.context.config.language
env.colorMode = this.context.config.colorMode
env.fontSizeScale = this.context.config.fontSizeScale
env.fontWeightScale = this.context.config.fontWeightScale
}
// ...
}
7. web前端页面
7.1. index_cn
<!-- entry/src/main/resource/rawfile/index_cn.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Document</title>
<link rel="stylesheet" href="./css/styles.css">
</head>
<body>
<div class="web_page_demo">
<div class="title">Web和应用的跳转与拉起</div>
<ul>
<li>
<a class="function_item" href="arkts://pages/toOriginPage">
跳转到原生页面
</a>
</li>
<li>
<a class="function_item" href="./index1_cn.html">
跳转到Web页面
</a>
</li>
<li>
<a class="function_item" href="network://pages/toSystemApp">
拉起系统应用
</a>
</li>
</ul>
</div>
</body>
</html>
<script></script>
7.2. index1_cn
<!-- entry/src/main/resource/rawfile/index1_cn.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Document</title>
<link rel="stylesheet" href="./css/styles1.css">
</head>
<body>
<div class="web_page_demo">
<div class="header">
<div class="back">
<img onclick="backup()" src="./statics/ic_back.png" alt=""/>
</div>
<span>Web 页面</span>
</div>
<div class="title">Hello Web</div>
<a class="function_item" href="javascript:void(0);" onclick="backup()"
>跳转回Web页面</a
>
</div>
</body>
</html>
<script>
function backup(event) {
window.history.back()
}
</script>
其他前端资源请到源码中获取。
8. 应用侧原始页面
// main/ets/pages/OriginPage.ets
import { Constants } from '../common/Constants'
import { env } from '../common/Env'
@ComponentV2
export struct OriginPage {
@Consumer('navPathStack') navPathStack: NavPathStack = new NavPathStack()
build() {
NavDestination() {
Column() {
Button($r('app.string.back_to_web_page'))
.width(Constants.FULL_SCREEN)
.height($r('app.float.button_height'))
.onClick(() => {
this.navPathStack.pop()
})
}
.width(Constants.FULL_SCREEN)
.height(Constants.FULL_SCREEN)
.justifyContent(FlexAlign.End)
.padding({
left: $r('app.float.space_24'),
right: $r('app.float.space_24'),
bottom: $r('app.float.space_bottom')
})
}
.title(env.language == Constants.ENGLISH_LANGUAGE
? Constants.ORIGIN_PAGE : Constants.ORIGIN_PAGE_CHINESE)
}
}
关键代码说明:
- @Consumer('navPathStack')通过消费父组件的navPathStack,获得路由栈对象。
- env.language == Constants.ENGLISH_LANGUAGE根据系统语言显示title。
9. 结语
✋ 需要参加鸿蒙认证的请点击 鸿蒙认证链接