Bootstrap

Harmony OS学习笔记——从简单的页面开始

ArkUI(方舟UI框架)介绍

ArkUI能力介绍

ArkUI:提供HarmonyOS应用开发框架,极简开发、精致体验、跨设备/跨平台
ArkUI(方舟UI框架):为HarmoneyOS应用的UI开发提供了完整的基础设施,包括简洁的UI语法,丰富的UI功能(组件、布局、动画以及交互事件),以及实时界面预览工具等,可以支持开发者进行丰富页面的开发。
深度融合语言/编译器/图形,构建关键的应用UI开发底座:

  • 开发模型层:提供了UI开发范式的基础语言规范,并提供了多种状态管理机制,为应用开发者提供一系列接口支持。
  • 引擎层:后端引擎提供了兼容不同开发范式的UI渲染管线,提供多种基础组件、布局计算、动效、交互事件,提供了状态管理和绘制能力。提供了高效地绘制能力,将渲染管线收集的渲染指令绘制到屏幕能力
  • 平台抽象层:提供了针对不同操作系统渲染层的适配,可抹平不同平台的接口差异,实现ArkUI框架多平台一致性。

image.png

声明式UI范式

ArkTS:是UI开发语言,基于TS语言扩展而来,是TS的超集。扩展能力包含各种装饰器、自定义组件、UI描述机制。
声明式开发范式:基于ArkTS的声明式开发范式的方舟框架是一套开发极简、高性能、支持跨设备的UI开发框架,提供了构建HarmoneyOS应用UI所必需的能力

声明式UI和命令式UI对比

image.pngimage.png
命令式开发范式:它由开发者一步一步的告诉计算机执行一系列的操作,得到想要的结果,这一过程中起主要作用的是开发者,计算机只是帮助开发者去执行计算
声明式开发范式:直接告诉计算机它想要的结果,怎么做由预先写好的程序依据一定的算法由计算机自动推算出来
与命令式开发相比的几个优点:

  1. 对于系统使用方: 通过设计声明式的接口,开发者无需关心底层实现,而更多关注上层业务
  2. 对于系统实现方:通过声明式的接口上层使用者接口相对稳定,系统可以不断的迭代优化
  3. 对于整个系统:能够更系统地收集更多信息能够依据策略进行系统行为优化,提升系统效率

声明式UI和类Web式UI对比

image.png
该开发方式更接近于Web前端开发者的使用习惯,便于快速将已有地Web应用改造成方舟UI框架应用,适用于界面较简单地中小型应用或者是卡片。

声明式UI和命令式UI数据更新机制对比

image.png
命令式UI数据更新机制:以Java代码为例,需要手动更新UI数据
声明式则不需要改动整体地业务逻辑,只需抽取数据然后变更数据内容即可实现刷新

总结

基于ArkTS的声明式开发范式的核心就是 数据驱动UI页面更新
image.png
开发者只需要简单的数据变更即可实现UI页面的更新
极简的UI语法,声明式UI开发带来高效开发体验与灵活性
声明式UI是系统选用的新一代开发范式,通过数据驱动UI变化,UI逻辑分离,更直观,更高效。
关键特征:

  1. 类自然语言的UI结构描述
  2. 积木式组件组合
  3. 极简语法-动效

常用组件

Button
Text
Image
线性布局(Row/Column)
创建列表(List)
创建网格(Grid/GridItem)
创建轮播(Swiper)
瀑布流(WaterFlow)
其他常用组件汇总
image.png
image.png

案例:快速入门

页面结构总览

image.png

组件的使用选择

主标题和分标题可以选择Text组件来进行实现
快速入门下的运营推荐位可以使用Swiper,以实现运营推荐位的需求
在本例中可以设置Grid单行显示,则赋能套件部分可以横向滑动。
入门教程部分由List组件实现

组件介绍

