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

效果展示

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

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

 

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

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

相关文章

YOLOv5 学习记录

文章目录 整体概况数据增强与前处理自适应Anchor的计算Lettorbox 架构SiLU激活函数YOLOv5改进点SSPF 模块 正负样本匹配损失函数 整体概况 YOLOv5 是一个基于 Anchor 的单阶段目标检测,其主要分为以下 5 个阶段: 1、输入端:Mosaic 数据增强、…

【LeetCode刷题-树】--100.相同的树

100.相同的树 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

【数据结构】C语言实现队列

目录 前言 1. 队列 1.1 队列的概念 1.2 队列的结构 2. 队列的实现 2.1 队列的定义 2.2 队列的初始化 2.3 入队 2.4 出队 2.5 获取队头元素 2.6 获取队尾元素 2.7 判断空队列 2.8 队列的销毁 3. 队列完整源码 Queue.h Queue.c 🎈个人主页&#xff1a…

获取每个部门中当前员工薪水最高的相关信息

个人网站 首发于公众号小肖学数据分析 描述 有一个员工表dept_emp简况如下: 有一个薪水表salaries简况如下: 获取每个部门中当前员工薪水最高的相关信息,给出dept_no, emp_no以及其对应的salary,按照部门编号dept_no升序排列,以上例子输出…

【NI-DAQmx入门】校准

1.设备定期校准的理由 随着时间的推移电子器件的特性会发生自然漂移,可能会导致测量结果的不准确性。防止出现良品和差品筛选出错的情况满足行业国际标准降低设备出现故障的风险使测量结果更具备参考性 2.查找NI设备的校准间隔。 定期校准会使DAQ设备的精度保持在…

电路的基本原理

文章目录 一、算数逻辑单元(ALU)1、功能2、组成 二、电路基本知识1、逻辑运算2、复合逻辑 三、加法器实现1、一位加法器2、串行加法器3、并行加法器 一、算数逻辑单元(ALU) 1、功能 算术运算:加、减、乘、除等 逻辑运算:与、或、非、异或等 辅助功能&am…

C语言ASCII码排序(1086: ASCII码排序(多实例测试))

题目描述 输入三个字符后,按各字符的ASCII码从小到大的顺序输出这三个字符。 输入:输入数据有多组,每组占一行,由三个字符组成,之间无空格。chu 输出:对于每组输入数据,输出一行,字符…

云课五分钟-0Cg++默认版本和升级-std=c++17

前篇: 云课五分钟-0B快速排序C示例代码-注释和编译指令 视频: 云课五分钟-0Cg默认版本和升级-stdc17 文本: 在Linux系统中,可以通过以下步骤升级g: 打开终端,使用root权限或者sudo权限登录。输入以下命令…

cad提示由于找不到mfc140u.dll,无法继续执行代码怎么修复

在Windows操作系统中,mfc140u.dll是一个重要的文件,很多软件运行都需要它,它属于Microsoft Visual C库的一部分。许多基于C的开发项目都依赖于这个文件,如果在使用过程中出现丢失现象,可能导致相关软件或游戏无法正常运…

Linux管道的工作过程

常用的匿名管道(Anonymous Pipes),也即将多个命令串起来的竖线。管道的创建,需要通过下面这个系统调用。 int pipe(int fd[2]) 我们创建了一个管道 pipe,返回了两个文件描述符,这表示管道的两端&#xff…

丢掉破解版,官方免费了!!!

哈喽!大家好。 几天不见,今天给大家带来一款海外的神器,官方宣布完全免费,但仅限于个人与教育用途,切勿商用噢! 不要看这个软件名字普普通通,实际上内蕴乾坤! 接下来看我给大家炫一…

【【VDMA彩条显示实验之三 之 RGB LCD 彩条显示实验 】】

VDMA彩条显示实验之三 之 RGB LCD 彩条显示实验 VDMA彩条显示实验之三 之 RGB LCD 彩条显示实验 LCD 的构造是在两片平行的玻璃基板当中放置液晶盒,下基板玻璃上设置 TFT(薄膜晶体管),上基板玻璃上设置彩色滤光片,通…

Android修行手册-POI操作中文API文档

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC 👉关于作者 专注于Android/Unity和各种游戏开发技巧,以及各种资源分…

【Linux系统化学习】进程的状态 | 僵尸进程 | 孤儿进程

个人主页点击直达:小白不是程序媛 Linux专栏:Linux系统化学习 目录 操作系统进程的状态 运行状态 阻塞状态 进程阻塞的现象 挂起阻塞状态 Linux进程状态 Linux内核源代码怎么说 R(running状态)运行状态 S(sl…

改进YOLOv8:结合ODConv构成C2f_ODConv:即插即用的动态卷积/可轻量化

🗝️YOLOv8实战宝典--星级指南:从入门到精通,您不可错过的技巧   -- 聚焦于YOLO的 最新版本, 对颈部网络改进、添加局部注意力、增加检测头部,实测涨点 💡 深入浅出YOLOv8:我的专业笔记与技术总结   -- YOLOv8轻松上手, 适用技术小白,文章代码齐全,仅需 …

穿越风波,“长红”的直播电商依然扎根产业和消费者

当消费者将最后一个快递拿进家门,2023年的双11也就落下了帷幕。相较于往年组队、拼单的玩法,如今最受欢迎的双11 流程,或许已经变成点进自己心仪主播、店铺的直播间,翻阅最新的产品清单,从中选择购物目标,在…

【wp】2023第七届HECTF信息安全挑战赛 Web

伪装者 考点:http协议flask的session伪造ssrf读取文件 首先根据题目要求就行伪造HTTP 这里不多说,比较基础 然后下面得到是个登入 页面,我们输入zxk1ing 得到 说什么要白马王子 ,一眼session伪造 看到ey开头感觉是jwt 输入看看 得…

vscode快捷键使用总结

) 1、格式化选中的代码 1、格式化选中的代码 vscode中选中所要格式化的代码: ctrl k,ctrlf 其实可以查到该命令 ctrlshiftp打开命令窗口输入format

手写消息队列(基于RabbitMQ)

一、什么是消息队列? 提到消息队列是否唤醒了你脑海深处的记忆?回看前面的这篇文章:《Java 多线程系列Ⅳ(单例模式阻塞式队列定时器线程池)》,其中我们在介绍阻塞队列时说过,阻塞队列最大的用途…

入股合作协议要不要写章程

公司章程,是注册公司的基本文件,也公司必备的规定公司组织及活动基本规则的书面文件,是公司成立的必不可少的基础,也是公司赖以生存的灵魂。那么,这次要和大家讨论的是有关于入股合作协议要不要写章程的问题了。 入股合…