HarmonyOS实战开发-如何实现一个简单的电子相册应用开发

news2024/12/1 8:36:21

介绍

本篇Codelab介绍了如何实现一个简单的电子相册应用的开发,主要功能包括:

  1. 实现首页顶部的轮播效果。
  2. 实现页面跳转时共享元素的转场动画效果。
  3. 实现通过手势控制图片的放大、缩小、左右滑动查看细节等效果。

相关概念

  • Swiper:滑块视图容器,提供子组件滑动轮播显示的能力。
  • Grid:网格容器,由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。
  • Navigation:Navigation组件一般作为Page页面的根容器,通过属性设置来展示页面的标题、工具栏、菜单。
  • List:列表包含一系列相同宽度的列表项。适合连续、多行呈现同类数据,例如图片和文本。
  • 组合手势:手势识别组,多种手势组合为复合手势,支持连续识别、并行识别和互斥识别。

环境搭建

软件要求

  • DevEco Studio版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。

硬件要求

  • 开发板类型:润和RK3568开发板。
  • OpenHarmony系统:3.2 Release。

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:

2.搭建烧录环境。

  1. 完成DevEco Device Tool的安装
  2. 完成RK3568开发板的烧录

3.搭建开发环境。

  1. 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
  2. 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”)。
  3. 工程创建完成后,选择使用真机进行调测。

代码结构解读

本篇Codelab只对核心代码进行讲解。

├──entry/src/main/ets                // 代码区
│  ├──common
│  │  ├──constansts
│  │  │  └──Constants.ets            // 常量类
│  │  └──utils
│  │     └──Logger.ets               // Logger公共类
│  ├──entryability
│  │  └──EntryAbility.ts             // 程序入口类
│  ├──pages
│  │  ├──DetailListPage.ets          // 图片详情页面
│  │  ├──DetailPage.ets              // 查看大图页面
│  │  ├──IndexPage.ets               // 电子相册主页面
│  │  └──ListPage.ets                // 图片列表页面
│  └──view
│     └──PhotoItem.ets               // 首页相册Item组件
└──entry/src/main/resources          // 资源文件

构建应用页面

应用首页

应用首页用Column组件来实现纵向布局,从上到下依次是标题组件Text、轮播图Swiper、相册列表Grid。标题和轮播图均设置固定高度,底部相册列表通过layoutWeight属性实现自适应占满剩余空间。

// IndexPage.ets
Column() {
  Row() {
    Text($r('app.string.EntryAbility_label'))
  }

  Swiper(this.swiperController) {
    ForEach(Constants.BANNER_IMG_LIST, (item: Resource) => {
      Row() {
        Image(item)
        ...
      }
    }, (item: Resource, index: number) => JSON.stringify(item) + index)
  }
  ...

  Grid() {
    ForEach(IMG_ARR, (photoArr: Array<Resource>) => {
      GridItem() {
        PhotoItem({ photoArr })
      }
      ...
      .onClick(() => {
        router.pushUrl({
          url: Constants.URL_LIST_PAGE,
          params: { photoArr: JSON.stringify(photoArr) }
        }).catch((error: Error) => {
          Logger.error(Constants.TAG_INDEX_PAGE, JSON.stringify(error));
        });
      })
    }, (item: Array<Resource>, index: number) => JSON.stringify(item) + index)
  }
  ...
  .layoutWeight(1)
}

图片列表页面

图片列表页是网格状展开的图片列表,主要使用Grid组件和GridItem组件,GridItem高度通过aspectRatio属性设置为跟宽度一致。

// ListPage.ets
Navigation() {
  Grid() {
    ForEach(this.photoArr, (img: Resource, index: number) => {
      GridItem() {
        Image(img)
          .onClick(() => {
            this.selectedIndex = index;
            router.pushUrl({
              url: Constants.URL_DETAIL_LIST_PAGE,
              params: {
                photoArr: JSON.stringify(this.photoArr),
              }
            }).catch((error: Error) => {
              Logger.error(Constants.TAG_LIST_PAGE, JSON.stringify(error));
            });
          })
      }
      ...
      .aspectRatio(1)
    }, (item: Resource) => JSON.stringify(item))
  }
  .columnsTemplate(Constants.GRID_COLUMNS_TEMPLATE)
  .layoutWeight(1)
}

图片详情页面

