鸿蒙HarmonyOS从零实现类微信app效果——基础界面搭建

最近鸿蒙HarmonyOS开发相关的消息非常的火,传言华为系手机后续将不再支持原生Android应用,所以对于原Android应用开发对应的Harmony版本也被一系列大厂提上了日程。作为一个名义上的移动端开发工程师((⊙o⊙)…,最近写python多过Android),当人不让要来学习一波。本次的学习计划是实现一个类微信app效果,计划将常规的app效果都实现一下,以便后续如果需要写Harmony应用,可以直接上手。

由于我本人有多年的开发经验和多种语言的开发经验,对于JavascriptTypeScript也写得比较多,所以对于TypeScript语法部分将不再过多说明,想快速了解到的同学可以直接查看我的快速入门TypeScript系列文章。同时,文章中设计到的我认为比较重要的知识点,会在文章中作说明。

功能拆分

上面只是一个简单的拆分示例,当我们拿到一个功能的时候,一定要先将页面进行拆分,当我们要实现的功能通过一个个子模块实现后,最终通过子模块的拼接,就可以得到一个完整的功能。

细节实现

今天第一课,先实现整体的界面搭建,最终的实现效果如下图。

当我们点击之后,可以切换上面的tab内容界面。

Harmony提供了很多种方式可以实现底部导航栏,真实项目使用的话,大家可以直接使用系统提供的方式即可。这里我采用的方式是自己用最基础的代码实现,这样也能联系到一些想要学习的功能,开箱即用是好的,但是也很容易让我们错过很多关键知识。

实现BottomNavigationItem

我们这里整体的底部是一个BottomNavigation,他是由四个BottomNavigationItem组合实现。首先定义一个实体类,用于存贮底部导航栏对象信息。

export class BottomNavigationEntity {
  /**
   * 底部导航tab标题
   */
  title: Resource;

  /**
   * 底部导航tab图片
   */
  image: Resource;

  /**
   * 底部导航tab图片,未选中
   */
  unCheckImage: Resource;

  /**
   * tab类型标志位
   */
  tag: number;

  constructor(tag: number, title: Resource, image: Resource, unCheckImage: Resource) {
    this.tag = tag;
    this.title = title;
    this.image = image;
    this.unCheckImage = unCheckImage;
  }
}

接下来的

组成是一个图标+一个文字组合而成,第一反应我们应该行到Column组件。

Column组件中,用于处理组件内容对其方式使用的话flex方式。 alignItems(value: HorizontalAlign): ColumnAttribute; # 水平方向 justifyContent(value: FlexAlign): ColumnAttribute; # 垂直方向 了解了这些之后,接下来看具体BottomNavigationItem的封装代码。

@Preview  # 方便单个view直接预览
@Component  # 标记是一个组件,可供其他组件引用
export default struct BottomNavigationItem {
  private navigationItem: BottomNavigationEntity;
  
  # 这里的Link是用于父组件和子组件进行通信	
  @Link currentIndex: number;

  build() {
    Column({ space: 5 }) {
    # 这里判断如果当前选中的item是当前的这个,则使用选中状态图片
      Image(this.currentIndex === this.navigationItem.tag ? this.navigationItem.image : this.navigationItem.unCheckImage)
        .width(24)
        .height(24)
      Text(this.navigationItem.title)
        .fontSize(14)
        .fontColor(this.currentIndex === this.navigationItem.tag ? Color.Green : 0x333333)
    }
  }
}

代码是不是非常简单。对于@Link你如果现在不太清楚,也没有关系,我最终会专门进行一个讲解。

实现BottomNavigation

@Preview
@Component
export default struct BottomNavigation {
  @Link currentItemIndex: number;

  build() {
    Row({ space: 5 }) {
      //  这里通过对结合遍历,生成BottomNavigationItem进行填充BottomNavigation
      ForEach(navigationViewModel.getNavigationList(), (item: BottomNavigationEntity, index: number) => {
        # 对于这里的$currentItemIndex写法可以先将疑问留着,后续结合Link一并说明
        BottomNavigationItem({ navigationItem: item, currentIndex: $currentItemIndex })
          .onClick(() => {
          	#  点击后更新选中的item,以实现刷新界面的效果
            this.currentItemIndex = index
          })
      })
    }
    .width('100%')
    .height(65)
    .padding({
      top: 5,
      bottom: 5
    })
    .justifyContent(FlexAlign.SpaceAround)
    .backgroundColor(0xF3EEEA)
  }
}

实现WechatMainFrame

整体的界面组合使用RelativeContainer进行组合,将BottomNavigation固定于屏幕的底部,内容区域底部在BottomNavigation之上,顶部和屏幕顶部对其,使其填充满BottomNavigation之上的部分。内容区域使用Stack将所有的内容层叠展示,切换到哪个展示,则使用visibility方法设置该页面展示即可。

@Entry
@Component
struct WechatMainFrame {
  @State currentCheckIndex: number = 0;

