鸿蒙:使用Stack、ContentTable、Flex等组件和布局实现一个显示界面

news/2024/7/21 12:20:31 标签: 华为, harmonyos

效果展示

38d92b82951c466c9eea6d47f2e94dd3.png

一.概述

跟随官网继续HarmonyOS学习

本篇博文实现一个食物详情页的开发Demo

通过这个开发过程学习如何使用容器组件StackFlex和基本组件ImageText构建用户自定义组件,完成图文并茂的食物介绍

二.构建Stack布局

1.食物名称

创建Stack组件,Text子组件

Stack组件为堆叠组件,可以包含一个或多个子组件,其特点是后一个子组件覆盖前一个子组件。

@Entry
@Component
struct MyComponent {
  build() {
    Stack() {
        Text('Tomato')
            .fontSize(26)
            .fontWeight(500)
            .fontColor(Color.White)
    }
  }
}

Previewer效果: 

34e2f012a9084c7f92a12bcabc48097f.png

2.食物图片

创建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效果: 

5feee84032c84f249fee04095d06f75c.png

 

3.通过资源访问图片

除指定图片路径外,也可以使用引用 媒体资源符$r 引用资源,需要遵循resources文件夹的资源限定词的规则。

右键resources文件夹,点击New>Resource Directory,选择Resource TypeMedia(图片资源)

注:新建的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: 

e3297d18775a46dcae3c8fba449f2caa.png

4.Image组件objectFit属性

示例中imageobjectFit属性设置为ImageFit.Contain
即保持图片长宽比的情况下,使图片完整地显示在边界内。

ImageobjectFit默认属性是ImageFit.Cover
即在保持长宽比的情况下放大或缩小,使其填满整个显示边界。

如果要想Image填满了整个屏幕,原因如下:
    1.Image没有设置宽高。
    2.objectFit属性使用默认值ImageFit.Cover

5.设置Stack布局属性

Stack默认为居中对齐,本示例中修改为底部起始端对齐
设置Stack构造参数alignContentAlignment.BottomStart

AlignmentFontWeight一样,都是框架提供的内置枚举类型

代码:

@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:  

fe28ca1159ef4d879955177cfe18a768.png

6.调整Text组件的外边距margin

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:

8509562c2d0542e6933b8586e782ec8b.png

6.调整组件间的结构,语义化组件名称

创建页面入口组件为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:

3c7653ab992b4f8a9d23fb5b12497f65.png

三.构建Flex布局

Flex:弹性布局

使用Flex弹性布局来构建食物的食物成分表,

弹性布局在本场景的优势在于可以免去多余的宽高计算,通过比例来设置不同单元格的大小,更加灵活。

1.新建ContentTable组件

新建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效果与上一节一样。

2.创建Flex组件展示Tomato两类成分

一类是热量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:

501da9cb100245a6bae2d7d7d108a657.png

3.调整布局,设置各部分占比

分类名占比(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:

9ca5951920fa48509f45d094cc408cbe.png

4.仿照热量分类创建营养成分分类

营养成分部分(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:

38d92b82951c466c9eea6d47f2e94dd3.png

5.优化代码

可以发现,每个成分表中的成分单元其实都是一样的UI结构

2c75982c6a0849be90ac1400fa46ee09.png

可以通过自定义@Builder函数对代码进行精简

使用自定义@Builder抽象出相同的UI结构
@Builder修饰的方法和Componentbuild方法都是为了声明一些UI渲染结构,遵循一样的ArkTS语法。

可以定义一个或者多个 @Builder修饰的方法,但Componentbuild方法必须只有一个

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)
    }
  }
}

ContentTablebuild方法内调用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布局已完成食物的图文展示和营养成分表,构建出了第一个普通视图的食物详情页

下一篇博文将继续跟随官网,开发食物分类列表页,并完成食物分类列表页面和食物详情页面的跳转和数据传递。

 


http://www.niftyadmin.cn/n/5195483.html

相关文章

【顺序表的实现】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 1. 数据结构相关概念 1、什么是数据结构 2、为什么需要数据结构? 2、顺序表 1、顺序表的概念及结构 1.1 线性表 2、顺序表分类 3、动态顺序表的实现 总…

hiredis/examples /example-libevent.c

1 在C++中与Redis交互,你可以使用hiredis,这是一个比较流行的C语言客户端,同时它也适用于C++。以下是如何在C++中使用hiredis与Redis进行交互的简单例子: 首先,你需要安装hiredis。如果你使用的是Linux,可以通过包管理器安装。例如,在Ubuntu上,你可以使用以下命令:su…

LCD1602显示自定义字符

代码&#xff1a; #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27, 16, 2); //根据lcd1602的地址修改0x27. dht DHT; byte degree[8] {B00100, B01010, B10001, B10101, B10101, B01110, B00100,B00100 }; //自定义字符的2进制数据 byte customCh…

【Java 进阶篇】JQuery 事件绑定之事件切换:让页面动起来

欢迎来到这个充满动感的 JQuery 事件绑定之旅&#xff01;在这篇博客中&#xff0c;我们将深入研究 JQuery 中的事件切换&#xff0c;让你的页面焕发出活力和互动。无论你是前端小白还是有一定经验的开发者&#xff0c;相信这篇文章都会对你有所帮助。 走进事件切换的奇妙世界…

【WSL/WSL2-Ubuntu】突破界限:不使用服务器在一台Windows搭建Nginx+FastDFS

打造超级开发环境&#xff1a;Nginx和FastDFS在WSL中的完美结合 前言 随着软件开发领域的快速发展&#xff0c;跨平台的开发环境变得日益重要。Windows Subsystem for Linux&#xff08;WSL&#xff09;和WSL 2为开发者提供了在Windows操作系统上体验Linux环境的便捷途径。本…

小迪安全笔记(3)——基础入门3、基础入门4

文章目录 一、抓包&封包&协议&APP&小程序&PC应用&web应用二、30余种加密编码进制&web&数据库&系统&代理 一、抓包&封包&协议&APP&小程序&PC应用&web应用 APP&小程序&PC抓包HTTP/S数据——Charles、F…

FPGA_边沿检测电路设计

FPGA_边沿检测电路设计 边沿检测原理图波形图分析实现方法方法一&#xff1a;与逻辑实现方法二&#xff1a;或逻辑实现方法三&#xff1a;与逻辑实现 边沿检测原理图 由状态转移表可以看出&#xff0c;其转换条件中需要检测到下降沿以及上升沿&#xff0c;而边沿检测其原理就是…

麒麟系统安装找不到安装源!!!!设置基础软件仓库时出错

记录--华为RH2288 V3服务器安装麒麟系统遇到的问题 1.遇到的问题--“设置基础软件仓库时出错”报错导致无法继续安装 没办法下一步 先说结论&#xff1a;系统bug 该问题在CentOS、Rocky Linux最新版中均存在 解决&#xff1a; &#xff08;一&#xff09;、如果是外网直接配…