Text组件用于在界面上展示一段文本信息
Swiper轮播图组件提供滑动轮播显示的能力
Grid组件为网格容器由行和列分隔的单元格所组成,其中容器内个条目对应一个GridItem组件,如果仅设置行、列、数量与占比之中的一个,网格单元格将按照设置的方向排列,超出Grid显示区域后Grid拥有可滚动的能力。
List组件可以轻松高效地显示结构化可滚动的信息,当列表项达到一定数量时,内容超过屏幕大小可以自动提供滚动能力。

构建快速入门页

准备数据

下面会用到的两个类:

class BannerClass {
  id: string = '';
  imageUrl: ResourceStr = '';
  url: string = ''

  constructor(id: string, imageUrl: ResourceStr, url: string) {
    this.id = id;
    this.imageUrl = imageUrl;
    this.url = url;
  }
}

class ArticleClass {
  id: string = '';
  imageSrc: ResourceStr = '';
  title: string = ''
  brief: string = '';
  webUrl: string = '';

  constructor(id: string, imageSrc: ResourceStr, title: string, brief: string, webUrl: string) {
    this.id = id;
    this.imageSrc = imageSrc;
    this.title = title;
    this.brief = brief;
    this.webUrl = webUrl;
  }
}

将会用到的数据源的数据

bannerList: Array<BannerClass> = [
    new BannerClass('pic0', $r('app.media.banner_pic0'),
      'https://developer.huawei.com/consumer/cn/training/course/video/C101718352529709527'),
    new BannerClass('pic1', $r('app.media.banner_pic1'),
      'https://developer.huawei.com/consumer/cn/'),
    new BannerClass('pic2', $r('app.media.banner_pic2'),
      'https://developer.huawei.com/consumer/cn/deveco-studio/'),
    new BannerClass('pic3', $r('app.media.banner_pic3'),
      'https://developer.huawei.com/consumer/cn/arkts/'),
    new BannerClass('pic4', $r('app.media.banner_pic4'),
      'https://developer.huawei.com/consumer/cn/arkui/'),
    new BannerClass('pic5', $r('app.media.banner_pic5'),
      'https://developer.huawei.com/consumer/cn/sdk')
  ];