图片详情页由两个横向滚动的List组件完成整体布局,两个组件之间有联动的效果。滚动底部的List,上边展示的图片会随着改变,同样左右滑动上边的图片时,底部List组件也会随之改变。

// DetailListPage.ets
Stack({ alignContent: Alignment.Bottom }) {
  List({ scroller: this.bigScroller, initialIndex: this.selectedIndex }) {
    ForEach(this.photoArr, (img: Resource, index: number) => {
      ListItem() {
        Image(img)
          ...
          .gesture(PinchGesture({ fingers: Constants.DOUBLE_NUMBER })
            .onActionStart(() => this.goDetailPage()))
          .onClick(() => this.goDetailPage())
      }
    }, (item: Resource) => JSON.stringify(item))
  }
  ...
  .onScroll((scrollOffset, scrollState) => {
    if (scrollState === ScrollState.Fling) {
      this.bigScrollAction(scrollTypeEnum.SCROLL);
    }
  })
  .onScrollStop(() => this.bigScrollAction(scrollTypeEnum.STOP))

  List({ scroller: this.smallScroller, space: Constants.LIST_ITEM_SPACE, initialIndex: this.selectedIndex }) {
    ForEach(this.smallPhotoArr, (img: Resource, index: number) => {
      ListItem() {
        this.SmallImgItemBuilder(img, index)
      }
    }, (item: Resource, index: number) => JSON.stringify(item) + index)
  }
  ...
  .listDirection(Axis.Horizontal)
  .onScroll((scrollOffset, scrollState) => {
    if (scrollState === ScrollState.Fling) {
      this.smallScrollAction(scrollTypeEnum.SCROLL);
    }
  })
  .onScrollStop(() => this.smallScrollAction(scrollTypeEnum.STOP))
}

查看大图页面

查看大图页面由一个横向滚动的List组件来实现图片左右滑动时切换图片的功能,和一个Row组件实现图片的缩放和拖动查看细节功能。对图片进行缩放时会从List组件切换成Row组件来实现对单张图片的操作,对单张图片进行滑动操作时,也会由Row组件转换为List组件来实现图片的切换功能。

// DetailPage.ets
Stack() {
  List({ scroller: this.scroller, initialIndex: this.selectedIndex }) {
    ForEach(this.photoArr, (img: Resource) => {
      ListItem() {
        Image(img)
          ...
          .onClick(() => router.back()
          )
      }
      .gesture(PinchGesture({ fingers: Constants.DOUBLE_NUMBER })
        .onActionStart(() => {
          this.resetImg();
          this.isScaling = true;
          this.imgOffSetX = 0;
          this.imgOffSetY = 0;
        })
        .onActionUpdate((event: GestureEvent) => {
          this.imgScale = this.currentScale * event.scale;
        })
        .onActionEnd(() => {
          if (this.imgScale < 1) {
            this.resetImg();
            this.imgOffSetX = 0;
            this.imgOffSetY = 0;
          } else {
            this.currentScale = this.imgScale;
          }
        })
      )
    }, (item: Resource) => JSON.stringify(item))
  }
  ...
  .onScrollStop(() => {
    let currentIndex = Math.round((this.scroller.currentOffset()
      .xOffset + (this.imageWidth / Constants.DOUBLE_NUMBER)) / this.imageWidth);
    this.selectedIndex = currentIndex;
    this.scroller.scrollTo({ xOffset: currentIndex * this.imageWidth, yOffset: 0 });
  })
  .visibility(this.isScaling ? Visibility.Hidden : Visibility.Visible)

  Row() {
    Image(this.photoArr[this.selectedIndex])
    ...
  }
  .visibility(this.isScaling ? Visibility.Visible : Visibility.Hidden)
}

通过手势控制图片

大图浏览界面双指捏合时通过改变Image组件的scale来控制图片的缩放,单手拖动时通过改变Image的偏移量来控制图片的位置,手势操作调用组合手势GestureGroup实现。其中PinchGesture实现双指缩放手势,PanGesture实现单指拖动手势。

