鸿蒙开发【商城列表案例】详细实操

商城列表案例实操总结

完成如下图展示效果:

一、构建自定义组件Header用于封装页头

1.1在编译器中创建一个components目录,在目录下创建一个ArkTS 文件命名为Header

如下图:

代码展示如下:

@Component

  struct Header {
 
  build() {

      }

1.2 创建一个ArkTS 文件命名为ItemPage

如下图:


@Entry
@Component

struct ItemPage {

build(){

}

1.3 在Header页面中build()下建立Row()容器,并从左往右依次排列Image图片,Text文本组件,Image图片组件,利用space让其有间距

代码示例:

build() {
 
Row({space:10}){
   
Image($r('app.media.jiantou')).width(30)
   
//如果需要传值
    // Text("我是标题")
   
Text(this.title)
     
.fontSize(30)
     
.fontWeight(FontWeight.Bold)
   
Blank() //把剩余的空间都填充满
    Image($r('app.media.shuaxin')).width(30)
 
}
 
.width('100%')//row容器设置一个宽度
  .padding(20)

}

1.4在Text文本内容和Image组件中添加Blank()组件,用于调节各个组件之间的距离,把剩余的空间都填充满。

    Blank() //把剩余的空间都填充满

1.5定义title 标题变量,并传入标题数据

  struct Header {

title:string  //标题不能写死,在这里定义一个标题变量
 
build() {
   
Row({space:10}){
     
Image($r('app.media.jiantou')).width(30)
      Text(this.title)
       
.fontSize(30)
       
.fontWeight(FontWeight.Bold)
     
Blank() //把剩余的空间都填充满
     
Image($r('app.media.shuaxin')).width(30)
   
}
   
.width('100%')//row容器设置一个宽度
   
.padding(20)
  }
}

1.6对自定义组件Header进行导出处理

export  struct Header {

title:string  //标题不能写死,在这里定义一个标题变量
 
build() {
   
Row({space:10}){
     
Image($r('app.media.jiantou')).width(30)
      Text(this.title)
       
.fontSize(30)
       
.fontWeight(FontWeight.Bold)
     
Blank() //把剩余的空间都填充满
     
Image($r('app.media.shuaxin')).width(30)
   
}
   
.width('100%')//row容器设置一个宽度
   
.padding(20)
  }
}

1.7 在ItemPage中建立建立Column容器,导包并使用Header

struct ItemPage {

build() {
 
Row(){
   
Column({space:10}){
    
      Header({title:'商品列表'})

导包使用:

//import 导入 Header
// import {Header} from './Header'
//
加了default Header就不用再加上{ }
import Header from './Header'

 引用 Header Footer import {Header,Footer} from './Header'

export 对外有了一个访问的接口,是在Header的到出方法中添加default

则ItemPage就不用再导入加不用再加上{ }了。

如下例代码:

@Component
//export 对外有了一个访问的接口
 
export default  struct Header {
 
title:string  //标题不能写死,在这里定义一个标题变量

1.8 给Header和Item Page中添加属性,从而实现效果

.width('100%')//row容器设置一个宽度
.padding(20)

ItemPage中Column容器属性

.width('100%')
.height('100%')
.backgroundColor('#EFEFEF')
.padding(20)

运行效果:

二、学习循环渲染,定义Item类并通过ForEach循环渲染界面

2.1定义ImageItem类,传入商品名称,图片、价格、折扣等变量实例化

//声明一个ImageItem
class ImageItem {
 
name :string
 
image:ResourceStr
 
price:number
 
discount:number

//修改里面的值
 
setName(name:string){
   
// this.name =name+this.name
   
this.name =name
 
}
 
getName(){
   
return this.name
 
}

2.2定义构造函数传参

//定义构造函数
constructor( name :string,image:ResourceStr,price:number,discount:number=0) {
 
// ?: 非必选,设置可选参数
 
//赋值
 
this.name =name
 
this.image=image
 
this.price=price
 
this.discount=discount
}

若不写构造函数,只能使用new的方法来实现传入商品数据。

//若不写构造函数,只能使用new
//item:Item =new Item('
华为Mate60',$r('app.media.mate60'),6999,500)

2.3定义一个数组,来传入商品的具体数据信息


struct ItemPage {
  private  list:Array<ImageItem> =[
   
new ImageItem('华为Mate60',$r('app.media.mate60'),6999,500),
    new
ImageItem('华为Book',$r('app.media.Book'),13999),
    new
ImageItem('WatchGT4',$r('app.media.WatchGT4'),1438),
    new
ImageItem('FreeBuds',$r('app.media.FreeBuds'),1499),
    new
ImageItem('Mate X5',$r('app.media.MateX5'),12999),
    new
ImageItem('WatchGT41',$r('app.media.WatchGT4'),1438),
    new
ImageItem('FreeBuds1',$r('app.media.FreeBuds'),1499),
    new
ImageItem('Mate X51',$r('app.media.MateX5'),12999)
  ]

2.4在自定义组件Header下继续创建Row()容器传入子组件Image

在Row容器从左到右依次部署Image组件、Column组件,传入数据,并调整Image和Text组件大小。

build() {
 
Row(){
   
Column({space:10}){
      Header({title:'商品列表'})
        Row(){
            
Image(item.image)
              
.width(100)
            
Column(){
               Text(item.name)
                
.fontSize(20)
                
.fontWeight(FontWeight.Bold)

2.5利用ForEach进行循环渲染,将页面展示组件渲染成多个,利用Item数组为其传入数据

ForEach(this.list,(item:ImageItem)=>{
 
Row(){
      
Image(item.image)
        
.width(100)
      
Column(){
         Text(item.name)
          
.fontSize(20)
          
.fontWeight(FontWeight.Bold)

Text(`原价:¥ ${item.price}`)

三、学习条件渲染,根据条件判断显示不同的样式

3.1再Column容器中从上到下依次部署四个Text组件,从上到下依次传入商品名称、原价、折扣价、补贴

Column(){
  Text(item.name)
   
.fontSize(20)
   
.fontWeight(FontWeight.Bold)
    Text(`原价:¥ ${item.price}`)
   
Text(`折扣价:¥ ${item.price-item.discount}`)
   
Text(`补贴:¥ ${item.discount}`)

3.2 使用if…else…判断语句,如果discount没有值则执行正常组件,如果discount有值则执行折扣组件

//if渲染
 
if (item.discount){
   
Text(`原价:¥ ${item.price}`)
     
.fontColor('#ccc')
     
.decoration({type:TextDecorationType.LineThrough})
   
Text(`折扣价:¥ ${item.price-item.discount}`)
     
.fontSize(16)
     
.fontColor('#F36')
   
Text(`补贴:¥ ${item.discount}`)
 
}else{
   
Text('原价:¥'+item.price)
     
.fontSize(16)
     
.fontColor('#F36')
 
}
}

3.3 给代码加上属性美化页面

代码运行结果:

加上之前:

加上之后:整体实现效果了

四、学习List组件,结合循环渲染改造页面

4.1添加List组件,让页面有效果

List( {space:10}){
 
ForEach(this.list,(item:ImageItem)=>{

  if (item.discount){
   
Text(`原价:¥ ${item.price}`)
     
.fontColor('#ccc')
     
.decoration({type:TextDecorationType.LineThrough})
   
Text(`折扣价:¥ ${item.price-item.discount}`)
     
.fontSize(16)
     
.fontColor('#F36')
   
Text(`补贴:¥ ${item.discount}`)
 
}else{
   
Text('原价:¥'+item.price)
     
.fontSize(16)
     
.fontColor('#F36')
 
}
}

4.2 使用List组件后还需要使用List子组件ListItem,ListItem组件分别嵌套折扣和不折扣两种状态

List( {space:10}){
 
ForEach(this.list,(item:ImageItem)=>{
   
ListItem(){
      Row(){
       
Image(item.image)
         
.width(100)
       
Column(){
         
//标题名字固定
         
Text(item.name)
           
.fontSize(20)
           
.fontWeight(FontWeight.Bold)
         
//if渲染
         
if (item.discount){
           
Text(`原价:¥ ${item.price}`)
             
.fontColor('#ccc')
             
.decoration({type:TextDecorationType.LineThrough})
           
Text(`折扣价:¥ ${item.price-item.discount}`)
             
.fontSize(16)
             
.fontColor('#F36')
           
Text(`补贴:¥ ${item.discount}`)
             
.fontSize(16)
             
.fontColor('#F36')
         
}else{
           
Text('原价:¥'+item.price)
             
.fontSize(16)
             
.fontColor('#F36')
         
}
        }

4.3给ForEach设置键值生成,设置唯一标识重复渲染

4.4利用layoutWeight设置权重

目前商城界面已经支持滑动了,但是最下面的图片还不能完全展示出来,我们需要用.layoutWeight属性设置权重。

为List组件使用.layoutWeight属性设置权重。

未设置前:

设置以后就能完全看见底部内容展示了

4.5 利用学习的属性对页面进行相应的美化

设置容器的间距,以及组件颜色,对容器设置宽度大小,背景颜色,以及字体颜色等。

build() {
 
Row(){
   
Column({space:10}){
     
      Header({title:'商品列表'})
     
      List( {space:10}){
       
ForEach(this.list,(item:ImageItem)=>{
         
ListItem(){
            Row(){
             
Image(item.image)
               
.width(100)
             
Column(){
               
//标题名字固定
               
Text(item.name)
                 
.fontSize(20)
                 
.fontWeight(FontWeight.Bold)
               
//if渲染
               
if (item.discount){
                 
Text(`原价:¥ ${item.price}`)
                   
.fontColor('#ccc')
                   
.decoration({type:TextDecorationType.LineThrough})
                 
Text(`折扣价:¥ ${item.price-item.discount}`)
                   
.fontSize(16)
                   
.fontColor('#F36')
                 
Text(`补贴:¥ ${item.discount}`)
                   
.fontSize(16)
                   
.fontColor('#F36')
               
}else{
                 
Text('原价:¥'+item.price)
                   
.fontSize(16)
                   
.fontColor('#F36')
               
}
              }
             
.height('100%')
             
.alignItems(HorizontalAlign.Start)
           
}//设置row的宽度和高度,以及圆角边框大小,背景颜色
           
.height(120)
           
.width('100%')
           
.borderRadius(20)
           
.backgroundColor('#FFF')
           
.padding(10)
         
}
        }
,(item:ImageItem)=>item.name) //键值生成规则 重复渲染 设置唯一标识
     
}
     
.layoutWeight(1)

    }
   
.width('100%')
   
.height('100%')
   
.backgroundColor('#EFEFEF')
   
.padding(20)
 
}
}

五、学习自定义构建函数,将列表项进行封装使用

5.1创建一个自定义函数ItemCard ,用于将打折的内容封装在里面

// //自定义构建函数
@Builder ItemCard(item:ImageItem){
 
Row(){
   
Image(item.image)
     
.width(100)
   
Column(){
      Text(item.name)
       
.fontSize(20)
       
.fontWeight(FontWeight.Bold)
      if (item.discount){
       
Text(`原价:¥ ${item.price}`)
         
.fontColor('#ccc')
         
.decoration({type:TextDecorationType.LineThrough})
       
Text(`折扣价:¥ ${item.price-item.discount}`)
         
.fontSize(16)
         
.fontColor('#F36')
       
Text(`补贴:¥ ${item.discount}`)
         
.fontSize(16)
         
.fontColor('#F36')
     
}else{
       
Text('原价:¥'+item.price)
         
.fontSize(16)
         
.fontColor('#F36')
     
}
    }
   
.height('100%')
   
.alignItems(HorizontalAlign.Start)
 
}//设置row的宽度和高度,以及圆角边框大小,背景颜色
 
.height(120)
 
.width('100%')
 
.borderRadius(20)
 
.backgroundColor('#FFF')
 
.padding(10)
}

5.2再回到ListItem()里面直接调用自定义函数ItemCard

List( {space:10}){
 
ForEach(this.list,(item:ImageItem)=>{
   
ListItem(){
     
// 直接调用自定义函数ItemCard
     
this.ItemCard(item)

5.3 还可以使用全局自定义函数

定义一个全局自定义函数ItemCardTotal,全局自定义构建函数需要加function函数

@Builder function  ItemCardTotal(item:ImageItem){
 
Row(){
   
Image(item.image)
     
.width(100)
   
Column(){
      Text(item.name)
       
.fontSize(20)
       
.fontWeight(FontWeight.Bold)
      if (item.discount){
       
Text(`原价:¥ ${item.price}`)
         
.fontColor('#ccc')
         
.decoration({type:TextDecorationType.LineThrough})
       
Text(`折扣价:¥ ${item.price-item.discount}`)
         
.fontSize(16)
         
.fontColor('#F36')
       
Text(`补贴:¥ ${item.discount}`)
         
.fontSize(16)
         
.fontColor('#F36')
     
}else{
       
Text('原价:¥'+item.price)
         
.fontSize(16)
         
.fontColor('#F36')
     
}
    }
   
.height('100%')
   
.alignItems(HorizontalAlign.Start)
 
}//设置row的宽度和高度,以及圆角边框大小,背景颜色
 
.height(120)
 
.width('100%')
 
.borderRadius(20)
 
.backgroundColor('#FFF')
 
.padding(10)
}

5.4 全局自定义函数的调用

全局自定函数在build()里面就不需要this来进行调用了

List( {space:10}){
 
ForEach(this.list,(item:ImageItem)=>{
   
ListItem(){
      ItemCardTotal(item)

这样效果就完全显示出来啦!

六、学习@Style装饰器,封装组件重用样式并引用

6.1创建一个fillScreen对自定义函数组件Row的属性进行封装

@Styles function fillScreen(){
   
.height(120)
   
.width('100%')
   
.borderRadius(20)
   
.backgroundColor('#FFF')
   
.padding(10)
}

6.2 对自定义组件Row容器属性的调用

@Builder function  ItemCardTotal(item:ImageItem){
 
Row(){
   
Image(item.image)
     
.width(100)
   
Column(){
      Text(item.name)
       
.fontSize(20)
       
.fontWeight(FontWeight.Bold)
      if (item.discount){
       
Text(`原价:¥ ${item.price}`)
         
.fontColor('#ccc')
         
.decoration({type:TextDecorationType.LineThrough})
       
Text(`折扣价:¥ ${item.price-item.discount}`)
         
.fontSize(16)
         
.fontColor('#F36')
       
Text(`补贴:¥ ${item.discount}`)
         
.fontSize(16)
         
.fontColor('#F36')
     
}else{
       
Text('原价:¥'+item.price)
         
.fontSize(16)
         
.fontColor('#F36')
     
}
    }
   
.height('100%')
   
.alignItems(HorizontalAlign.Start)
 
}
  .fillScreen()

以上是在全局自定义组件里面调用

6.3以相同的原理对Column容器的属性进行封装并引用

@Styles function fillScreen1(){
.width('100%')
.height('100%')
.backgroundColor('#EFEFEF')
.padding(20)


}

只能对通用属性进行调用

1.4同理在build()里面对Column的属性进行调用

build() {
   
Row(){
     
Column({space:10}){

Header({title:'商品列表'})

List( {space:10}){
 
ForEach(this.list,(item:ImageItem)=>{
   
ListItem(){

ItemCardTotal(item)

}
  }
,(item:ImageItem)=>item.name) //键值生成规则 重复渲染 设置唯一标识
}
.layoutWeight(1)

}
.fillScreen1()

}
}

}

七、学习@Extend装饰器,定义扩展组件样式并引用

7.1.定义一个priceText()用来存放字体颜色和字体大小

必须声明式Text文本

@Extend(Text) function priceText(){
 
.fontSize(16)
 
.fontColor('#F36')
}

7.2 在自定义函数里面调用该扩展的样式

@Builder function  ItemCardTotal(item:ImageItem){
 
Row(){
   
Image(item.image)
     
.width(100)
   
Column(){
     
//标题名字固定
     
Text(item.name)
       
.fontSize(20)
       
.fontWeight(FontWeight.Bold)
     
//if渲染
     
if (item.discount){
       
Text(`原价:¥ ${item.price}`)
         
.fontColor('#ccc')
         
.decoration({type:TextDecorationType.LineThrough})
       
Text(`折扣价:¥ ${item.price-item.discount}`)
         
.priceText()
       
Text(`补贴:¥ ${item.discount}`)
         
.priceText()
     
}else{
       
Text('原价:¥'+item.price)
         
.priceText()
      }
    }
.alignItems(HorizontalAlign.Start)
   
.fillScreen1()

 
}
  .fillScreen()


}

八、源代码展示

//import 导入 Header
// import {Header} from './Header'
//
加了default Header就不用再加上{ }
import Header from './Header'
//声明一个ImageItem
class ImageItem {
 
name :string
 
image:ResourceStr
 
price:number
 
discount:number
//修改里面的值
 
setName(name:string){
   
// this.name =name+this.name
   
this.name =name
 
}
 
getName(){
   
return this.name
 
}
 
//定义构造函数
constructor( name :string,image:ResourceStr,price:number,discount:number=0) {
   
// ?: 非必选,设置可选参数
   
//赋值
   
this.name =name
   
this.image=image
   
this.price=price
   
this.discount=discount
 
}
}

//全局自定义构建函数需要加function函数 因此build()里面就不需要this来进行调用了
@Builder function  ItemCardTotal(item:ImageItem){
 
Row(){
   
Image(item.image)
     
.width(100)
   
Column(){
     
//标题名字固定
     
Text(item.name)
       
.fontSize(20)
       
.fontWeight(FontWeight.Bold)
     
//if-else渲染
     
if (item.discount){
       
Text(`原价:¥ ${item.price}`)
         
.fontColor('#ccc')
         
.decoration({type:TextDecorationType.LineThrough})
       
Text(`折扣价:¥ ${item.price-item.discount}`)
         
.priceText()
       
Text(`补贴:¥ ${item.discount}`)
         
.priceText()
     
}else{
       
Text('原价:¥'+item.price)
         
.priceText()
         
// .fontSize(16)
          // .fontColor('#F36')
     
}
    }
.alignItems(HorizontalAlign.Start)
   
.fillScreen1()

 
}//设置row的宽度和高度,以及圆角边框大小,背景颜色
 
.fillScreen()

}
@Styles function fillScreen(){
   
.height(120)
   
.width('100%')
   
.borderRadius(20)
   
.backgroundColor('#FFF')
   
.padding(10)
}
@Styles function fillScreen1(){
 
.width('100%')
.height('100%')
.backgroundColor('#EFEFEF')
.padding(20)

 
}
@Extend(Text) function priceText(){
 
.fontSize(16)
 
.fontColor('#F36')
}
@Entry
@Component

struct ItemPage {
  //定义数组
 
private  list:Array<ImageItem> =[
   
new ImageItem('华为Mate60',$r('app.media.mate60'),6999,500),
    new
ImageItem('华为Book',$r('app.media.Book'),13999),
    new
ImageItem('WatchGT4',$r('app.media.WatchGT4'),1438),
    new
ImageItem('FreeBuds',$r('app.media.FreeBuds'),1499),
    new
ImageItem('Mate X5',$r('app.media.MateX5'),12999),
    new
ImageItem('WatchGT41',$r('app.media.WatchGT4'),1438),
    new
ImageItem('FreeBuds1',$r('app.media.FreeBuds'),1499),
    new
ImageItem('Mate X51',$r('app.media.MateX5'),12999)
  ]

  //如果不传参数,直接给默认值0
 
test(name:string,age:number =0){
   
console.log('name',name,'age',age)
 
}
 
build() {
   
Row(){
     
Column({space:10}){

Header({title:'商品列表'})

List( {space:10}){
 
ForEach(this.list,(item:ImageItem)=>{
   
ListItem(){

ItemCardTotal(item)

}
  }
,(item:ImageItem)=>item.name) //键值生成规则 重复渲染 设置唯一标识
}
.layoutWeight(1)

}
.fillScreen1()

   }
  }

}

嘿嘿 !最后我们就完成了商城案例啦!

  • 30
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
华为鸿蒙HarmonyOS开发整理资料汇总,共38份。 1学前必读:HarmonyOS学习资源主题分享 2学前必读:OpenHarmony-联盟生态资料合集 3-1.HarmonyOS概述:技术特性 3-2.HarmonyOS概述:开发工具与平台 3-3.HarmonyOS概述:系统安全 3-4.HarmonyOS概述:系统定义 3-5.HarmonyOS概述:下载与安装软件 3-6.HarmonyOS概述:应用开发基础知识 3-7.HarmonyOS概述:最全HarmonyOS文档和社区资源使用技巧 4-1.生态案例:【开发者说】重塑经典,如何在HarmonyOS手机上还原贪吃蛇游戏 4-2.生态案例:HarmonyOLabo涂鸦鸿蒙亲子版 4-3.生态案例:HarmonyOS分镜头APP案例 4-4.生态案例:HarmonyOS时光序历史学习案例 4-5.生态案例:HarmonyOS先行者说 宝宝巴士携手HarmonyOS共同打造儿童教育交互新体验 4-6.生态案例:HarmonyOS智能农场物联网连接实践 4-7.生态案例:分布式开发样例,带你玩转多设备 4-8.生态案例华为分布式日历应用开发实践 5-1.【Codelab】HarmonyOS基于图像模块实现图库图片的四种常见操作 5-2.【CodeLab】手把手教你创建第一个手机“Hello World” 5-3.【Codelab】如此简单!一文带你学会15个HarmonyOS JS组件 5-4.【Codelab】懒人“看”书新法—鸿蒙语音播报,到底如何实现? 5-5.【Codelab】基于AI通用文字识别的图像搜索,这波操作亮了 5-6.【Codelab】开发样例概览 6-1.技术解读之HarmonyOS轻量JS开发框架与W3C标准差异分析 6-2.技术解读之HarmonyOS驱动加载过程分析 6-3.技术解读之HarmonyOS组件库使用实践 6-4.技术解读之华为架构师解读:HarmonyOS低时延高可靠消息传输原理 6-5.技术解读之解密HarmonyOS UI框架 6-6.技术解读之如何从OS框架层面实现应用服务功能解耦 7-1.常见问题之HarmonyOS元服务的设计与开发解析 7-2.常见问题之Java开发 7-3.常见问题之JS开发 7-4.常见问题之模拟器登录 7-5.常见问题之模拟器运行 7-6.常见问题之如何使用JsJava开发HarmonyOS UI 7-7.常见问题之应用配置 7-8.常见问题之预览器运行 8【视频合集】入门到进阶视频学习资料合集30+

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值