一、项目描述
本案例包含标题栏,返回键,刷新键,商品卡片等元素构成。
该案例主要为了查看商品图片、商品名称、商品价格。可以支持滑动。
该案例效果图如上图所示。
二、项目准备
2.1初始代码
去除无效代码保存以下代码:
@Entry
@Component
struct Text001 {
build() {
Column(){
}.width("100%").height("100%")
.backgroundColor(0xDCDCDC)
}
}
2.2导入图片
将实现准备好的图片导入entry/src/main/resources/base/media目录下。
如下图所示 ,将需要的图片均已导入目录中。
三、页面绘制
利用线性布局中的Row和Column容器组件进行页面绘制。
由分析可知,本案例可以通过Image、Text组件构建了商品栏。通过ForEach循环可将一个商品栏渲染成商品列表。为商品列表加入List即可实现滑动效果。
3.1 标题栏的绘制
由分析可知标题栏主要由返回按钮、标题名称、留白、刷新按钮四部分组成。
四个元素之间采用Row容器布局,具体代码如下。
Row(){
Image($r('app.media.back'))
Text("华为商城")
Blank()
Image($r('app.media.refresh'))
}
上述代码并不能实现图示效果,还需对其美化,具体代码如下:
Row(){
Image($r('app.media.back'))
.width(30)
.height(30)
Text("华为商城")
.fontSize(30)
Blank()
Image($r('app.media.refresh'))
.height(30)
.width(30)
}.width("98%").alignItems(VerticalAlign.Top).margin(10)
至此,标题栏绘制完毕。
3.2商品栏的绘制
由分析可知,商品栏采用了Row和Column两种布局容器,两个文本之间用Column容器包裹,图片与文本之间用Row容器包裹。
代码如下:
Row(){
Image($r('app.media.huaweimatebookxpro'))
Column(){
Text("华为Matebook X Pro")
Text("¥ 14999")
}
}
此时布局效果还不能显现,还需对其加入属性进行美化,代码如下:
Row(){
Image($r('app.media.huaweimatebookxpro'))
.width(100)
.height(100)
.padding({left:10,top:10})
Column(){
Text("华为Matebook X Pro")
.fontSize(20)
Text("¥ 14999")
.fontSize(20)
.fontColor(Color.Red)
}.margin({left:10,top:20}).alignItems(HorizontalAlign.Start)
}.width("96%").height("15%").alignItems(VerticalAlign.Top).borderRadius(10)
.backgroundColor(Color.White)
.margin(5)
至此,商品栏的效果就实现了。
四、循环渲染
并定义item类,建立数组items用于储存商品名称、商品图片、商品价格等信息。利用ForEach对商品栏进行循环渲染,使其成为商品列表。
4.1定义变量
在全局建立item类,在item类中建立变量name、image、price,分别用于储存商品名称,商品图片,商品价格。并实例化这些变量。代码如下:
class item{
name:string
image:ResourceStr
price:number
constructor(name:string,image:ResourceStr,price:number){
this.name=name
this.image=image
this.price=price
}
}
4.2建立数组
在Text001内建立items数组,用于存放商品名称、商品图片、商品价钱。
代码如下:
private items:Array<item>=[
new item("华为nova12",$r("app.media.huaweinova12"),4399),
new item("华为Mate60",$r("app.media.huaweimate60"),5499),
new item("华为Matebookxpro",$r("app.media.huaweimatebookxpro"),14999),
new item("华为Matebook14",$r("app.media.huaweimatebook14"),8499),
new item("华为Matebookxpro",$r("app.media.huaweimatebookxpro"),14999),
new item("华为Mate60",$r("app.media.huaweimate60"),5499),
new item("华为Matex5",$r("app.media.huaweimatex5"),14399),
new item("华为freebuds4e",$r("app.media.huaweifreebuds4e"),399),
new item("华为Matebook14",$r("app.media.huaweimatebook14"),8499)
]
4.3渲染商品
利用ForEach循环,将商品栏作为循环体放入其中,并将image变量传给Image组件,name、price变量同理传给两个Text组件。
代码如下:
ForEach(
this.items,
(item:item)=>{
Row(){
Image(item.image)
.width(100)
.height(100)
.padding({left:10,top:10})
Column(){
Text(item.name)
.fontSize(20)
Text("¥
"+item.price)
.fontSize(20)
.fontColor(Color.Red)
}.margin({left:10,top:20}).alignItems(HorizontalAlign.Start)
}.width("96%").height("15%").alignItems(VerticalAlign.Top).borderRadius(10)
.backgroundColor(Color.White)
.margin(5)
}
)
效果图如下:
通过使用ForEach,首先拿到items数组,并指定数组类型为Item类型。并将商品栏写入循环体中。
从而依次渲染了华为Nova12、华为Mate60…等商品。
效果图如下图:
4.4实现滑动
通过加入List组件和List的子组件ListItem来实现滑动效果。并为其设置权重layoutweight来实现滑动触底。
代码如下:
List(){
ForEach(
this.items,
(item:item)=>{
ListItem(){
Row(){
Image(item.image)
.width(100)
.height(100)
.padding({left:10,top:10})
Column(){
Text(item.name)
.fontSize(20)
Text("¥ "+item.price)
.fontSize(20)
.fontColor(Color.Red)
}.margin({left:10,top:20}).alignItems(HorizontalAlign.Start)
}.width("96%").height("15%").alignItems(VerticalAlign.Top).borderRadius(10)
.backgroundColor(Color.White)
.margin(5)
}
}
)
} .layoutWeight(1)
五、封装
对上述代码分析可知,虽然实现了基本功能,但代码的结构并不清晰,所以需要对代码进行封装处理。
5.1封装标题组件
建立subassembly包用来存放自定义组件。并建立Header标题组件。
在Header文件中,创建自定义组件Header用于封装标题组件并对其进行导出处理。代码如下:
@Component
export struct Header {
title: string
build() {
Row() {
Image($r('app.media.back'))
.width(30)
.height(30)
Text(this.title)
.fontSize(30)
Blank()
Image($r('app.media.refresh'))
.height(30)
.width(30)
}.width("98%").alignItems(VerticalAlign.Top).margin(10)
}
}
删除pages包下的标题组件部分,调用自定义组件Header,并为其title变量传值。代码如下:
build() {
Column(){
Header({title:"华为商城"})
List(){…}.layoutweight(1)
}.width("100%").height("100%")
.backgroundColor(0xDCDCDC)
}
5.2构建自定义函数
由于618马上来了,现在开始华为商城平台准备对个别商品进行打折促销,为此新增变量subsidy记录折扣价格。并构建自定义函数用于封装打折和不打折两种状态的卡片。具体代码如下:
5.2.1新增折扣变量
新增折扣价格变量subsidy,用于记录折扣价,subsidy为可选可不选参数,代码如下:
class item {
name: string
image: ResourceStr
price: number
subsidy: number
constructor(name: string, image: ResourceStr, price: number, subsidy?: number) {
this.name = name
this.image = image
this.price = price
this.subsidy = subsidy
}
}
由于618的到来部分商品打折,所以商品数组变化如下:
private items: Array<item> = [
new item("华为nova12", $r("app.media.huaweinova12"), 4399,200),
new item("华为Mate60", $r("app.media.huaweimate60"), 5499),
new item("华为Matebookxpro", $r("app.media.huaweimatebookxpro"), 14999,500),
new item("华为Matebook14", $r("app.media.huaweimatebook14"), 8499),
new item("华为Matebookxpro", $r("app.media.huaweimatebookxpro"), 14999),
new item("华为Mate60", $r("app.media.huaweimate60"), 5499,100),
new item("华为Matex5", $r("app.media.huaweimatex5"), 14399),
new item("华为freebuds4e", $r("app.media.huaweifreebuds4e"), 399,50),
new item("华为Matebook14", $r("app.media.huaweimatebook14"), 8499)
]
5.2.2封装商品栏
由于618的到来,部分商品会进行打折处理,所以分别对是否打折两种卡片利用自定义构建函数进行封装。
创建自定义属性方法用于封装组件样式
1.商品名称自定义样式:
@Extend(Text) function fancy () {
.fontWeight(FontWeight.Bold)
.fontSize(20)
}
2.商品栏属性样式:
@Styles function globalFancy() {
.backgroundColor(Color.White).width("95%").padding(10).margin({left:10}).borderRadius(20).height(120)
}
3.纵向主容器属性样式
@Styles function globalFancy2() {
.backgroundColor("#ffeff2f6").width("100%").height("100%").padding(10)
}
对有折扣的商品进行封装:
@Builder ModuleCard1(item:item){
Row() {
Image(item.image)
.width(100)
.height(100)
.padding({ left: 10, top: 10 })
Column() {
Text(item.name)
.fancy()
Text("原价:¥ " + item.price)
.fontColor(Color.Grey)
.decoration({type:TextDecorationType.LineThrough})
Text("折扣价:¥" + (item.price - item.subsidy))
.fontColor(Color.Red)
Text("补贴:¥" + item.subsidy)
.fontColor(Color.Red)
}.margin({ left: 10, top: 20 }).alignItems(HorizontalAlign.Start)
}.globalFancy()
}
对未打折的商品进行封装:
@Builder ModuleCard2(item:item){
Row() {
Image(item.image)
.width(100)
.height(100)
.padding({ left: 10, top: 10 })
Column() {
Text(item.name)
.fancy()
Text("¥ " + item.price)
.fontSize(20)
.fontColor(Color.Red)
}.margin({ left: 10, top: 20 }).alignItems(HorizontalAlign.Start)
}.globalFancy()
}
六、调用
上述,已经将常用到的组件和方法进行了封装处理,这里要对其进行调用。
通过if..else..语句判断是否折扣,并调用封装好的组件,完成页面绘制。
代码如下:
build() {
Column() {
Header({title:"华为商城"})
List({space:12}) {
ForEach(
this.items,
(item: item) => {
ListItem() {
if (item.subsidy!=null) {
this.ModuleCard1(item)
}else {
this.ModuleCard2(item)
}
}
}
)
}.layoutWeight(1)
}.globalFancy2()
}
七、源码
//Header
//自定义标题组件
@Component
export struct Header {
title: string
build() {
Row() {
Image($r('app.media.back'))//返回按钮图片
.width(30)
.height(30)//宽高
Text(this.title)//标题提示文本
.fontSize(30)
Blank()//留白
Image($r('app.media.refresh'))//刷新图片
.height(30)
.width(30)
}.width("98%").alignItems(VerticalAlign.Top).margin(10)//布局属性
}
}
//Text001
import { Header } from '../subassembly/Header'
//定义类,存放变量
class item {
name: string
image: ResourceStr
price: number
subsidy:number
constructor(name: string, image: ResourceStr, price: number,subsidy?:number) {
this.name = name
this.image = image
this.price = price
this.subsidy=subsidy
}
}
//自定义属性方法
//文字属性
@Extend(Text) function fancy () {
.fontWeight(FontWeight.Bold)
.fontSize(20)
}
//商品栏属性
@Styles function globalFancy() {
.backgroundColor(Color.White).width("95%").padding(10).margin({left:10}).borderRadius(20).height(120)
}
//容器属性
@Styles function globalFancy2() {
.backgroundColor("#ffeff2f6").width("100%").height("100%").padding(10)
}
@Entry
@Component
struct Text001 {
//自定义构造函数打折商品栏
@Builder ModuleCard1(item:item){
Row() {
Image(item.image)
.width(100)
.height(100)
.padding({ left: 10, top: 10 })
Column() {
Text(item.name)
.fancy()
Text("原价:¥ " + item.price)
.fontColor(Color.Grey)
.decoration({type:TextDecorationType.LineThrough})
Text("折扣价:¥" + (item.price - item.subsidy))
.fontColor(Color.Red)
Text("补贴:¥" + item.subsidy)
.fontColor(Color.Red)
}.margin({ left: 10, top: 20 }).alignItems(HorizontalAlign.Start)
}.globalFancy()
}
//自定义构建函数未打折商品栏
@Builder ModuleCard2(item:item){
Row() {
Image(item.image)
.width(100)
.height(100)
.padding({ left: 10, top: 10 })
Column() {
Text(item.name)
.fancy()
Text("¥ " + item.price)
.fontSize(20)
.fontColor(Color.Red)
}.margin({ left: 10, top: 20 }).alignItems(HorizontalAlign.Start)
}.globalFancy()
}
//商品数组
private items: Array<item> = [
new item("华为nova12", $r("app.media.huaweinova12"), 4399,200),
new item("华为Mate60", $r("app.media.huaweimate60"), 5499),
new item("华为Matebookxpro", $r("app.media.huaweimatebookxpro"), 14999,500),
new item("华为Matebook14", $r("app.media.huaweimatebook14"), 8499),
new item("华为Matebookxpro", $r("app.media.huaweimatebookxpro"), 14999),
new item("华为Mate60", $r("app.media.huaweimate60"), 5499,100),
new item("华为Matex5", $r("app.media.huaweimatex5"), 14399),
new item("华为freebuds4e", $r("app.media.huaweifreebuds4e"), 399,50),
new item("华为Matebook14", $r("app.media.huaweimatebook14"), 8499)
]
//布局
build() {
Column() {
Header({title:"华为商城"})
//列表,条件判断、循环渲染
List({space:12}) {
ForEach(
this.items,
(item: item) => {
ListItem() {
if (item.subsidy!=null) {
this.ModuleCard1(item)
}else {
this.ModuleCard2(item)
}
}
}
)
}.layoutWeight(1)
}.globalFancy2()
}
}
至此,封装完成,效果实现,任务完成。耶✌!!!