tutorialList: Array<ArticleClass> = [
    new ArticleClass('1', $r('app.media.tutorial_pic1'), 'Step1 环境的搭建',
      '本篇教程实现了快速入门——一个用于了解和学习HarmonyOS的应用程序。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('2', $r('app.media.tutorial_pic2'), 'Step2 使用Swiper构建运营广告位',
      'Swiper组件提供滑动轮播显示的能力。Swiper本身是一个容器组件,当设置了多个子组件后,可以对这些子组件进行轮播显示。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('3', $r('app.media.tutorial_pic3'), 'Step3 创建和组合视图',
      'Item定义子组件相关特征。相关组件支持使用条件渲染、循环渲染、懒加载等方式生成子组件。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('4', $r('app.media.tutorial_pic4'), 'Step4 网格和列表组建的使用',
      '网格和列表组件中,当Item达到一定数量,内容超过屏幕大小时,可以自动提供滚动功能,适合用于呈现同类数据类型或数据类型集',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('5', $r('app.media.tutorial_pic5'), 'Step5 应用架构设计基础——MVVM模式',
      'ArkUI采取MVVM = Model + View + ViewModel模式,将数据与视图绑定在一起,更新数据的时候直接更新视图。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('6', $r('app.media.tutorial_pic6'), 'Step6 应用架构设计基础——三层架构',
      '为了更好地适配复杂应用的开发,建议采用三层架构的方式对整个应用的功能进行模块化,实现高内聚、低耦合开发。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('7', $r('app.media.tutorial_pic7'), 'Step6 ArkWeb页面适配',
      'ArkWeb(方舟Web)提供了Web组件,用于在应用程序中显示Web页面内容,为开发者提供页面加载、页面交互、页面调试等能力。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('8', $r('app.media.tutorial_pic8'), 'Step7 数据驱动UI更新', '数据更新的同时会直接驱动UI的改变',
      'xxx'),
    new ArticleClass('9', $r('app.media.tutorial_pic9'), 'Step8 设置组件导航',
      'Navigation组件适用于模块内页面切换,一次开发,多端部署场景。通过组件级路由能力实现更加自然流畅的转场体验,并提供多种标题栏样式来呈现更好的标题和内容联动效果。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('10', $r('app.media.tutorial_pic10'), 'Step9 原生智能:AI语音朗读',
      '文本转语音服务提供将文本信息转换为语音并进行播报的能力,便于用户与设备进行互动,实现实时语音交互,文本播报。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('11', $r('app.media.tutorial_pic11'), 'Step10 原生互联:分布式流转',
      '流转能力打破设备界限,多设备联动,使用户应用程序可分可合、可流转,实现如邮件跨设备编辑、多设备协同健身、多屏游戏等分布式业务。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('12', $r('app.media.tutorial_pic12'), 'Step11 一次开发,多端部署',
      '一套代码工程,一次开发上架,多端按需部署。支撑开发者快速高效的开发支持多种终端设备形态的应用,实现对不同设备兼容的同时,提供跨设备的流转、迁移和协同的分布式体验。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
  ];

tutorialList: Array<ArticleClass> = [
    new ArticleClass('1', $r('app.media.tutorial_pic1'), 'Step1 环境的搭建',
      '本篇教程实现了快速入门——一个用于了解和学习HarmonyOS的应用程序。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('2', $r('app.media.tutorial_pic2'), 'Step2 使用Swiper构建运营广告位',
      'Swiper组件提供滑动轮播显示的能力。Swiper本身是一个容器组件,当设置了多个子组件后,可以对这些子组件进行轮播显示。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('3', $r('app.media.tutorial_pic3'), 'Step3 创建和组合视图',
      'Item定义子组件相关特征。相关组件支持使用条件渲染、循环渲染、懒加载等方式生成子组件。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('4', $r('app.media.tutorial_pic4'), 'Step4 网格和列表组建的使用',
      '网格和列表组件中,当Item达到一定数量,内容超过屏幕大小时,可以自动提供滚动功能,适合用于呈现同类数据类型或数据类型集',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('5', $r('app.media.tutorial_pic5'), 'Step5 应用架构设计基础——MVVM模式',
      'ArkUI采取MVVM = Model + View + ViewModel模式,将数据与视图绑定在一起,更新数据的时候直接更新视图。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('6', $r('app.media.tutorial_pic6'), 'Step6 应用架构设计基础——三层架构',
      '为了更好地适配复杂应用的开发,建议采用三层架构的方式对整个应用的功能进行模块化,实现高内聚、低耦合开发。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('7', $r('app.media.tutorial_pic7'), 'Step6 ArkWeb页面适配',
      'ArkWeb(方舟Web)提供了Web组件,用于在应用程序中显示Web页面内容,为开发者提供页面加载、页面交互、页面调试等能力。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('8', $r('app.media.tutorial_pic8'), 'Step7 数据驱动UI更新', '数据更新的同时会直接驱动UI的改变',
      'xxx'),
    new ArticleClass('9', $r('app.media.tutorial_pic9'), 'Step8 设置组件导航',
      'Navigation组件适用于模块内页面切换,一次开发,多端部署场景。通过组件级路由能力实现更加自然流畅的转场体验,并提供多种标题栏样式来呈现更好的标题和内容联动效果。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('10', $r('app.media.tutorial_pic10'), 'Step9 原生智能:AI语音朗读',
      '文本转语音服务提供将文本信息转换为语音并进行播报的能力,便于用户与设备进行互动,实现实时语音交互,文本播报。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('11', $r('app.media.tutorial_pic11'), 'Step10 原生互联:分布式流转',
      '流转能力打破设备界限,多设备联动,使用户应用程序可分可合、可流转,实现如邮件跨设备编辑、多设备协同健身、多屏游戏等分布式业务。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
    new ArticleClass('12', $r('app.media.tutorial_pic12'), 'Step11 一次开发,多端部署',
      '一套代码工程,一次开发上架,多端按需部署。支撑开发者快速高效的开发支持多种终端设备形态的应用,实现对不同设备兼容的同时,提供跨设备的流转、迁移和协同的分布式体验。',
      'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
];

💡 图片资源可以到到官方给出的案例中去下载源码<HarmonyOS第一课>从简单的页面开始-华为开发者学堂 (huawei.com)

设置快速入门标题

// 设置文本内容
Text('快速入门')
  //分别设置字体、字体大小、字体粗细、文本的行高
  .fontFamily('HarmonyHeiTi-Bold')
  .fontSize(25)
  .fontWeight(FontWeight.Bold)
  .lineHeight(33)
  // 设置文本段落在水平方向的对齐方式
  .textAlign(TextAlign.Start)
  .padding({ left: 16 })
  .margin({ top: 20 })
  .width('100%')

使用循环渲染:ForEach

ForEach简介

通过ForEach从数组中获取数据,并为每个数据项创建相应的组件,可减少重复代码

ForrEach(
  arr:Array,
  itemGenerator:(item:Array,index?:number=>void,
  keyGenerator?:(item:Array,index?:number):string => string
)

ForEach包含的三个参数:
参数1:数据源,为Array类型的数组。
参数2:itemGenerator为子组件生成函数,为数组中的每个元素创建对应的组件。
参数3:keyGenerator为数组项唯一键值生成函数,为数据源arr的每个数组项生成唯一且持久的键值。函数返回值为开发者自定义的键值生成规则

设置轮播图

@Component
struct Banner {
  // 准备轮播的数据
  @State bannerList: Array<BannerClass> = [......];
  // 创建Swiper控制器
  private swiperController: SwiperController = new SwiperController();

  build() {
    Swiper(this.swiperController) {
      // 循环渲染
      ForEach(this.bannerList, (item: BannerClass, index: number) => {
        // 将Image组件作为Item
        Image(item.imageUrl)
          .objectFit(ImageFit.Contain)
          .width('100%')
          .padding({ top: 11, left: 16, right: 16 })
          .borderRadius(16)
      }, (item: BannerClass, index: number) => item.id)
    }
    // 设置自动播放和循环播放
    .autoPlay(true)
    .loop(true)
  }

设置网格

@Component
struct EnablementView {
  // 准备数据
  @State enablementList: Array<ArticleClass> = [...];
  build() {

    Column() {
      Text('赋能套件')
        .fontColor('#182431')
        .fontSize(16)
        .fontWeight(500)
        .fontFamily('HarmonyHeiTi-medium')
        .textAlign(TextAlign.Start)
        .padding({left: 16})
        .margin({bottom: 8.5})
      // 设置网格容器
      Grid() {
        // 对GridItem循环
        ForEach(this.enablementList,(item: ArticleClass)=>{
          GridItem(){
            EnablementItem({enablementItem: item})
          }
        },(item: ArticleClass)=> item.id)
      }
      .rowsTemplate('1fr')
      .columnsGap(8)
      .scrollBar(BarState.Off)
      .height(169)
      .padding({top: 2, left: 16, right: 16})
    }
    .margin({top: 18})
    .alignItems(HorizontalAlign.Start)
  }
}

@Component
struct EnablementItem {
  @Prop enablementItem: ArticleClass;
  build() {
    // 设置Column布局
    Column(){
      // 设置配图
      Image(this.enablementItem.imageSrc)
        .width('100%')
        .objectFit(ImageFit.Cover)
        .height(96)
        .borderRadius({
          topLeft:16,
          topRight:16
        })
      // 设置标题
      Text(this.enablementItem.title)
        .height(19)
        .width('100%')
        .fontSize(14)
        .textAlign(TextAlign.Start)
        .textOverflow({overflow: TextOverflow.Ellipsis})
        .maxLines(1)
        .fontWeight(400)
        .padding({left: 12, right: 12})
        .margin({top: 8})
      // 设置简介
      Text(this.enablementItem.brief)
        .height(32)
        .width('100%')
        .fontSize(12)
        .textAlign(TextAlign.Start)
        .textOverflow({overflow: TextOverflow.Ellipsis})
        .maxLines(2)
        .fontWeight(400)
        .fontColor('rgba(0,0,0,0.6)')
        .padding({left: 12, right: 12})
        .margin({top: 2})
    }
    .width(160)
    .height(169)
    .backgroundColor(Color.White)
  }
}

设置列表

@Component
struct TutorialView {
  @State message: string = "入门教程"
  // 准备数据
  @State tutorialList: Array<ArticleClass> = [......];
  build() {
    Column() {
      Text(this.message)
        .fontSize(16)
        .fontWeight(500)
        .fontFamily('HarmonyHeiTi-medium')
        .textAlign(TextAlign.Start)
        .padding({left: 16})
        .width('100%')
        .margin({bottom: 8.5})
      // 设置列表容器
      List({space: 12}) {
        // 进行数据源循环
        ForEach(this.tutorialList,(item: ArticleClass)=>{
          ListItem() {
            TutorialItem({ tutorialItem: item })
          }
        }, (item: ArticleClass) => item.id)
      }
      .width('auto')
      .height('auto')
    }
  }
}

@Component
struct TutorialItem {
  // 接收来着父组件的数据
  @Prop tutorialItem: ArticleClass;

  build() {
    // 用Row包裹整个视图
    Row() {
      // 设置Column布局
      Column(){
        // 包裹两个Text组件
        Text(this.tutorialItem.title)
          .height(19)
          .width('100%')
          .fontSize(14)
          .textAlign(TextAlign.Start)
          .textOverflow({overflow: TextOverflow.Ellipsis})
          .maxLines(1)
          .fontWeight(400)
          .margin({top: 4})
        Text(this.tutorialItem.brief)
          .height(32)
          .width('100%')
          .fontSize(12)
          .textAlign(TextAlign.Start)
          .textOverflow({overflow: TextOverflow.Ellipsis})
          .maxLines(2)
          .fontWeight(400)
          .fontColor('rgba(0,0,0,0.6)')
          .margin({top: 5})
      }
      .height('100%')
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
      .margin({right: 12})
      // 设置Image组件
      Image(this.tutorialItem.imageSrc)
        .height(64)
        .width(108)
        .objectFit(ImageFit.Cover)
        .borderRadius(16)
    }
    .width('100%')
    .height(88)
    .borderRadius(16)
    .backgroundColor(Color.White)
    .padding(12)
    .alignItems(VerticalAlign.Top)
  }
}



组合完整页面

@Entry
@Component
struct Index {
  @State message: string = '快速入门';

  build() {
    Column() {
      Text(this.message)
        .fontSize(24)
        .fontWeight(700)
        .width('100%')
        .textAlign(TextAlign.Start)
        .padding({ left: 16 })
        .fontFamily('HarmonyHeiTi-Bold')
        .lineHeight(33)
      Scroll() {
        Column() {
          // 运营推荐位
          Banner()
          //赋能套件
          EnablementView()
          //入门教程
          TutorialView()
        }
      }
      .layoutWeight(1)
      .scrollBar(BarState.Off)
      .align(Alignment.TopStart)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F3F5')
  }
}

总结与回顾

  • 使用Text、Image等基本组件
  • 使用ForEach循环渲染组件
  • 使用Swiper组件构建轮播图
  • 使用Grid组件构建网格布局
  • 使用List组件构建列表布局
;