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

一.概述
跟随官网继续HarmonyOS学习
本篇博文实现一个食物详情页的开发Demo
通过这个开发过程学习如何使用容器组件Stack、Flex和基本组件Image、Text,构建用户自定义组件,完成图文并茂的食物介绍
二.构建Stack布局
1.食物名称
创建Stack组件,Text子组件
Stack组件为堆叠组件,可以包含一个或多个子组件,其特点是后一个子组件覆盖前一个子组件。
@Entry
@Component
struct MyComponent {build() {Stack() {Text('Tomato').fontSize(26).fontWeight(500).fontColor(Color.White)}}
}
Previewer效果:

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

3.通过资源访问图片
除指定图片路径外,也可以使用引用 媒体资源符$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:

4.Image组件objectFit属性
示例中image的objectFit属性设置为ImageFit.Contain,
即保持图片长宽比的情况下,使图片完整地显示在边界内。
Image的objectFit默认属性是ImageFit.Cover,
即在保持长宽比的情况下放大或缩小,使其填满整个显示边界。
如果要想Image填满了整个屏幕,原因如下:
1.Image没有设置宽高。
2.objectFit属性使用默认值ImageFit.Cover
5.设置Stack布局属性
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:

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:

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:

三.构建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:

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:

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:

5.优化代码
可以发现,每个成分表中的成分单元其实都是一样的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 {@BuilderIngredientItem(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布局已完成食物的图文展示和营养成分表,构建出了第一个普通视图的食物详情页
下一篇博文将继续跟随官网,开发食物分类列表页,并完成食物分类列表页面和食物详情页面的跳转和数据传递。
相关文章:
鸿蒙:使用Stack、ContentTable、Flex等组件和布局实现一个显示界面
效果展示 一.概述 跟随官网继续HarmonyOS学习 本篇博文实现一个食物详情页的开发Demo 通过这个开发过程学习如何使用容器组件Stack、Flex和基本组件Image、Text,构建用户自定义组件,完成图文并茂的食物介绍 二.构建Stack布局 1.食物名称 创建Stack…...
3.生成验证码 + 开发登录、退出功能 + 显示登录信息
目录 1.生成验证码 2.开发登录、退出功能 2.1 开发数据访问层 2.2 开发业务层:实现登录功能...
基于龙格-库塔算法优化概率神经网络PNN的分类预测 - 附代码
基于龙格-库塔算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于龙格-库塔算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于龙格-库塔优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要:针对P…...
2022最新版-李宏毅机器学习深度学习课程-P51 BERT的各种变体
之前讲的是如何进行fine-tune,现在讲解如何进行pre-train,如何得到一个pre train好的模型。 CoVe 其实最早的跟预训练有关的模型,应该是CoVe,是一个基于翻译任务的一个模型,其用encoder的模块做预训练。 但是CoVe需要…...
JavaFX中Application、Stage、Scene和Parent的区别
在JavaFX中,Application、Stage、Scene和Parent是用于构建图形用户界面(GUI)的关键组件,它们各自有不同的作用和责任。以下是它们之间的主要区别: 1、Application(应用程序) Application是Java…...
ubuntu18.04 terminal打不开的解决方法
目录 现象解决 现象 打开terminal时,一直转圈,然后消失,总是打不开terminal. 解决 编辑文件sudo vim /etc/default/locale,修改为 # File generated by update-locale LANG"en_US.UTF-8" LANGUAGE"en_US:en"重启系统,问题解决....
部署Kubernetes Dashboard
Dashboard简介 Dashboard 是基于网页的 Kubernetes 用户界面。 可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中,也可以对容器应用排错,还能管理集群资源。 Dashboard创建 #创建pods kubectl apply -f https://raw.githubusercontent.com/kub…...
Java对List的操作
List<Person>转成map,并自定义key 假设有一List中有如下数据 Person{id100, name张三0100} Person{id101, name张三1100} Person{id102, name张三2100} Person{id103, name张三3100} Person{id104, name张三4100} Person{id105, name张三5100} Person{id106…...
git 将本地已有的一个项目上传到新建的git仓库的方法
将本地已有的一个非git项目上传到新建的git仓库的方法一共有两种 一、 克隆拷贝 第一种方法比较简单,直接用把远程仓库拉到本地,然后再把自己本地的项目拷贝到仓库中去。然后push到远程仓库上去即可。此方法适用于本地项目不是一个git仓库的情况。 具…...
基于Docker的安装和配置Canal
基本介绍 Canal介绍:Canal 是用 Java 开发的基于数据库增量日志解析,提供增量数据订阅&消费的中间件(数据库同步需要阿里的 Otter 中间件,基于 Canal)。 Canal背景:阿里巴巴 B2B 公司,因为…...
去除IDEA中代码的波浪线(黄色警示线)
去除IDEA中代码的波浪线 首先是点击File—>Settings 操作如下图所示: 然后点击Editor—>Inspections—>General—>Duplicated code fragment(去掉勾选)—>Apply—>OK 即可,详情请看下图所示:...
【Qt之QSplashScreen】开场动画使用:进度条加载及设置鼠标指针不转圈
效果 开场动画效果如下: 开场动画 介绍 QSplashScreen小部件提供了一个启动屏幕,可以在应用程序启动期间显示。 启动屏幕是一个小部件,通常在应用程序启动时显示。启动屏幕通常用于启动时间较长的应用程序(例如需要花费时间建立连接的数据…...
WPF Button点击鼠标左键弹出菜单
目录 ContextMenu介绍WPF实现点击鼠标左键弹出菜单如何禁用右键菜单如何修改菜单样式菜单位置设置 本篇博客介绍WPF点击按钮弹出菜单,效果如下: 菜单的位置、央视可以自定义。 实现技巧:不在xaml里菜单,在按钮左键按下的点击事件里…...
http库requests
http库requests requets简介第一个requestsrequests发送基本的HTTP请求requests处理请求参数requests处理响应requests处理Cookiesrequests处理sessionrequests使用代理requests设置请求头requests处理SSL证书验证requests错误处理和异常处理requests连接池requests请求重试...
package.json 依赖版本中的符号含义
依赖包的版本问题 实例说明~1.2.3主版本次要版本补丁版本;1.2.3 < version < 1.3.0;~1.2主版本次要版本;1.2.0 < version < 1.3.0~1主版本;1.0.0 < version < 2.0.0 符号实例版本范围说明1.0.01.0.0锁定1.0.0版本,必须这个版本。^会匹配最新的大…...
Python try except 用法
关键字解释trytry 就是执行代码的部分,但是对这部分代码没有信心就试一试嘛,这就是tryexcept很不幸,试的时候有错误,没事儿,except 帮你来兜底,它会输出错误,并继续执行下去else程序没有错误&am…...
代码随想录二刷 | 链表 | 翻转链表
代码随想录二刷 | 链表 | 翻转链表 题目描述解题思路 & 代码实现双指针法递归法 206.翻转链表 题目描述 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1: 输入:head [1,2,3,4…...
每日一题(LeetCode)----链表--两两交换链表中的节点
每日一题(LeetCode)----链表–两两交换链表中的节点 1.题目([24. 两两交换链表中的节点](https://leetcode.cn/problems/spiral-matrix/)) 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内…...
竞赛选题 身份证识别系统 - 图像识别 深度学习
文章目录 0 前言1 实现方法1.1 原理1.1.1 字符定位1.1.2 字符识别1.1.3 深度学习算法介绍1.1.4 模型选择 2 算法流程3 部分关键代码 4 效果展示5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 毕业设计 图像识别 深度学习 身份证识别…...
什么时候用@MapperScan 注解?
hello,我是小索奇,给大家讲解一下MapperScan注解的用法。 MapperScan 注解是 MyBatis 框架中的一个注解,它的主要作用是扫描指定包路径下的 Mapper 接口,将其注册为 Spring 的 Bean。这样,在使用 MyBatis 进行数据库操作时&#…...
Seraphine终极指南:英雄联盟自动BP与战绩查询系统完整教程
Seraphine终极指南:英雄联盟自动BP与战绩查询系统完整教程 【免费下载链接】Seraphine 英雄联盟战绩查询工具 项目地址: https://gitcode.com/gh_mirrors/se/Seraphine Seraphine是一款基于英雄联盟官方LCU API开发的智能辅助工具,专注于提供自动…...
各编程语言什么能学什么不能学?
编程语言的选择与适用场景编程语言的选择取决于学习目标、项目需求和职业发展方向。不同语言在设计理念、性能、生态系统和应用领域上有显著差异。以下从多个维度分析主流编程语言的适用性。适合学习的编程语言PythonPython以简洁语法和强大生态著称,适合初学者入门…...
LSTM门控机制原理解析与工业级调优实战
1. 为什么今天还要认真学LSTM?——一个被低估但从未过时的序列建模基石你可能已经注意到,现在打开任何一篇讲大模型、讲NLP前沿的文章,标题里不是“Transformer”就是“LLM”,仿佛RNN时代早已尘封进教科书的附录。但上周我帮一家做…...
如何成为全栈Web开发者:HTML/CSS/JavaScript三件套终极入门指南 [特殊字符]
如何成为全栈Web开发者:HTML/CSS/JavaScript三件套终极入门指南 🚀 【免费下载链接】Become-A-Full-Stack-Web-Developer Free resources for learning Full Stack Web Development 项目地址: https://gitcode.com/gh_mirrors/be/Become-A-Full-Stack-…...
DeepVision实时视频流处理:10个高效实现技巧
DeepVision实时视频流处理:10个高效实现技巧 【免费下载链接】DeepVision 在我很多项目中用到的CV算法推理框架应用。 项目地址: https://gitcode.com/gh_mirrors/de/DeepVision DeepVision是一个功能强大的CV算法推理框架应用,专为实时视频流处理…...
Oumuamua-7b-RP真实作品:基于‘贵族女仆’设定的料理指导+生活关怀对话
Oumuamua-7b-RP真实作品:基于贵族女仆设定的料理指导生活关怀对话 1. 项目介绍 Oumuamua-7b-RP 是一款专为日语角色扮演对话设计的Web界面应用,基于Mistral-7B大语言模型架构开发。它能够模拟各种角色进行自然流畅的对话,特别适合创建沉浸式…...
CANN/HCOMM AI CPU通信算子编译部署
编译部署 【免费下载链接】hcomm HCOMM(Huawei Communication)是HCCL的通信基础库,提供通信域以及通信资源的管理能力。 项目地址: https://gitcode.com/cann/hcomm 开发者完成通信算子开发之后,需部署到运行环境上进行功能…...
通过Taotoken CLI工具一键配置团队开发环境中的大模型接入点
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 通过Taotoken CLI工具一键配置团队开发环境中的大模型接入点 当团队开始将大模型能力集成到开发流程中时,一个常见的挑…...
AI算力治理:从技术原理到产业实践,如何管控AI时代的核心资源
1. 算力:AI时代的“新石油”与治理基石在人工智能领域,有一个被反复验证的“苦涩教训”:最根本的进步往往不是来自精巧的算法设计,而是来自简单粗暴地投入更多计算资源。从AlphaGo到GPT-4,每一次AI能力的阶跃式突破&am…...
边缘计算AI安全防护体系:从架构设计到工程实践
1. 项目概述:当边缘计算遇上AI安全最近几年,边缘计算(MEC)和物联网(IoT)这两个词在技术圈里几乎成了标配。大家聊的都是怎么把算力下沉、怎么让设备更智能、怎么实现毫秒级响应。但说实话,我干了…...