// DetailPage.ets 
Row() {
    Image(this.photoArr[this.selectedIndex])
      .position({ x: this.imgOffSetX, y: this.imgOffSetY })
      .scale({ x: this.imgScale, y: this.imgScale })
  }
  .gesture(GestureGroup(GestureMode.Exclusive,
  PinchGesture({ fingers: Constants.DOUBLE_NUMBER })
    .onActionUpdate((event: GestureEvent) => {
      this.imgScale = this.currentScale * event.scale;
    })
    .onActionEnd(() => {
      if (this.imgScale < 1) {
        this.resetImg();
        this.imgOffSetX = 0;
        this.imgOffSetY = 0;
      } else {
        this.currentScale = this.imgScale;
      }
    }),
  PanGesture()
    .onActionStart(() => {
      this.preOffsetX = this.imgOffSetX;
      this.preOffsetY = this.imgOffSetY;
    })
    .onActionUpdate((event: GestureEvent) => {
      this.imgOffSetX = this.preOffsetX + event.offsetX;
      this.imgOffSetY = this.preOffsetY + event.offsetY;
    })
    .onActionEnd(() => this.handlePanEnd())
  ))

总结

您已经完成了本次Codelab的学习,并了解到以下知识点:

  1. 如何实现首页顶部的轮播效果。
  2. 如何实现页面跳转时共享元素的转场动画效果。
  3. 如何通过手势控制图片的放大、缩小、左右滑动查看细节等效果。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→HarmonyOS教学视频

HarmonyOS教学视频:语法ArkTS、TypeScript、ArkUI等…视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取完整版白皮书方式请点击→《鸿蒙生态应用开发白皮书V2.0PDF

在这里插入图片描述

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

  1. 应用开发导读(ArkTS)
  2. .……

在这里插入图片描述


二、HarmonyOS 概念

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

在这里插入图片描述

三、如何快速入门?《鸿蒙基础入门学习指南》

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. .……

在这里插入图片描述


四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. .……

在这里插入图片描述


五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 7.网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. .……

在这里插入图片描述


更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1555593.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Matlab-写入mhd和raw医学图像处理格式文件

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 mhd和raw是什么&#xff1f; MHD&#xff08;MetaImage&#xff09;和RAW&#xff08;Raw Image Data&#xff09;是用于医学图像…

数字化坚鹏:小熊电器面向数字化转型的大数据顶层设计实践培训

小熊电器面向数字化转型的大数据顶层设计实践培训圆满结束 ——努力打造“数据技术营销”三轮驱动的数字化领先企业 小熊电器股份有限公司由李一峰创立于2006年&#xff0c;是一家专业从事创意小家电研发、设计、生产和销售的实业型企业。2019年8月23日正式在深交所挂牌上市。…

