鸿蒙应用开发学习:改进小鱼动画实现按键一直按下时控制小鱼移动和限制小鱼移出屏幕
一、前言
近期我在学习鸿蒙应用开发,跟着B站UP主黑马程序员的视频教程做了一个小鱼动画应用,UP主提供的小鱼动画源代码仅仅实现了移动组件的功能,还存在一些问题,如默认进入页面是竖屏而页面适合横屏显示;真机测试发现手机的状态栏影响到了返回键对按键事件的响应;方向键不能响应一直按着的操作;还有小鱼会移出屏幕范围。
之前已经解决了强制横屏和隐藏手机状态栏,这次则是通过一番研究,实现了按键一直按下时控制小鱼移动和限制小鱼移出屏幕这两个功能。
二、实现方法
1. 一直按下方向键时控制小鱼移动
实现这一功能是在方向键下添加onTouch方法,对按键一直按下事件进行响应。在onTouch方法中还需要判断TouchType.Down事件和TouchType.Up事件。在TouchType.Down事件时,添加animateTo方法,实现按键时一直控制小鱼移动(需要通过setInterval方法设置定时任务让animateTo方法定期执行)。在TouchType.Up事件时,通过clearInterval清除定时任务,小鱼就不会一直移动了。以向右按键为例,改造后的代码如下:
Button('→').backgroundColor('#20101010').onClick(() => { animateTo({ duration: 500 },() => {this.src = $r('app.media.fish')this.fishX += this.speed})}).onTouch((event: TouchEvent) => {if (event.type === TouchType.Down) {this.taskId = setInterval(() => {animateTo({ duration: 500 },() => { this.src = $r('app.media.fish')this.fishX += this.speed})}, 200)}if (event.type === TouchType.Up) {clearInterval(this.taskId)this.taskId = -1}})}.height(240).width(240).justifyContent(FlexAlign.Center).position({ x: 0, y: 120 })
2.限制小鱼移出屏幕
实现了上面的代码后,一直按下方向键,小鱼终于可以一直移动了,但往一个方向一直移动就会移出屏幕,为让小鱼不移出屏幕,还需要对按键操作事件进行判断,检查当前小鱼的位置,只有小鱼在限制的范围内才能执行animateTo方法移动小鱼。
按下向左方向键:对小鱼的X坐标(this.fishX)进行判断。屏幕左侧边界的X值为0,小鱼的大小为40(this.fishSize)。this.fishX是小鱼图片中心点的坐标,则当小鱼接触到屏幕左侧边界时,小鱼的中心点X坐标值为20。本软件中设置的小鱼的移动速度为20(this.speed),因此,我设置的判断条件是当this.fishX >= this.fishSize时,才能执行animateTo方法。当this.fishX == 40时,再移动一次this.fishX就变成了20,此时小鱼图片的左侧边缘正好接触到屏幕左边界。
按下向上方向键:对小鱼的Y坐标(this.fishY)进行判断。屏幕上边界Y值为0,小鱼大小为40。原理和按下向左方向机一样。我设置的判断条件是当this.fishY >= this.fishSize时,才能执行animateTo方法。
对于屏幕下方的边界和屏幕右侧的边界判断需要导入模块 import display from '@ohos.display' ,并在页面的onPageShow方法获取屏幕的尺寸信息。
onPageShow() {// 获取旋转的方向,具体可以查看对应文档let orientation = window.Orientation.LANDSCAPE;// 获取屏幕尺寸信息let promise = display.getAllDisplays()promise.then((data) => {console.info('设备屏幕信息:' + JSON.stringify(data));console.info('testTag', '屏幕宽度px:' + JSON.stringify(data[0].width));console.info('testTag', '屏幕高度px:' + JSON.stringify(data[0].height));this.screenWidth = px2vp(data[0].width)this.screenHeight = px2vp(data[0].height)console.info('testTag', '屏幕宽度vp:' + JSON.stringify(this.screenWidth));console.info('testTag', '屏幕高度vp:' + JSON.stringify(this.screenHeight));}).catch((err) => {console.error('错误信息:' + JSON.stringify(err));})}
按下向右方向机:对小鱼的X坐标(this.fishX)进行判断。屏幕右侧边界的X值为变量this.screenHeight, 则判定语句为 this.fishX <= this.screenHeight - this.fishSize 。只有符合该条件是才执行animateTo方法。
按下向下方向机:对小鱼的Y坐标(this.fishY)进行判断。屏幕右侧边界的Y值为变量this.screenWidth, 则判定语句为 this.fishY <= this.screenWidth - this.fishSize 。只有符合该条件是才执行animateTo方法。
这些判断语句都要添加到方向键的onClick方法和onTouch方法。
三、完整源代码
最后上这个文件的完整源代码:
import router from '@ohos.router';
import window from '@ohos.window'; // 用于强制设为横屏
import display from '@ohos.display'@Entry
@Component
struct Aquarium1Page {onPageShow() {// 获取旋转的方向,具体可以查看对应文档let orientation = window.Orientation.LANDSCAPE;try {// 设置屏幕旋转globalThis.windowClass.setPreferredOrientation(orientation, (err) => {console.log('testTag', `onPageShow函数中setPreferredOrientation方法错误码为${err}`)});} catch (exception) {console.error('设置失败: ' + JSON.stringify(exception));}// 获取屏幕尺寸信息let promise = display.getAllDisplays()promise.then((data) => {console.info('设备屏幕信息:' + JSON.stringify(data));console.info('testTag', '屏幕宽度px:' + JSON.stringify(data[0].width));console.info('testTag', '屏幕高度px:' + JSON.stringify(data[0].height));this.screenWidth = px2vp(data[0].width)this.screenHeight = px2vp(data[0].height)console.info('testTag', '屏幕宽度vp:' + JSON.stringify(this.screenWidth));console.info('testTag', '屏幕高度vp:' + JSON.stringify(this.screenHeight));}).catch((err) => {console.error('错误信息:' + JSON.stringify(err));})}onPageHide() {// 获取旋转的方向,具体可以查看对应文档let orientation = window.Orientation.PORTRAIT;try {// 设置屏幕旋转globalThis.windowClass.setPreferredOrientation(orientation, (err) => {console.log('testTag', `onPageHide函数中setPreferredOrientation方法错误码为${err}`)});} catch (exception) {console.error('设置失败: ' + JSON.stringify(exception));}}// 小鱼的位置@State fishX: number = 200@State fishY: number = 180// 小鱼的大小fishSize: number = 40// 小鱼角度@State angle: number = 0// 小鱼图片@State src: Resource = $r('app.media.fish')// 是否开始游戏@State isBegin: boolean = false// 小鱼的速度speed: number = 20// 用于控制Interval的idtaskId: number = -1// 屏幕尺寸screenWidth: number = px2vp(2000)screenHeight: number = px2vp(1080)build() {Row() {Stack() {Button('返回').position({ x: 20, y: 20 }).backgroundColor('#20101010').onClick(() => {router.back()})if (!this.isBegin) {Button('开始游戏').onClick(() => {animateTo({ duration: 1000 },() => {// 点击后显示小鱼this.isBegin = true})})} else {// 小鱼图片Image(this.src).position({ x: this.fishX - 20, y: this.fishY - 20 }).rotate({ angle: this.angle, centerX: '50%', centerY: '50%' }).width(this.fishSize).height(this.fishSize)//.animation({duration: 500, curve: Curve.Smooth}).transition({type: TransitionType.Insert,opacity: 0,translate: { x: -250 }})}// 操作按钮Row() {// 向左移动,小鱼身体不能超出屏幕范围Button('←').backgroundColor('#20101010').onClick(() => {if (this.fishX >= this.fishSize) {animateTo({ duration: 500 },() => {this.src = $r('app.media.fish_rev')this.fishX -= this.speed})}}).onTouch((event: TouchEvent) => {if (event.type === TouchType.Down) {this.taskId = setInterval(() => {animateTo({ duration: 500 },() => {if (this.fishX >= this.fishSize) {this.src = $r('app.media.fish_rev')this.fishX -= this.speed}})}, 200)}if (event.type === TouchType.Up) {clearInterval(this.taskId)this.taskId = -1}})Column({ space: 40 }) {// 向上和向下移动,小鱼的身体均不能超出屏幕范围Button('↑').backgroundColor('#20101010').onClick(() => {if (this.fishY >= this.fishSize) {animateTo({ duration: 500 },() => {this.fishY -= this.speed})}}).onTouch((event: TouchEvent) => {if (event.type === TouchType.Down) {this.taskId = setInterval(() => {animateTo({ duration: 500 },() => {if (this.fishY >= this.fishSize) {this.fishY -= this.speed}})}, 200)}if (event.type === TouchType.Up) {console.log("testTag", `停止向上,当前fishY为:${this.fishY}`)clearInterval(this.taskId)this.taskId = -1}})Button('↓').backgroundColor('#20101010').onClick(() => {if (this.fishY <= this.screenWidth - this.fishSize) {animateTo({ duration: 500 },() => {this.fishY += this.speed})}}).onTouch((event: TouchEvent) => {if (event.type === TouchType.Down) {this.taskId = setInterval(() => {animateTo({ duration: 500 },() => {if (this.fishY <= this.screenWidth - this.fishSize) {this.fishY += this.speed}})}, 200)}if (event.type === TouchType.Up) {console.log("testTag", `停止向下,当前fishY为:${this.fishY}`)clearInterval(this.taskId)this.taskId = -1}})}Button('→').backgroundColor('#20101010').onClick(() => {if (this.fishX <= this.screenHeight - this.fishSize) {animateTo({ duration: 500 },() => {this.src = $r('app.media.fish')this.fishX += this.speed})}}).onTouch((event: TouchEvent) => {if (event.type === TouchType.Down) {this.taskId = setInterval(() => {animateTo({ duration: 500 },() => {if (this.fishX <= this.screenHeight - this.fishSize) {this.src = $r('app.media.fish')this.fishX += this.speed}})}, 200)}if (event.type === TouchType.Up) {clearInterval(this.taskId)this.taskId = -1}})}.height(240).width(240).justifyContent(FlexAlign.Center).position({ x: 0, y: 120 })}.height('100%').width('100%')}.width('100%').height('100%').backgroundImage($r('app.media.underwater_cartoon')).backgroundImageSize({ height: '100%', width: '100%' })}
}
四、B站视频链接:
鸿蒙应用开发学习:改进小鱼动画实现按键一直按下时控制小鱼移动和限制小鱼移出屏幕-CSDN博客
相关文章:
鸿蒙应用开发学习:改进小鱼动画实现按键一直按下时控制小鱼移动和限制小鱼移出屏幕
一、前言 近期我在学习鸿蒙应用开发,跟着B站UP主黑马程序员的视频教程做了一个小鱼动画应用,UP主提供的小鱼动画源代码仅仅实现了移动组件的功能,还存在一些问题,如默认进入页面是竖屏而页面适合横屏显示;真机测试发现…...
紫光展锐5G扬帆出海 | Blade系列勇当拉美5G先锋
5G对拉丁美洲(简称“拉美”)绝大多数消费者来说还是一个新鲜技术。GSMA报告显示,过去五年,拉美运营商在移动网络方面的资本开支大部分用于部署4G网络。但在5G网络方面拉美也在积极大力投入中,紧跟全球5G发展大潮&#…...
如何设计一个高并发系统?
所谓高并发系统,是指能同时处理大量并发请求,并及时响应,从而保证系统的高性能和高可用 那么我们在设计一个高并发系统时,应该考虑哪些方面呢? 1. 搭建集群 如果你只部署一个应用,只部署一台服务器&…...
基于WebRTC技术的EasyRTC视频云服务系统在线视频客服解决方案
一、需求分析 随着互联网技术的发展,视频客服也成为服务行业的标配体验,基于WebRTC实时通信技术,客服人员与用户可以建立实时双向的视频交互与沟通。借助视频客服功能可以更加直观地了解用户的需求,提高沟通效率,并帮…...
黑马程序员——2022版软件测试——乞丐版——day04
目录: html介绍 前端三大核心html骨架标签注释标签 标题:h1~h6段落:p超链接a图片空格与换行布局标签列表input标签form标签作业 1.html介绍 前端三大核心 html:超文本标记语言,由一套标记标签组成标签: 单标签&…...
uniapp微信小程序投票系统实战 (SpringBoot2+vue3.2+element plus ) -创建图文投票实现
锋哥原创的uniapp微信小程序投票系统实战: uniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )_哔哩哔哩_bilibiliuniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )共计21条视频…...
Spring系列学习九、Spring MVC的使用
Spring MVC的使用 一、MVC设计模式概述二、Spring MVC的工作原理三、HandlerMapping和ViewResolver四、 处理表单、文件上传和异常处理五、前端页面(View)编写1. 引入Thymeleaf模板引擎2.页面相关的示例代码3.后端处理代码编写 六、总结 本章我们将与大家…...
开源内容管理系统Wagtail本地安装运行并结合内网穿透实现公网访问
文章目录 前言1. 安装并运行Wagtail1.1 创建并激活虚拟环境 2. 安装cpolar内网穿透工具3. 实现Wagtail公网访问4. 固定的Wagtail公网地址 前言 Wagtail是一个用Python编写的开源CMS,建立在Django Web框架上。Wagtail 是一个基于 Django 的开源内容管理系统…...
【蓝桥杯/DFS】路径之谜 (Java)
路径之谜小明冒充X星球的骑士,进入了一个奇怪的城堡。 城堡里边什么都没有,只有方形石头铺成的地面。假设城堡地面是 n x n 个方格。【如图1.png】所示。按习俗,骑士要从西北角走到东南角。 可以横向或纵向移动,但不能斜着走&…...
Go语言的内存分配器
1. 内存分配器的历史 Go语言的第一个内存分配器是简单的伙伴分配器。伙伴分配器是一种经典的内存分配器,它将堆内存划分为多个大小相同的块,并使用一种递归的算法来分配和释放内存块。伙伴分配器简单高效,但它存在一个问题:当分配…...
Swift单元测试Quick+Nimble
文章目录 使用QuickNimble1、苹果官方测试框架XCTest的优缺点2、选择QuickNimble的原因:3、QuickNimble使用介绍集成:Quick关键字说明:Nimble中的匹配函数等值判断:使用equal函数是否是同一个对象:使用beIdenticalTo函…...
详解电源动态响应的测试方法及重要性 -纳米软件
电源动态响应测试的重要性 电源动态响应测试是为了检测电源系统在负载变化、输入电压变化情况下的性能表现,包括响应速度、稳定性以及恢复能力等,从而判断电源能否快速、准确地恢复到正常工作状态,为电源的优化设计提供依据。 动态响应能力影…...
计算机网络系统结构-2020期末考试解析
【前言】 不知道为什么计算机网络一门课这么多兄弟,这份看着也像我们的学科,所以也做了。 一. 单选题(每题 2 分,共 20 题,合计 40 分) 1 、当数据由主机 A 发送到主机 B ,不参…...
二叉树的遍历 Java
二叉树的遍历 递归法前序遍历中序遍历后序遍历改进 迭代法前序、后序遍历中序遍历 Java 中 null、NULL、nullptr 区别 public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) { this.val val; }TreeNode(int val, TreeNode left, Tree…...
数据结构之str类
str类 str 是字符串类。str 大概是 Python 中除了int 之外最基本、最常用的数据类型,在Java与其他语言里基本叫做String,其用途广泛,随处可见,但是要记住一点,字符串是不允许修改的。不过,我们仍然可以对其…...
Java电影购票小程序在线选座订票电影
Java电影购票小程序 功能:注册用户可已查看电影场次评价选座订票退票,影院管理员可以排片退款在线卖票和管理演播室等。超级管理员可管理电影排片电影院用户管理等。 演示视频 小程序: https://www.bilibili.com/video/BV11W4y1A7mK/?shar…...
24-1-9 bilibilic++音视频
下午两点面试,面试官迟到了一会,面试官人很好,整体面试经历很不错,但是我人太紧张了,基础知识掌握的深度不够,没有深挖, 是做音视频的底层相关的, 实习要求只要每天打卡够九个小时就…...
备案(三)
首次备案需要多少天 备案初审:您提交初审后,天翼云会在一个工作日内进行审核,并通过短信和邮件形式通知审核结果。 管局审核:各地通管局审核时间不同,一般为1到20个工作日,审核结果将以短信和邮件形式通知…...
Hotspot源码解析-第十九章-ClassLoaderData、符号表、字符串表的初始化
第十九章-ClassLoaderData初始化 讲解本章先从一张图开始 众所周知,Java类的相关信息都是存储在元空间中的,但是是怎么存储的,相信很多读者是不清楚的,这里就不得不涉及到ClassLoaderDataGraph、classLoader、classLoaderData&…...
impala元数据自动刷新
一.操作步骤 进入CM界面 > Hive > 配置 > 搜索 启用数据库中的存储通知(英文界面搜索:Enable Stored Notifications in Database),并且勾选,注意一定要勾选,配置后面的配置不生效。数据库通知的保留时间默认为2天&#…...
用Wireshark抓包分析Powerlink协议:从数据帧看懂主站轮询与从站响应
Wireshark实战:深度解析Powerlink协议的主从站通信机制 工业以太网协议Powerlink凭借其确定性实时通信能力,在自动化控制领域占据重要地位。本文将带您通过Wireshark抓包分析,揭开Powerlink主站轮询与从站响应的核心机制。不同于基础配置教程…...
3分钟学会!用Video-subtitle-extractor轻松提取视频硬字幕,告别手动转录烦恼
3分钟学会!用Video-subtitle-extractor轻松提取视频硬字幕,告别手动转录烦恼 【免费下载链接】video-subtitle-extractor 视频硬字幕提取,生成srt文件。无需申请第三方API,本地实现文本识别。基于深度学习的视频字幕提取框架&…...
SolidWorks 2021建模技巧:用‘拉伸切除’和‘多轮廓草图’高效搞定PCB屏蔽腔设计
SolidWorks 2021建模效率革命:多轮廓草图与拉伸切除在PCB屏蔽设计中的高阶应用 当你在设计一块需要严格电磁屏蔽的PCB时,那些看似简单的腔体结构往往会成为消耗你大量时间的"黑洞"。传统的单轮廓草图拉伸方式不仅操作繁琐,更会在后…...
不删除属性的情况下简化对象属性的方法探讨
是否还有其他方法可以简化从对象中删除特定属性的操作。舍友提出了一个对象属性简化的问题,询问在不删除属性的情况下,如何简化从对象中删除特定属性的操作。02解决方案最初,我曾考虑过不直接删除属性,而是仅保留业务所需的那些。…...
Claude集成Spring Boot全链路实践:从零搭建智能API网关的7步标准化流程
更多请点击: https://intelliparadigm.com 第一章:Claude集成Spring Boot全链路实践:从零搭建智能API网关的7步标准化流程 环境准备与依赖声明 确保 JDK 17、Maven 3.8 和 Spring Boot 3.2.x 基础环境就绪。在 pom.xml 中引入 Claude 官方…...
软件开发加速安全审查滞后:“查找 - 修复”与“防御 - 推迟”难敌新风险!
ZDNET的关键要点持续部署让旧安全模型过时,漏洞积压令开发团队不堪重负,应用程序安全需向代码创建阶段转移。锻炼时在跑步机上反复踏步,付出努力却原地不动,毫无成就感,第二天再重复就更觉沮丧。应用程序安全也类似&am…...
5个场景告诉你:为什么你需要这款免费的窗口分辨率神器
5个场景告诉你:为什么你需要这款免费的窗口分辨率神器 【免费下载链接】SRWE Simple Runtime Window Editor 项目地址: https://gitcode.com/gh_mirrors/sr/SRWE 你是否曾遇到过这些困扰?游戏内分辨率选项有限,无法满足你对极致画质的…...
CodeSandbox终极指南:10个让你开发效率倍增的隐藏功能
CodeSandbox终极指南:10个让你开发效率倍增的隐藏功能 【免费下载链接】codesandbox-client An online IDE for rapid web development 项目地址: https://gitcode.com/gh_mirrors/co/codesandbox-client CodeSandbox是一款强大的在线IDE,专为快速…...
开源机器人夹爪OpenClaw Max:从硬件组装到ROS集成的完整开发指南
1. 项目概述与核心价值 最近在机器人抓取领域,一个名为 minakovai/openclaw-max-guide 的项目在社区里引起了不小的讨论。乍一看这个标题,它像是一个关于“OpenClaw Max”的开源指南或教程。但如果你深入挖掘,会发现它远不止于此。这实际上…...
从芯片拆解看移动通信产业演进:基带、射频与SoC集成趋势
1. 拆解背后的逻辑:为什么我们要关注十年前的芯片趋势?每次看到工程师朋友对着一块新出的手机主板两眼放光,拿着热风枪和撬片跃跃欲试时,我都能理解那种心情。硬件拆解,尤其是对手机、平板这类消费电子产品的深度拆解&…...