  build() {
    RelativeContainer() {
      BottomNavigation({ currentItemIndex: $currentCheckIndex })
        .alignRules({
          bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
          left: { anchor: "__container__", align: HorizontalAlign.Start }
        })
        .id("bottomNavigation")

      Stack() {
        HomeFragment().visibility(this.currentCheckIndex == 0 ? Visibility.Visible : Visibility.Hidden)
        ContactFragment().visibility(this.currentCheckIndex == 1 ? Visibility.Visible : Visibility.Hidden)
        DiscoverFragment().visibility(this.currentCheckIndex == 2 ? Visibility.Visible : Visibility.Hidden)
        MeFragment().visibility(this.currentCheckIndex == 3 ? Visibility.Visible : Visibility.Hidden)
      }
      .width('100%')
      .height('100%')
      .alignRules({
        left: { anchor: "__container__", align: HorizontalAlign.Start },
        right: { anchor: "__container__", align: HorizontalAlign.End },
        bottom: { anchor: "bottomNavigation", align: VerticalAlign.Top },
        top: { anchor: "__container__", align: VerticalAlign.Top }
      })
      .id("contentPanel")
    }
    .width('100%').height('100%')
  }
}

入口页面EntryAbility

export default class EntryAbility extends UIAbility {
  ...
  onWindowStageCreate(windowStage: window.WindowStage) {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    windowStage.loadContent('pages/WechatMainFrame', (err, data) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
    });
  }
  ...

至此整个页面的框架结构完成了。

关于@Link相关的说明

关于更详细的内容,可以看官方文章说明。

我们对于视图更新,可以使用@State 标记变量,但是@State不能进行跨文件使用。这个时候@Link的实现就弥补了@State的不足。使用@Link的话。子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。

  • @Link装饰的变量与其父组件中的数据源共享相同的值。
  • @Link装饰器不能在@Entry装饰的自定义组件中使用。
  • @Link子组件从父组件初始化@State的语法为Comp({ aLink: this.aState })。同样Comp({aLink: $aState})也支持。

下面我们回到上面的代码中。结合代码进行分析。 当我们在BottomNavigation.onClick(() => { this.currentItemIndex = index })在点击之后,会更改@Link currentItemIndex: number;触发界面ui的更改。而BottomNavigationItem({ navigationItem: item, currentIndex: $currentItemIndex })中,我们需要把选中的item的index值传递给BottomNavigationItem本身。而作为传递的值,则需要使用\$标记。这样点击之后会将BottomNavigationItem的值也触发更改,以达到更改布局效果。BottomNavigationItem\的判断也会根据这个值变化而变化。

点击之后,除了对BottomNavigation的状态更新之外,还需要对内容区域进行判断展示不同的界面。因此BottomNavigation@Link currentItemIndex: number;又要和WechatMainFrame@State currentCheckIndex: number = 0;进行双向绑定BottomNavigation({ currentItemIndex: $currentCheckIndex })。最终当我们点击BottomNavigationonclick的时候,就会向上和WechatMainFrame双向绑定更改内容区域,也会和BottomNavigationItem双向绑定更改底部导航展示。

由于我自己也是在边学边实现功能逻辑,既要写文章,还要实现代码功能,所以更新大体上控制在2~3天更新一篇,文章中尽可能会将我觉得比较重要的知识点拎出来说明。对于看完还不太清楚的,可以私信或者查阅其他文章了解,知识的获取不应该被局限,希望我的文章给你带来帮助。

为了能让大家更好的学习鸿蒙 (Harmony OS) 开发技术,这边特意整理了《鸿蒙 (Harmony OS)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

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

入门必看:https://qr21.cn/FV7h05

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

HarmonyOS 概念:https://qr21.cn/FV7h05

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

如何快速入门:https://qr21.cn/FV7h05

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

开发基础知识:https://qr21.cn/FV7h05

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

基于ArkTS 开发:https://qr21.cn/FV7h05

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

应急响应-挖矿病毒处理

应急响应-挖矿病毒处理 使用top​命令实时监控占用CPU资源的是哪个进程,结果可以看到是2725这个进程。 ​​ 再使用netstat -anltp命令查看网络连接状态,定位到对应的PID号后,就拿到了远程地址 ​​ 拿到远程IP,结果是VPN入口…

JVM 运行时内存(三)

Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代。 1. 新生代 是用来存放新生的对象。一般占据堆的 1/3 空间。由于频繁创建对象,所以新生代会频繁触发MinorGC 进行垃圾回收。新生代又分为 Eden 区、ServivorFrom、…

人工智能_机器学习059_非线性核函数_poly核函数_rbf核函数_以及linear核函数效果对比---人工智能工作笔记0099

人工智能_机器学习059_非线性核函数介绍---人工智能工作笔记0099 那么我们应该如何调整这个SVC的参数,也就是我们应该使用哪种核函数,比较合适呢?这取决于我们的数据,适合使用哪个核函数,正好我们有 提供的score = accuracy_score(y_test,y_pred) 这样的评分函数,我们可以根据…

B2B公司如何寻找意向客户的联系方式?

在B2B公司的营销过程中,少不了寻找意向客户的阶段,这也是销售过程中非常重要的一步。 很多新人都是拿到客户联系方式,就直接打电话拜访,俗话说不打没有准备的仗,因此在拜访客户之前就应该做好功课,充分了解…