XUbuntu22.04之Typora快捷键Ctrl+5不生效问题(二百二十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Discourse 最多允许有几个分类级别

和 DISCUZ 不同&#xff0c;DISCUZ 可以允许分类下面还有分类&#xff0c;再继续分类这种嵌套式分类。 Discourse 最多只允许有 2 个分类。 如果你在已有的分类下再继续分类的话&#xff0c;系统会提示错误&#xff1a; 意思就是子分类不能再分子分类。 Discourse 尽量采取了…

Web前端—(原生JS)歌词滚动效果

歌词滚动效果实现 歌词滚动效果HTML部分CSS部分JS部分解析歌词字符串&#xff0c;得到歌词的对象数组计算在当前情况下&#xff0c;播放器播放到第几秒的情况创建歌词元素设置ul元素的偏移量最后对时间变化的事件进行监听完整JS代码 歌词滚动效果 实现效果如图所示&#xff1a…

PyCharm中配置PyQt5并添加外部工具

Qt Designer、PyUIC和PyRcc是Qt框架下的三个重要工具&#xff0c;总的来说&#xff0c;这三个工具各司其职&#xff0c;相辅相成&#xff0c;能显著提升Qt开发的速度与效率。 Qt Designer&#xff1a;是一个用于创建图形用户界面的工具&#xff0c;可轻松构建复杂的用户界面。…

Photoshoot 2(Java)

Photoshoot 2 题目描述 在一个似曾相识的场景中&#xff0c;Farmer John 正在将他的 N 头奶牛&#xff08;1≤N≤10^5&#xff09;排成一排&#xff08;为了方便将它们按 1⋯1⋯N 编号&#xff09;&#xff0c;以便拍照。 最初&#xff0c;奶牛从左到右按照 a1,a2,⋯,aN 的顺…

Facebook轮播广告是什么?投放过程中有哪些需要注意的吗?

轮播广告是Facebook广告形式中的一种&#xff0c;可以把3—5个广告合并到一个可滚动的广告单元中。轮播广告会出现在新鲜事即News Feed中&#xff0c;是独立站卖家常用的一种广告形式 为什么选择轮播广告&#xff1f; 转化率更高&#xff1a;相较于单图广告&#xff0c;轮播广…

2024 ccfcsp认证打卡 2022 06 01 归一化处理

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt(); // 输入数字的个数int[] a new int[1010]; // 创建一个数组来存储输入的数字double sum 0; // 用于计算所有输入数字的总…

Linux基础命令篇:操作系统服务管理(systemctl service)

Linux基础命令篇&#xff1a;操作系统服务管理&#xff08;systemctl & service&#xff09; service和systemctl是两个用于管理Linux系统服务的命令。它们分别属于SysV init和systemd系统&#xff0c;这两个系统是Linux中用于初始化和管理服务的不同框架。在许多现代Linux…

四、Yocto创建静态IP和VLAN(基于raspiberrypi 4B)

Yocto创建VLAN配置 在车载域控中很多时候需要创建VLAN&#xff0c;本小节记录如何为yocto构建出来的image自动化创建静态IP以及VLAN。 关于各种VLAN的配置参考&#xff1a;VLAN 1. ubuntu系统中使用netplan创建VLAN 正常情况下我们在ubuntu系统中可以通过netplan来自动化创建…

《数据结构学习笔记---第七篇》---栈和队列的OJ练习

1. 括号匹配问题。OJ链接 step1:思路分析 &#xff1a; 1.括号匹配&#xff0c;我们首先考虑用栈实现&#xff0c;我们通过符号栈帧的思想知道&#xff0c;求前中后缀表达式的时候用的就是栈帧&#xff0c;操作数栈和符号栈。 2.根据常见的情况 考虑怎么使用栈&#xff0c;首先…

【Qt】:信号与槽(二)

信号与槽 一.带参数的信号和槽二.信号与槽的多对多连接三.信号与槽的断开四.lamda表达式定义槽函数 一.带参数的信号和槽 Qt的信号和槽也⽀持带有参数,同时也可以⽀持重载.此处我们要求,信号函数的参数列表要和对应连接的槽函数参数列表⼀致.&#xff08;一致指的是类型一致&a…

微信小程序【从入门到精通】——服务器的数据交互

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

LoRA 及其衍生技术总览:An Overview of the LoRA Family

编者按&#xff1a; 对于大语言模型的微调训练来说&#xff0c;传统的全参数微调方法需要处理数百万甚至数十亿级别的参数&#xff0c;计算量和显存占用都非常大。而 LoRA 这种技术方案&#xff0c;通过引入少量的可训练矩阵来调整预训练模型的行为&#xff0c;极大降低了训练所…

EasyRecovery2024中文版数据恢复软件功能全面介绍

EasyRecovery2024是世界著名数据恢复公司 Ontrack 的技术杰作&#xff0c;它是一个威力非常强大的硬盘数据恢复工具。能够帮你恢复丢失的数据以及重建文件系统。 EasyRecovery不会向你的原始驱动器写入任何东东&#xff0c;它主要是在内存中重建文件分区表使数据能够安全地传输…

基于DCT(离散余弦变换)的图像水印算法,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

【LeetCode: 面试题 16.05. 阶乘尾数 + 阶乘】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Android 12.0 mtp模式下连接pc后显示的文件夹禁止删除copy重命名功能实现

1.前言 在12.0的系统rom定制化开发中,usb连接pc端的时候有好几种模式,在做otg连接pc端的时候,改成mtp模式的时候,在pc端可以看到产品设备 的显示的文件夹的内容,对于产品设备里面的文件在pc端禁止做删除重命名拷贝等操作功能的实现 2.mtp模式下连接pc后显示的文件夹禁止删…

深入探讨多线程编程:从0-1为您解释多线程(下)

文章目录 6. 死锁6.1 死锁原因 6.2 避免死锁的方法加锁顺序一致性。超时机制。死锁检测和解除机制。 6. 死锁 6.1 死锁 原因 系统资源的竞争&#xff1a;&#xff08;产生环路&#xff09;当系统中供多个进程共享的资源数量不足以满足进程的需要时&#xff0c;会引起进程对2…