背景
悬浮视图或者窗体,在Android和iOS两大移动平台均有使用,HarmonyOS 也实现了此功能,如下为大家分享一下效果
准备
- 熟读HarmonyOS 悬浮窗口指导
- 熟读HarmonyOS 手势指导
- 熟读ALC签名指导,用于可以申请 “ohos.permission.SYSTEM_FLOAT_WINDOW” 权限。
- 熟悉的文档在下方
|
实践代码
- 如果开启了悬浮窗口,任何界面的物理返回键事件都会被悬浮窗口拦截掉,即 手势返回废了
- 参数类型易混淆, 拖动 PanGesture 中的onActionUpdate接口,数据单位为 vp ,window中的 moveWindowTo接口参数,数据单位为px
- 采用moveWindowTo实现的窗口拖动效果十分不平滑
- 通过 requestPermissionsFromUser 申请 ohos.permission.SYSTEM_FLOAT_WINDOW 权限时,无法弹出系统权限提示框
片段代码
配置module.json5
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
......
{
"name": "FloatWindowAbility",
"srcEntry": "./ets/myentryability/FloatWindowAbility.ts",
"description": "$string:FloatWindowAbility_desc",
"icon": "$media:icon",
"label": "$string:FloatWindowAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
},
],
"requestPermissions": [
{
"name": "ohos.permission.SYSTEM_FLOAT_WINDOW",
"usedScene": {
"abilities": [
"FloatWindowAbility"
],
"when": "always"
}
}
]
}
}
悬浮窗口UIAbility
import window from '@ohos.window';
import BaseUIAbility from '../baseuiability/BaseUIAbility';
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
import bundleManager from '@ohos.bundle.bundleManager';
const permissions: Array< Permissions > = ['ohos.permission.SYSTEM_FLOAT_WINDOW'];
export default class FloatWindowAbility extends BaseUIAbility {
onWindowStageCreate(windowStage: window.WindowStage) {
// Main window is created, set main page for this ability
let context = this.context;
let atManager = abilityAccessCtrl.createAtManager();
checkPermissions().then((result)= >{
if(result){
// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
atManager.requestPermissionsFromUser(context, permissions).then((data) = > {
let grantStatus: Array< number > = data.authResults;
let length: number = grantStatus.length;
for (let i = 0; i < length; i++) {
if (grantStatus[i] === 0) {
// 用户授权,可以继续访问目标操作
console.log('用户授权,可以继续访问目标操作')
} else {
// 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
console.log('用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限')
return;
}
}
// 授权成功
// 1.创建悬浮窗。
let windowClass = null;
let config = {name: "floatWindow", windowType: window.WindowType.TYPE_FLOAT, ctx: this.context};
window.createWindow(config, (err, data) = > {
if (err.code) {
console.error('Failed to create the floatWindow. Cause: ' + JSON.stringify(err));
return;
}
console.info('Succeeded in creating the floatWindow. Data: ' + JSON.stringify(data));
windowClass = data;
// 2.悬浮窗窗口创建成功后,设置悬浮窗的位置、大小及相关属性等。
windowClass.moveWindowTo(0, 200, (err) = > {
if (err.code) {
console.error('Failed to move the window. Cause:' + JSON.stringify(err));
return;
}
console.info('Succeeded in moving the window.');
});
windowClass.resize(1080, 151, (err) = > {
if (err.code) {
console.error('Failed to change the window size. Cause:' + JSON.stringify(err));
return;
}
console.info('Succeeded in changing the window size.');
});
// 3.为悬浮窗加载对应的目标页面。
windowClass.setUIContent("custompages/FloatPage", (err) = > {
if (err.code) {
console.error('Failed to load the content. Cause:' + JSON.stringify(err));
return;
}
console.info('Succeeded in loading the content.');
// 3.显示悬浮窗。
windowClass.showWindow((err) = > {
if (err.code) {
console.error('Failed to show the window. Cause: ' + JSON.stringify(err));
return;
}
console.info('Succeeded in showing the window.');
});
try {
windowClass.setWindowBackgroundColor('#00000000')
} catch (exception) {
console.error('Failed to set the background color. Cause: ' + JSON.stringify(exception));
}
});
})
}).catch((err) = > {
console.error(`requestPermissionsFromUser failed, code is ${err.code}, message is ${err.message}`);
})
}
})
}
}
async function checkAccessToken(permission: Permissions): Promise< abilityAccessCtrl.GrantStatus > {
let atManager = abilityAccessCtrl.createAtManager();
let grantStatus: abilityAccessCtrl.GrantStatus;
// 获取应用程序的accessTokenID
let tokenId: number;
try {
let bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
tokenId = appInfo.accessTokenId;
} catch (err) {
console.error(`getBundleInfoForSelf failed, code is ${err.code}, message is ${err.message}`);
}
// 校验应用是否被授予权限
try {
grantStatus = await atManager.checkAccessToken(tokenId, permission);
} catch (err) {
console.error(`checkAccessToken failed, code is ${err.code}, message is ${err.message}`);
}
return grantStatus;
}
async function checkPermissions(): Promise< boolean > {
const permissions: Array< Permissions > = ['ohos.permission.SYSTEM_FLOAT_WINDOW'];
let grantStatus: abilityAccessCtrl.GrantStatus = await checkAccessToken(permissions[0]);
if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
// 已经授权,可以继续访问目标操作
console.log('没有授权')
return true
} else {
// 申请日历权限
console.log('已授权')
return false
}
}
悬浮窗口页面
import common from '@ohos.app.ability.common';
import window from '@ohos.window';
@Entry
@Component
struct Index {
@State lasttime: number = 0
@State message: string = '悬浮窗'
@State foldStatus: boolean = false
@State idleName: string = '收起'
@State floatWindowWidth: number = 0
@State offsetX: number = 0
@State offsetY: number = 0
@State positionX: number = 0
@State positionY: number = 0
@State windowPosition: Position = { x: 0, y: 0 };
private context = getContext(this) as common.UIAbilityContext;
private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.All });
floatWindow: window.Window
aboutToAppear(){
this.eventHubFunc()
this.floatWindow = window.findWindow("floatWindow")
this.floatWindowWidth = 1080
this.panOption.setDistance(1)
}
onBackPress(){
console.log('返回')
}
build() {
Row() {
Text('X').width(px2vp(140))
.textAlign(TextAlign.Center)
.fontColor(Color.Red).onClick(()= >{
//关闭所依赖的UIAbility
this.context.terminateSelf()
//销毁悬浮窗。当不再需要悬浮窗时,可根据具体实现逻辑,使用destroy对其进行销毁。
this.floatWindow.destroyWindow((err) = > {
if (err.code) {
console.error('Failed to destroy the window. Cause: ' + JSON.stringify(err));
return;
}
console.info('Succeeded in destroying the window.');
});
})
Text(this.idleName)
.width(px2vp(140))
.height('100%')
.fontSize(18)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Gray)
.onClick(()= >{
this.foldStatus = !this.foldStatus
if(this.foldStatus){
this.idleName = "展开"
this.floatWindowWidth = 280
} else {
this.idleName = "收起"
this.floatWindowWidth = 1080
}
})
Divider().vertical(true).color(Color.Red)
if(!this.foldStatus) {
Text(this.message)
.width(px2vp(800))
.fontSize(18)
.fontColor(Color.White)
.padding('12vp')
}
}
.width(px2vp(this.floatWindowWidth))
.height(px2vp(150))
.borderRadius('12vp')
.backgroundColor(Color.Green)
.gesture(
// 绑定PanGesture事件,监听拖拽动作
PanGesture(this.panOption)
.onActionStart((event: GestureEvent) = > {
console.info('Pan start');
})
// 发生拖拽时,获取到触摸点的位置,并将位置信息传递给windowPosition
.onActionUpdate((event: GestureEvent) = > {
console.log(event.offsetX +' ' + event.offsetY)
this.offsetX = this.positionX + event.offsetX
this.offsetY = this.positionY + event.offsetY
this.floatWindow.moveWindowTo(vp2px(this.offsetX), vp2px(this.offsetY));
})
.onActionEnd(() = > {
this.positionX = this.offsetX
this.positionY = this.offsetY
console.info('Pan end');
})
)
}
eventHubFunc() {
this.context.eventHub.on('info', (data) = > {
this.message = data
});
}
}
审核编辑 黄宇
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网
网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。
举报投诉
-
鸿蒙
+关注
关注
57文章
2339浏览量
42803 -
HarmonyOS
+关注
关注
79文章
1973浏览量
30138 -
鸿蒙OS
+关注
关注
0文章
188浏览量
4382
发布评论请先 登录
相关推荐
鸿蒙实战基础(ArkTS)-窗口管理
(CommonConstants.HOME_PAGE_ACTION);
}, CommonConstants.LOGIN_WAIT_TIME);
}
本文主要是对基于窗口能力,实现验证码登录的场景。有关鸿蒙的进阶技能大家可以前往主页查看更多
发表于 01-12 17:51
鸿蒙应用/元服务开发-窗口(Stage模型)设置悬浮窗
一、设置悬浮窗说明
悬浮窗可以在已有的任务基础上,创建一个始终在前台显示的窗口。即使创建悬浮窗的任务退至后台,悬浮窗仍然可以在前台显示。通常
发表于 02-04 14:05
鸿蒙实战项目开发:【短信服务】
数据管理
电话服务
分布式应用开发
通知与窗口管理
多媒体技术
安全技能
任务管理
WebGL
国际化开发
应用测试
DFX面向未来设计
鸿蒙系统移植和裁剪定制
……
《
发表于 03-03 21:29
鸿蒙OS崛起,鸿蒙应用开发工程师成市场新宠
应用的形态也在发生着翻天覆地的变化。作为全球领先的移动操作系统和智能终端制造商,华为公司自主研发的鸿蒙OS应运而生,致力于构建一个统一的分布式操作系统,为各行各业的应用开发带来全新的可能性。
一、
发表于 04-29 17:32
OpenHarmony实战开发-如何实现窗口开发概述
],单位为vp。
系统窗口存在大小限制,宽度范围:[0, 2560],高度范围:[0, 2560],单位为vp。
最后
如果大家觉得这篇内容对学习鸿蒙开发有帮助,我想邀请大家帮我三个小忙:
点赞,转发,有
发表于 05-06 14:29
鸿蒙Flutter实战:07混合开发
# 鸿蒙Flutter实战:混合开发
鸿蒙Flutter混合开发主要有两种形式。
## 1.基于har
将flutter module
发表于 10-23 16:00
鸿蒙OS应用程序开发
这份学习文档主要是带领大家在鸿蒙OS上学习开发一个应用程序,主要知识点如下:1、U-Boot引导文件烧写方式;2、内核镜像烧写方式;3、镜像运行。
发表于 09-11 14:39
鸿蒙 OS 应用开发初体验
的操作系统平台和开发框架。HarmonyOS 的目标是实现跨设备的无缝协同和高性能。
DevEco Studio
对标 Android Studio,开发鸿蒙 OS 应用的 IDE。
发表于 11-02 19:38
鸿蒙OS 2.0手机开发者Beta版发布会在京举办
三个月前,鸿蒙OS 2.0正式在华为开发者大会2020亮相。12月16日,鸿蒙OS 2.0手机开发
华为开发者大会2021鸿蒙os在哪场
华为开发者大会2021将在10月22日-24日举办,地点为东莞松山湖,鸿蒙os 3.0或将与我们见面,那么华为开发者大会2021鸿蒙
评论