AI医疗交流平台【Docola】申请823万美元纳斯达克IPO上市

来源:猛兽财经 作者:猛兽财经 猛兽财经获悉,总部位于美国的AI医疗交流平台Docola近期已向美国证券交易委员会(SEC)提交招股书,申请在纳斯达克IPO上市,股票代码为 (DOCO) ,Docola计划…

【五分钟】熟练使用numpy的histogram函数(干货!!!)

histogram函数重要参数详解 def histogram(a, bins10, rangeNone, normedNone, weightsNone, densityNone):...位置参数a: The histogram is computed over the flattened array.(源码对参数a的解释) 从源码对参数a的解释来看,参…

从0开始使用Maven

文章目录 一.Maven的介绍即相关概念1.为什么使用Maven/Maven的作用2.Maven的坐标 二.Maven的安装三.IDEA编译器配置Maven环境1.在IDEA的单个工程中配置Maven环境2.方式2:配置Maven全局参数 四.IDEA编译器创建Maven项目五.IDEA中的Maven项目结构六.IDEA编译器导入Mav…

设计模式之代理模式(1)

目录 概述定义应用场景主要角色类图 详述基本代码应用实例符合的设计原则 总结 概述 定义 代理模式是一种结构型设计模式,它允许通过一个代理对象来控制对原始对象的访问。代理对象可以在不改变原始对象的情况下,增加一些额外的功能,例如权限…

差分基准站

差分基准站,又称参考接收机,是一种固定式卫星接收机,用于提高卫星定位精度。 差分基准站的作用是提供已知位置和准确的位置信号,以纠正其他移动定位终端接收器接收到的卫星信号中的误差。 卫星定位信号会受到多种因素的影响&#…

selenium自动化测试:xpath八种定位方式!

01、前言 如果可以的话,请先关注(专栏和账号),然后点赞和收藏,最后学习和进步。你的支持是我继续写下去的最大动力,个人定当倾囊而送,不负众望。谢谢!!! 1.…

【蓝桥杯省赛真题49】Scratch小狗避障 蓝桥杯scratch图形化编程 中小学生蓝桥杯省赛真题讲解

目录 scratch小狗避障 一、题目要求 编程实现 二、案例分析 1、角色分析

JDK安装太麻烦?一篇文章搞定

JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVMJava系统类库)和JAVA工具。 JDK包含的基本组件包括: javac – 编译器&#xf…

从零开始入门Zapier:与ChatGPT双剑合璧,手把手教程让你进入AI与自动化新纪元

coments 1. 1. 打开Zapier的官方界面 登录之后,会出现一个调查表,可以根据自己的情况进行选择。 第一次注册成功,会送你14天的免费体验

SAP ABAP ALV创建动态树形菜单

创建动态树形菜单——ALV 创建的合同越多,使用树形菜单能比较直观的地显示,而且展开下阶也能明确的知识相关的信息,比如合同中的出口成品有哪些。 设计要点: 第一、 Node_key一定要区分,不能重复,否则出错…

C语言--每日选择题--Day35

第一题 1. 有如下定义:(x y) % 2 (int) a / (int) b 的值是() int x 3; int y 2;float a 2.5; float b 3.5; A:0 B:2 C:1.5 D:1 答案及解析 D 本题是考查强制类型转换和操作符优先级 操作…

JavaWeb 前端工程化

前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块化、组件化、规范化、自动化的问题,其主要目的为了提高效率和降低成本。 前端工程化实现技术栈 前端工程化实现的技术栈有很多,我们采用ES6nodejsnpmViteVUE3routerpiniaaxiosElement-plus组合来实现 ECMAScri…

由11月27日滴滴崩溃到近两个月国内互联网产品接二连三崩溃引发的感想

文章目录 知乎文分析微信聊天截图微信公众号 滴滴技术 发文k8s 官方文档滴滴官方微博账号 近两个月国内互联网产品“崩溃”事件2023-10-23 语雀崩溃2023-11-12 阿里云崩溃2023-11-27 滴滴崩溃2023-12-03 腾讯视频崩溃总结 我的感想 知乎文分析 最近连续加班,打车较…

简单的界面与数据分离的架构

草图绘制于2021年2月19日 当时用到了:qt的子项目、delegate、view和widget的关系,有感而写的小备忘,2022年底考的软件设计师里面的设计模式虽然可能早已包含,但自己也得有自己啊,要把自己哪怕不成熟的东西也记录下来&…

操作系统概述及发展史、Linux内核、发行版及应用领域

一、 操作系统(Operation System,OS) 裸机:没有安装操作系统的计算机 如果想在 裸机 上运行自己所编写的程序,就必须用机器语言书写程序如果计算机上安装了操作系统,就可以在操作系统上安装支持的高级语言…

【算法刷题】Day12

文章目录 1004. 最大连续1的个数 III题干:算法原理:1、暴力枚举 计数器2、利用滑动窗口 代码: 746. 使用最小花费爬楼梯题干:算法原理:解法一:1.1 状态表示1.2 状态转移方程1.3 初始化1.4 填表顺序1.5 返回…