商城列表案例实操总结
完成如下图展示效果:
一、构建自定义组件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()
}
}
}