接下来我们继续学习如何用ArkUI来开发一个购物应用程序(下半部分)
底部组件是由一个横向的图片列表组成,iconPath是底部初始状态下的3张图片路径数组。遍历iconPath数组,使用Image组件设置图片路径并添加到List中,给每个Image组件设置点击事件,点击更换底部3张图片。在HomeBottom中,iconPath使用的是@State修饰,当iconPath数组内容变化时,页面组件有使用到的地方都会随之发生变化。
在MyPage.ets文件中新建MyTransList组件和MoreGrid组件,MyTransList组件代码如下:
@Component
struct MyTransList {private imageItems: ImageItem[] = getTrans()build() {Column() {Text('My Transaction').fontSize(20).margin({ left: 10 }).width('100%').height(30)Row() {List() {ForEach(this.imageItems, item => {ListItem() {DataItem({ imageItem: item })}}, item => item.id.toString())}.height(70).width('100%').align(Alignment.Top).margin({ top: 5}).listDirection(Axis.Horizontal)}}.height(120)}
}
MoreGrid组件代码如下:
@Component
struct MoreGrid {private gridRowTemplate: string = ''private imageItems: ImageItem[] = getMore()private heightValue: numberaboutToAppear() {var rows = Math.round(this.imageItems.length / 3);this.gridRowTemplate = '1fr '.repeat(rows);this.heightValue = rows * 75 ;}build() {Column() {Text('More').fontSize(20).margin({ left: 10 }).width('100%').height(30)Scroll() {Grid() {ForEach(this.imageItems, (item: ImageItem) => {GridItem() {DataItem({ imageItem: item })}}, (item: ImageItem) => item.id.toString())}.rowsTemplate(this.gridRowTemplate).columnsTemplate('1fr 1fr 1fr').columnsGap(8).rowsGap(8).height(this.heightValue)}.padding({ left: 16, right: 16 })}.height(400)}
}
在MyTransList和MoreGrid组件中都包含子组件DataItem,为避免的重复代码,可以把多次要用到的结构体组件化,这里的结构体就是图片加上文本的上下结构体,DataItem组件内容如下:
@Component
struct MenuItem {private menu: Menubuild() {Column() {Text(this.menu.title).fontSize(15)Text(this.menu.num+'').fontSize(13)}.height(50).width(80).margin({left: 8,right:8}).alignItems(HorizontalAlign.Start).backgroundColor(Color.White)}
}
实现购物车页签
主界面购物车页签主要由下面三部分组成:
- 顶部的Text组件。
- 中间的List组件,其中List组件的item是一个水平的布局内包含一个toggle组件,一个Image组件和一个垂直布局,其item中的垂直布局是由2个Text组件组成。
- 底部一个水平布局包含两个Text组件。
构建一个购物车页签,给商品列表的每个商品设置一个单选框,可以选中与取消选中,底部Total值也会随之增加或减少,点击Check Out时会触发弹窗。下面我们来完成ShoppingCart页签。
在pages目录下面新建一个Page ,命名为ShoppingCart.。在ShoppingCart.ets文件中添加入口组件(ShoppingCart),并导入需要使用到的数据实体类、方法和组件。ShoppingCart组件代码如下:
@Entry
@Component
export struct ShoppingCart {@Provide totalPrice : number =0private goodsItems: GoodsData[] = initializeOnStartup()build() {Column() {Column() {Text('ShoppingCart').fontColor(Color.Black).fontSize(25).margin({ left: 60,right:60 }).align(Alignment.Center)}.backgroundColor('#FF00BFFF').width('100%').height(30)ShopCartList({ goodsItems: this.goodsItems });ShopCartBottom()}.alignItems(HorizontalAlign.Start)}
}
新建ShopCartList组件用于存放购物车商品列表,ShopCartList组件代码如下
@Component
struct ShopCartList {private goodsItems: GoodsData[]build() {Column() {List() {ForEach(this.goodsItems, item => {ListItem() {ShopCartListItem({ goodsItem: item })}}, item => item.id.toString())}.height('100%').width('100%').align(Alignment.Top).margin({top: 5})}.height(570)}
}
ShopCartListItem组件代码如下:
@Component
struct ShopCartListItem {@Consume totalPrice: numberprivate goodsItem: GoodsDatabuild() {Row() {Toggle({type: ToggleType.Checkbox}).width(10).height(10).onChange((isOn:boolean) => {if(isOn){this.totalPrice +=parseInt(this.goodsItem.price+'',0)}else{this.totalPrice -= parseInt(this.goodsItem.price+'',0)}})Image(this.goodsItem.imgSrc).objectFit(ImageFit.ScaleDown).height(100).width(100).renderMode(ImageRenderMode.Original)Column() {Text(this.goodsItem.title).fontSize(14)Text('¥' + this.goodsItem.price).fontSize(14).fontColor(Color.Red)}}.height(100).width(180).margin({left: 20}).alignItems(VerticalAlign.Center).backgroundColor(Color.White)}
}
在ShopCartListItem中使用Toggle的单选框类型来实现每个item的选择和取消选择,在Toggle的onChage事件中来改变totalPrice的数值。
新建ShopCartBottom组件,ShopCartBottom组件代码如下:
@Component
struct ShopCartBottom {@Consume totalPrice: numberbuild() {Row() {Text('Total: ¥'+this.totalPrice).fontColor(Color.Red).fontSize(18).margin({left:20}).width(150)Text('Check Out').fontColor(Color.Black).fontSize(18).margin({right:20,left:100}).onClick(()=>{prompt.showToast({message: 'Checking Out',duration: 10,bottom:100})})}.height(30).width('100%').backgroundColor('#FF7FFFD4').alignItems(VerticalAlign.Bottom)}
}
实现我的页签
我的页签主要由下面四部分组成:
- 顶部的水平布局。
- 顶部下面的文本加数字的水平List。
- My Transactio模块,图片加文本的水平List。
- More模块,图片加文本的Grid。
构建主页我的页签,主要可以划分成下面几步:
- 在pages目录下面新建一个Page 命名为MyPage
- 在MyPage.ets文件中添加入口组件(MyInfo)
MyInfo组件内容如下:
import {getMenu,getTrans,getMore} from '../model/GoodsDataModels'
import {Menu, ImageItem} from '../model/Menu'
@Entry
@Component
export struct MyInfo {build() {Column() {Row(){Image($rawfile('nav/icon-user.png')).margin({left:20}).objectFit(ImageFit.Cover).height(50).width(50).renderMode(ImageRenderMode.Original).margin({left:40,right:40})Column(){Text('John Doe').fontSize(15)Text('Member Name : John Doe >')}.height(60).margin({left:40,top:10}).alignItems(HorizontalAlign.Start)}TopList()MyTransList()MoreGrid()}.alignItems(HorizontalAlign.Start).width('100%').flexGrow(1)}
}
入口组件中还包含TopList,MyTransList和MoreGrid三个子组件。代码如下:
@Component
struct TopList {private menus: Menu[] = getMenu()build() {Row() {List() {ForEach(this.menus, item => {ListItem() {MenuItem({ menu: item })}}, item => item.id.toString())}.height('100%').width('100%').margin({top: 5}).edgeEffect(EdgeEffect.None).listDirection(Axis.Horizontal)}.width('100%').height(50)}
}
@Component
struct MyTransList {private imageItems: ImageItem[] = getTrans()build() {Column() {Text('My Transaction').fontSize(20).margin({ left: 10 }).width('100%').height(30)Row() {List() {ForEach(this.imageItems, item => {ListItem() {DataItem({ imageItem: item })}}, item => item.id.toString())}.height(70).width('100%').align(Alignment.Top).margin({ top: 5}).listDirection(Axis.Horizontal)}}.height(120)}
}
@Component
struct MoreGrid {private gridRowTemplate: string = ''private imageItems: ImageItem[] = getMore()private heightValue: numberaboutToAppear() {var rows = Math.round(this.imageItems.length / 3);this.gridRowTemplate = '1fr '.repeat(rows);this.heightValue = rows * 75 ;}build() {Column() {Text('More').fontSize(20).margin({ left: 10 }).width('100%').height(30)Scroll() {Grid() {ForEach(this.imageItems, (item: ImageItem) => {GridItem() {DataItem({ imageItem: item })}}, (item: ImageItem) => item.id.toString())}.rowsTemplate(this.gridRowTemplate).columnsTemplate('1fr 1fr 1fr').columnsGap(8).rowsGap(8).height(this.heightValue)}.padding({ left: 16, right: 16 })}.height(400)}
}
实现商品详情页面
商品详情页面主要由下面五部分组成:
- 顶部的返回栏。
- Swiper组件。
- 中间多个Text组件组成的布局。
- 参数列表。
- 底部的Buy。
把上面每一部分都封装成一个组件,然后再放到入口组件内,当点击顶部返回图标时返回到主页面的商品列表页签,点击底部Buy时,会触发进度条弹窗。
在pages目录下面新建一个Page, 命名为ShoppingDetail。在ShoppingDetail.ets文件中创建入口组件,组件内容如下:
import router from '@system.router';
import {ArsData} from '../model/ArsData'
import {getArs,getDetailImages} from '../model/GoodsDataModels'
import prompt from '@system.prompt';
@Entry
@Component
struct ShoppingDetail {private arsItems: ArsData[] = getArs()private detailImages: string[] = getDetailImages()build() {Column() {DetailTop()Scroll() {Column() {SwiperTop()DetailText()DetailArsList({ arsItems: this.arsItems })Image($rawfile('computer/computer1.png')).height(220).width('100%').margin({top:30})Image($rawfile('computer/computer2.png')).height(220).width('100%').margin({top:30})Image($rawfile('computer/computer3.png')).height(220).width('100%').margin({top:30})Image($rawfile('computer/computer4.png')).height(220).width('100%').margin({top:30})Image($rawfile('computer/computer5.png')).height(220).width('100%').margin({top:30})Image($rawfile('computer/computer6.png')).height(220).width('100%').margin({top:30})}.width('100%').flexGrow(1)}.scrollable(ScrollDirection.Vertical)DetailBottom()}.height(630)}
}
顶部DetailTop组件代码如下:
@Component
struct DetailTop{build(){Column(){Row(){Image($rawfile('detail/icon-return.png')).height(20).width(20).margin({ left: 20, right: 250 }).onClick(() => {router.push({uri: "pages/HomePage"})})}.width('100%').height(25).backgroundColor('#FF87CEEB')}.width('100%').height(30)}
}
SwiperTop组件代码如下:
@Component
struct SwiperTop{build() {Column() {Swiper() {Image($rawfile('computer/computer1.png')).height(220).width('100%')Image($rawfile('computer/computer2.png')).height(220).width('100%')Image($rawfile('computer/computer3.png')).height(220).width('100%')Image($rawfile('computer/computer4.png')).height(220).width('100%')Image($rawfile('computer/computer5.png')).height(220).width('100%')Image($rawfile('computer/computer6.png')).height(220).width('100%')}.index(0).autoPlay(true).interval(3000).indicator(true).loop(true).height(250).width('100%')}.height(250).width('100%')}
}
DetailText组件代码如下:
@Component
struct DetailText{build() {Column() {Row(){Image($rawfile('computer/icon-promotion.png')).height(30).width(30).margin({left:10})Text('Special Offer: ¥9999').fontColor(Color.White).fontSize(20).margin({left:10})}.width('100%').height(35).backgroundColor(Color.Red)Column(){Text('New Arrival: HUAWEI MateBook X Pro 2021').fontSize(15).margin({left:10}).alignSelf(ItemAlign.Start)Text('13.9-Inch, 11th Gen Intel® Core™ i7, 16 GB of Memory, 512 GB of Storage, Ultra-slim Business Laptop, 3K FullView Display, Multi-screen Collaboration, Emerald Green').fontSize(10).margin({left:10})Row(){Image($rawfile('nav/icon-buy.png')).height(15).width(15).margin({left:10})//TODO 暂不支持跑马灯组件,用Text代替Text('Limited offer').fontSize(10).fontColor(Color.Red).margin({left:100})}.backgroundColor(Color.Pink).width('100%').height(25).margin({top:10})Text(' Shipment: 2-day shipping').fontSize(13).fontColor(Color.Red).margin({left:10,top:5}).alignSelf(ItemAlign.Start)Text(' Ship To: Hubei,Wuhan,China').fontSize(13).fontColor(Color.Red).margin({left:10,top:5}).alignSelf(ItemAlign.Start).onClick(()=>{prompt.showDialog({title:'select address',})})Text('Guarantee: Genuine guaranteed').fontSize(13).margin({left:10,top:5}).alignSelf(ItemAlign.Start)}.height(150).width('100%')}.height(160).width('100%')}
}
DetailArsList组件代码如下:
@Component
struct DetailArsList{private arsItems: ArsData[]build() {Scroll() {Column() {List() {ForEach(this.arsItems, item => {ListItem() {ArsListItem({ arsItem: item })}}, item => item.id.toString())}.height('100%').width('100%').margin({ top: 5 }).listDirection(Axis.Vertical)}.height(200)}}
}
ArsListItem组件代码如下:
@Component
struct ArsListItem {private arsItem: ArsDatabuild() {Row() {Text(this.arsItem.title+" :").fontSize(11).margin({left:20}).flexGrow(1)Text( this.arsItem.content).fontSize(11).margin({right:20})}.height(14).width('100%').backgroundColor(Color.White)}
}
DetailBottom组件代码如下:
@Component
struct DetailBottom{@Provideprivate value : number=1dialogController : CustomDialogController = new CustomDialogController({builder: DialogExample({action: this.onAccept}),cancel: this.existApp,autoCancel: true});onAccept() {}existApp() {}build(){Column(){Text('Buy').width(40).height(25).fontSize(20).fontColor(Color.White).onClick(()=>{this.value=1this.dialogController.open()})}.alignItems(HorizontalAlign.Center).backgroundColor(Color.Red).width('100%').height(40)}
}
DialogExample自定义弹窗组件代码如下:
@CustomDialog
struct DialogExample {@Consumeprivate value : numbercontroller: CustomDialogController;action: () => void;build() {Column() {Progress({value: this.value++ >=100?100:this.value, total: 100, style: ProgressStyle.Eclipse}).height(50).width(100).margin({top:5})}.height(60).width(100)}
}
添加资源文件
程序中所用到的资源文件都放置到resources\rawfile目录下。
删除index.ets
因为程序的入口文件已经改为了HomePage.ets文件,因此需要删除Index.ets文件。
HomePage.ets文件重新命名
把HomePage.ets文件重新命名为Index.ets文件。
程序运行效果
以上内容是介绍了如何用ArkUI来开发一个购物应用程序全部教程
为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了几套最新版的HarmonyOS NEXT学习资源
获取完整版高清学习路线,请点击→《HarmonyOS教学视频》
HarmonyOS教学视频
鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程
鸿蒙生态应用开发白皮书V2.0PDF:
获取白皮书:请点击→《鸿蒙生态应用开发白皮书V2.0PDF》
鸿蒙 (Harmony OS)开发学习手册
一、入门必看
- 应用开发导读(ArkTS)
- .……
二、HarmonyOS 概念
- 系统定义
- 技术架构
- 技术特性
- 系统安全
- …
三、如何快速入门?《鸿蒙基础入门学习指南》
- 基本概念
- 构建第一个ArkTS应用
- .……
四、开发基础知识
- 应用基础知识
- 配置文件
- 应用数据管理
- 应用安全管理
- 应用隐私保护
- 三方应用调用管控机制
- 资源分类与访问
- 学习ArkTS语言
- .……
五、基于ArkTS 开发
- Ability开发
- UI开发
- 公共事件与通知
- 窗口管理
- 媒体
- 安全
- 7.网络与链接
- 电话服务
- 数据管理
- 后台任务(Background Task)管理
- 设备管理
- 设备使用信息统计
- DFX
- 国际化开发
- 折叠屏系列
- .……
更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册》