赞
踩
跟随官网继续HarmonyOS学习
本篇博文实现一个食物详情页的开发Demo
通过这个开发过程学习如何使用容器组件Stack、Flex和基本组件Image、Text,构建用户自定义组件,完成图文并茂的食物介绍
创建Stack组件,Text子组件
Stack组件为堆叠组件,可以包含一个或多个子组件,其特点是后一个子组件覆盖前一个子组件。
- @Entry
- @Component
- struct MyComponent {
- build() {
- Stack() {
- Text('Tomato')
- .fontSize(26)
- .fontWeight(500)
- .fontColor(Color.White)
- }
- }
- }
Previewer效果:
创建Image组件,指定Image组件的url
Text组件要在Image组件上方显示,所以先声明Image组件。
图片资源放在resources下的rawfile文件夹内,引用rawfile下资源时使用$rawfile('filename')的形式,filename为 rawfile 目录下的文件相对路径。
当前$rawfile仅支持Image控件引用图片资源
- @Entry
- @Component
- struct MyComponent {
- build() {
- Stack() {
- Image($rawfile('Tomato.png'))
- Text('Tomato')
- .fontSize(26)
- .fontWeight(500)
- }
- }
- }
Previewer效果:
除指定图片路径外,也可以使用引用 媒体资源符$r 引用资源,需要遵循resources文件夹的资源限定词的规则。
右键resources文件夹,点击New>Resource Directory,选择Resource Type为Media(图片资源)
注:新建的Resource Directory目录只能在base目录下,但是base目录默认是有media文件的
直接把Tomato.png放入media文件夹内,就可以通过$r('app.type.name')的形式引用应用资源了
Tomato.png即为 $r('app.media.Tomato')。
代码:
- @Entry
- @Component
- struct MyComponent {
- build() {
- Stack() {
- Image($r('app.media.Tomato'))
- .objectFit(ImageFit.Contain)
- .height(357)
- //Image($rawfile('Tomato.png'))
-
- Text('Tomato')
- .fontSize(26)
- .fontWeight(500)
- }
- }
- }
Previewer:
示例中image的objectFit属性设置为ImageFit.Contain,
即保持图片长宽比的情况下,使图片完整地显示在边界内。
Image的objectFit默认属性是ImageFit.Cover,
即在保持长宽比的情况下放大或缩小,使其填满整个显示边界。
如果要想Image填满了整个屏幕,原因如下:
1.Image没有设置宽高。
2.objectFit属性使用默认值ImageFit.Cover
Stack默认为居中对齐,本示例中修改为底部起始端对齐,
设置Stack构造参数alignContent为Alignment.BottomStart
Alignment和FontWeight一样,都是框架提供的内置枚举类型
代码:
- @Entry
- @Component
- struct MyComponent {
- build() {
- Stack({ alignContent: Alignment.BottomStart }) {
- Image($r('app.media.Tomato'))
- .objectFit(ImageFit.Contain)
- .height(357)
- //Image($rawfile('Tomato.png'))
-
- Text('Tomato')
- .fontSize(26)
- .fontWeight(500)
- }
- }
- }
Previewer:
margin属性调整组件外边距
(1).margin(Length),即上、右、下、左四个边的外边距都是Length。
(2).margin { top?: Length,
right?: Length,
bottom?: Length,
left?:Length },即分别指定四个边的边距
代码:
- @Entry
- @Component
- struct MyComponent {
- build() {
- Stack({ alignContent: Alignment.BottomStart }) {
- Image($r('app.media.Tomato'))
- .objectFit(ImageFit.Contain)
- .height(357)
- Text('Tomato')
- .fontSize(26)
- .fontWeight(500)
- .margin({left: 26, bottom: 17.4})
- }
- }
- }
Previewer:
创建页面入口组件为FoodDetail,在FoodDetail中创建Column,
设置水平方向上居中对齐 alignItems(HorizontalAlign.Center)
MyComponent组件名改为FoodImageDisplay,为FoodDetail的子组件
Column是子组件竖直排列的容器组件,本质为线性布局,所以只能设置交叉轴方向的对齐
代码:
- @Component
- struct FoodImageDisplay {
- build() {
- Stack({ alignContent: Alignment.BottomStart }) {
- Image($r('app.media.Tomato'))
- .objectFit(ImageFit.Contain)
- Text('Tomato')
- .fontSize(26)
- .fontWeight(500)
- .margin({ left: 26, bottom: 17.4 })
- }
- .height(357)
- }
- }
-
- @Entry
- @Component
- struct FoodDetail {
- build() {
- Column() {
- FoodImageDisplay()
- }
- .alignItems(HorizontalAlign.Center)
- }
- }
Previewer:
Flex:弹性布局
使用Flex弹性布局来构建食物的食物成分表,
弹性布局在本场景的优势在于可以免去多余的宽高计算,通过比例来设置不同单元格的大小,更加灵活。
新建ContentTable组件,使其成为页面入口组件FoodDetail的子组件。
代码:
- @Component
- struct FoodImageDisplay {
- build() {
- Stack({ alignContent: Alignment.BottomStart }) {
- Image($r('app.media.Tomato'))
- .objectFit(ImageFit.Contain)
- .height(357)
- Text('Tomato')
- .fontSize(26)
- .fontWeight(500)
- .margin({ left: 26, bottom: 17.4 })
- }
- }
- }
-
- @Component
- struct ContentTable {
- build() {}
- }
-
- @Entry
- @Component
- struct FoodDetail {
- build() {
- Column() {
- FoodImageDisplay()
- ContentTable()
- }
- .alignItems(HorizontalAlign.Center)
- }
- }
Previewer:
ContentTable子组件是空的,还没填充内容,当前Previewer效果与上一节一样。
一类是热量Calories:卡路里(Calories);
一类是营养成分Nutrition,包含:蛋白质(Protein)、
脂肪(Fat)、
碳水化合物(Carbohydrates)
维生素C(VitaminC)。
先创建热量这一类
新建Flex组件,高度为280,上、右、左内边距为30,
包含三个Text子组件分别代表:类别名(Calories)
含量名称(Calories)
含量数值(17kcal)
Flex组件默认为水平排列方式。
ContentTable代码:
- @Component
- struct ContentTable {
- build() {
- Flex() {
- Text('Calories')
- .fontSize(17.4)
- .fontWeight(FontWeight.Bold)
- Text('Calories')
- .fontSize(17.4)
- Text('17kcal')
- .fontSize(17.4)
- }
- .height(280)
- .padding({ top: 30, right: 30, left: 30 })
- }
- }
Previewer:
分类名占比(layoutWeight)为1,
成分名和成分含量一共占比(layoutWeight)2。
成分名和成分含量位于同一个Flex中,成分名占据所有剩余空间flexGrow(1)。
ContentTable代码:
- @Component
- struct ContentTable {
- build() {
- Flex() {
- Text('Calories')
- .fontSize(17.4)
- .fontWeight(FontWeight.Bold)
- .layoutWeight(1)
- Flex() {
- Text('Calories')
- .fontSize(17.4)
- .flexGrow(1)
- Text('17kcal')
- .fontSize(17.4)
- }
- .layoutWeight(2)
- }
- .height(280)
- .padding({ top: 30, right: 30, left: 30 })
- }
- }
Previewer:
营养成分部分(Nutrition)包含:
蛋白质(Protein)、
脂肪(Fat)、
碳水化合物(Carbohydrates)
维生素C(VitaminC)
设置外层Flex为竖直排列 FlexDirection.Column
在主轴方向(竖直方向)上等距排列 FlexAlign.SpaceBetween
在交叉轴方向(水平轴方向)上首部对齐排列 ItemAlign.Start
ContentTable代码:
- @Component
- struct ContentTable {
- build() {
- Flex({ direction: FlexDirection.Column,
- justifyContent: FlexAlign.SpaceBetween,
- alignItems: ItemAlign.Start }) {
- Flex() {
- Text('Calories')
- .fontSize(17.4)
- .fontWeight(FontWeight.Bold)
- .layoutWeight(1)
- Flex() {
- Text('Calories')
- .fontSize(17.4)
- .flexGrow(1)
- Text('17kcal')
- .fontSize(17.4)
- }
- .layoutWeight(2)
- }
-
- Flex() {
- Text('Nutrition')
- .fontSize(17.4)
- .fontWeight(FontWeight.Bold)
- .layoutWeight(1)
- Flex() {
- Text('Protein')
- .fontSize(17.4)
- .flexGrow(1)
- Text('0.9g')
- .fontSize(17.4)
- }
- .layoutWeight(2)
- }
-
- Flex() {
- Text(' ')
- .fontSize(17.4)
- .fontWeight(FontWeight.Bold)
- .layoutWeight(1)
- Flex() {
- Text('Fat')
- .fontSize(17.4)
- .flexGrow(1)
- Text('0.2g')
- .fontSize(17.4)
- }
- .layoutWeight(2)
- }
-
- Flex() {
- Text(' ')
- .fontSize(17.4)
- .fontWeight(FontWeight.Bold)
- .layoutWeight(1)
- Flex() {
- Text('Carbohydrates')
- .fontSize(17.4)
- .flexGrow(1)
- Text('3.9g')
- .fontSize(17.4)
- }
- .layoutWeight(2)
- }
-
- Flex() {
- Text(' ')
- .fontSize(17.4)
- .fontWeight(FontWeight.Bold)
- .layoutWeight(1)
- Flex() {
- Text('vitaminC')
- .fontSize(17.4)
- .flexGrow(1)
- Text('17.8mg')
- .fontSize(17.4)
- }
- .layoutWeight(2)
- }
- }
- .height(280)
- .padding({ top: 30, right: 30, left: 30 })
- }
- }
Previewer:
可以发现,每个成分表中的成分单元其实都是一样的UI结构
可以通过自定义@Builder函数对代码进行精简
使用自定义@Builder抽象出相同的UI结构
@Builder修饰的方法和Component的build方法都是为了声明一些UI渲染结构,遵循一样的ArkTS语法。
可以定义一个或者多个 @Builder修饰的方法,但Component的build方法必须只有一个
在ContentTable内声明@Builder修饰的IngredientItem方法,用于声明分类名、成分名称和成分含量UI描述。
- @Component
- struct ContentTable {
- @Builder IngredientItem(title:string, name: string, value: string) {
- Flex() {
- Text(title)
- .fontSize(17.4)
- .fontWeight(FontWeight.Bold)
- .layoutWeight(1)
- Flex({ alignItems: ItemAlign.Center }) {
- Text(name)
- .fontSize(17.4)
- .flexGrow(1)
- Text(value)
- .fontSize(17.4)
- }
- .layoutWeight(2)
- }
- }
- }
在ContentTable的build方法内调用IngredientItem接口,
需要用this去调用该Component作用域内的方法,以此来区分全局的方法调用。
- @Component
- struct ContentTable {
- ......
- build() {
- Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Start }) {
- this.IngredientItem('Calories', 'Calories', '17kcal')
- this.IngredientItem('Nutrition', 'Protein', '0.9g')
- this.IngredientItem('', 'Fat', '0.2g')
- this.IngredientItem('', 'Carbohydrates', '3.9g')
- this.IngredientItem('', 'VitaminC', '17.8mg')
- }
- .height(280)
- .padding({ top: 30, right: 30, left: 30 })
- }
- }
ContentTable组件全代码如下:
- @Component
- struct ContentTable {
- @Builder
- IngredientItem(title:string, name: string, value: string) {
- Flex() {
- Text(title)
- .fontSize(17.4)
- .fontWeight(FontWeight.Bold)
- .layoutWeight(1)
- Flex() {
- Text(name)
- .fontSize(17.4)
- .flexGrow(1)
- Text(value)
- .fontSize(17.4)
- }
- .layoutWeight(2)
- }
- }
-
- build() {
- Flex({ direction: FlexDirection.Column,
- justifyContent: FlexAlign.SpaceBetween,
- alignItems: ItemAlign.Start }) {
- this.IngredientItem('Calories', 'Calories', '17kcal')
- this.IngredientItem('Nutrition', 'Protein', '0.9g')
- this.IngredientItem('', 'Fat', '0.2g')
- this.IngredientItem('', 'Carbohydrates', '3.9g')
- this.IngredientItem('', 'VitaminC', '17.8mg')
- }
- .height(280)
- .padding({ top: 30, right: 30, left: 30 })
- }
- }
Previewer:
本小节只是优化代码,实现效果与上一小节相同
Stack布局和Flex布局已完成食物的图文展示和营养成分表,构建出了第一个普通视图的食物详情页
下一篇博文将继续跟随官网,开发食物分类列表页,并完成食物分类列表页面和食物详情页面的跳转和数据传递。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。