先容
本示例利用position绝对定位实现应用内悬浮窗,而且通过animateTo联合curves动画曲线实现悬浮窗拖拽跟手和放手吸附边沿的弹性动画效果。
效果图预览
利用阐明
按住悬浮窗可以拖拽,松开后悬浮窗主动靠左或靠右,假如悬浮窗超出内容区上下界限,主动吸附在界限位置。
实现思绪
- 悬浮窗组件利用Stack嵌套video布局,利用属性position绝对定位使组件悬浮。源码参考FloatWindowMainPage.ets
- Stack({ alignContent: Alignment.Bottom }) {
- Video({
- src: $rawfile('float_window_video.mp4'),
- controller: this.videoController
- })
- .controls(false)
- .autoPlay(true)
- .loop(true)
- .muted(true)
- .width($r('app.string.float_window_full_size'))
- .onClick(() => {
- this.videoController.requestFullscreen(true);
- })
- .borderRadius($r('app.string.ohos_id_corner_radius_default_l'))
- Text($r('app.string.float_window_live_text'))
- .width($r('app.string.float_window_full_size'))
- .fontSize($r('app.string.ohos_id_text_size_body1'))
- .fontColor($r('app.color.ohos_id_color_background'))
- .textAlign(TextAlign.Center)
- .backgroundColor($r('app.color.ohos_id_color_list_alert'))
- .borderRadius({
- bottomLeft: $r('app.string.ohos_id_corner_radius_default_l'),
- bottomRight: $r('app.string.ohos_id_corner_radius_default_l')
- })
- }
- .clip(true)
- .border({
- width: $r('app.integer.float_window_border_width'),
- color: $r('app.color.ohos_id_color_background')
- })
- .borderRadius($r('app.string.ohos_id_corner_radius_default_l'))
- .width(Constants.FLOAT_WINDOW_WIDTH)
- .height(Constants.FLOAT_WINDOW_HEIGHT)
- .backgroundColor($r('app.color.ohos_id_color_foreground'))
- .position({ x: this.positionX, y: this.positionY })
- .onTouch((event: TouchEvent) => {
- this.onTouchEvent(event);
- })
复制代码
- 在悬浮窗组件的aboutToAppear中获取应用窗口尺寸,利用窗口宽度减去悬浮窗宽度和右边距让悬浮窗初始靠右。源码参考FloatWindowMainPage.ets
- try {
- const properties = windowClass.getWindowProperties();
- // 获取应用窗口宽高
- this.windowRectWidth = px2vp(properties.windowRect.width);
- this.windowRectHeight = px2vp(properties.windowRect.height)
- // 窗口宽度减去悬浮窗宽度和右边距让悬浮窗初始靠右
- this.positionX = this.windowRectWidth - Constants.FLOAT_WINDOW_WIDTH - Constants.PAGE_PADDING;
- } catch (exception) {
- logger.error(TAG, 'Failed to obtain the window properties. Cause: ' + JSON.stringify(exception));
- }
复制代码
- 利用getWindowAvoidArea获取顶部状态栏高度和底部导航栏高度。源码参考FloatWindowMainPage.ets
- try {
- const avoidArea = windowClass.getWindowAvoidArea(type);
- // 获取顶部状态栏高度
- this.topRectHeight = px2vp(avoidArea.topRect.height);
- // 获取底部导航栏高度
- this.bottomRectHeight = px2vp(avoidArea.bottomRect.height);
- } catch (exception) {
- logger.error(TAG, 'Failed to obtain the area. Cause:' + JSON.stringify(exception));
- }
复制代码
- 悬浮窗组件添加onTouchEvent回调,在手指按下时生存触摸点与悬浮窗左上角的偏移量offsetX和offsetY,用于移动时悬浮窗位置的盘算。源码参考FloatWindowMainPage.ets
- case TouchType.Down: {
- this.offsetX = event.touches[0].x;
- this.offsetY = event.touches[0].y;
- break;
- }
复制代码
- 手指移动时,获取触摸点相对于应用窗口左上角的X和Y坐标,通过盘算设置悬浮窗的position坐标实现拖拽,利用默认参数的弹性跟手动画曲线curves.responsiveSpringMotion联合animateTo实现跟手动画效果。源码参考FloatWindowMainPage.ets
- case TouchType.Move: {
- const windowX: number = event.touches[0].windowX;
- const windowY: number = event.touches[0].windowY;
- // TODO:知识点:跟手动画,推荐使用默认参数的弹性跟手动画曲线curves.responsiveSpringMotion。
- animateTo({ curve: curves.responsiveSpringMotion() }, () => {
- this.positionX = windowX - this.offsetX - Constants.PAGE_PADDING;
- this.positionY = windowY - this.offsetY - this.topRectHeight - Constants.PAGE_PADDING; // 减去手指位置到悬浮窗左上角的y轴偏移和设备顶部状态栏高度
- })
- break;
- }
复制代码
- 手指抬起时,通过判定悬浮窗中心在程度方向位于窗口中心的左侧或右侧设置悬浮窗靠左或靠右,假如悬浮窗超出内容区上下界限,则将悬浮窗设置在界限位置,利用curves.springMotion弹性动画曲线实现吸附界限时的弹性动画效果。源码参考FloatWindowMainPage.ets
- case TouchType.Up: {
- // TODO:知识点:通过判断悬浮窗在窗口中的位置,设置悬浮窗贴边,使用curves.springMotion()弹性动画曲线,可以实现阻尼动画效果
- animateTo({ curve: curves.springMotion() }, () => {
- // 判断悬浮窗中心在水平方向是否超过窗口宽度的一半,根据结果设置靠左或靠右
- if (this.positionX > (this.windowRectWidth - Constants.FLOAT_WINDOW_WIDTH) / 2) {
- this.positionX = this.windowRectWidth - Constants.FLOAT_WINDOW_WIDTH - Constants.PAGE_PADDING; // 悬浮窗靠右
- } else {
- this.positionX = Constants.PAGE_PADDING; // 悬浮窗靠左
- }
- // 页面高度
- const pageHeight: number = this.windowRectHeight - this.topRectHeight - this.bottomRectHeight;
- // 判断悬浮窗是否超出内容区上下边界,根据结果将悬浮窗设置在边界位置
- if (this.positionY < Constants.PAGE_PADDING) {
- this.positionY = Constants.PAGE_PADDING;
- } else if (this.positionY > pageHeight - Constants.FLOAT_WINDOW_HEIGHT - Constants.PAGE_PADDING) {
- this.positionY = pageHeight - Constants.FLOAT_WINDOW_HEIGHT - Constants.PAGE_PADDING;
- }
- })
- break;
- }
复制代码 高性能知识点
不涉及
工程布局&模块范例
- floatwindow // har类型
- |---/src/main/ets/common
- | |---Constants.ets // 常量
- |---/src/main/ets/pages
- | |---FloatWindowMainPage.ets // 视图层-悬浮窗首页
复制代码 模块依靠
- 本实例依靠common模块中的日志
工具类logger。
- 本示例依靠动态路由模块来实现页面的动态加载。
参考资料
@ohos.window (窗口)
显式动画 (animateTo)
@ohos.curves (插值盘算)
为了能让各人更好的学习鸿蒙(HarmonyOS NEXT)开辟技能,这边特意整理了《鸿蒙开辟学习手册》(共计890页),渴望对各人有所资助:https://qr21.cn/FV7h05
《鸿蒙开辟学习手册》:
怎样快速入门:https://qr21.cn/FV7h05
开辟根本知识:https://qr21.cn/FV7h05
- 应用根本知识
- 设置文件
- 应用数据管理
- 应用安全管理
- 应用隐私掩护
- 三方应用调用管控机制
- 资源分类与访问
- 学习ArkTS语言
- ……
基于ArkTS 开辟:https://qr21.cn/FV7h05
- Ability开辟
- UI开辟
- 公共变乱与关照
- 窗口管理
- 媒体
- 安全
- 网络与链接
- 电话服务
- 数据管理
- 配景任务(Background Task)管理
- 装备管理
- 装备利用信息统计
- DFX
- 国际化开辟
- 折叠屏系列
- ……
鸿蒙开辟口试真题(含参考答案):https://qr18.cn/F781PH
鸿蒙开辟口试大盘集篇(共计319页):https://qr18.cn/F781PH
1.项目开辟必备口试题
2.性能优化方向
3.架构方向
4.鸿蒙开辟体系底层方向
5.鸿蒙音视频开辟方向
6.鸿蒙车载开辟方向
7.鸿蒙南向开辟方向
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金 |