【HarmonyOS NEXT开发——ArkTS语言】购物商城的实现【合集】

简介: HarmonyOS应用开发使用@Component装饰器将Home结构体标记为一个组件,意味着它可以在界面构建中被当作一个独立的UI单元来使用,并且按照其内部定义的build方法来渲染具体的界面内容。txt:string定义了一个名为Data的接口,用于规范表示产品数据的结构。src:类型为,推测是用于引用资源(可能是图片资源等)的一种特定类型,用于指定产品对应的图片资源。txt:字符串类型,用于存放产品的文字描述,比如产品名称等相关信息。price:数值类型,用于表示产品的价格信息。

目录

😋环境配置:华为HarmonyOS开发者

📺演示效果:

📖实验步骤及方法:

一、在src/main/ets文件中创建components文件夹并在其中创建Home.ets和HomeProduct.ets文件。编辑

二、在Home.ets文件中定义 Home 组件,进行商城主页的布局与相关功能的部署。

1. 导入模块

2. Home 组件定义

3. aboutToAppear 方法

4. build 方法(核心界面构建逻辑)

(1)整体布局结构:

(2)搜索栏部分:

(3)主体内容部分(基于 List 组件)

5. 自定义文本样式扩展函数

三、在HomePruduct.ets文件中定义 HomeProduct 组件,展示商城主页中的内容。

1. 接口定义

2. HomeProduct 组件定义

3. 组件状态定义

4. aboutToAppear 生命周期方法

5. getItem 构建函数

6. build 方法(核心界面构建逻辑)

四、在src/main/ets/pages/Index.ets文件中,定义Index 组件作为应用的入口组件。

1. 导入模块

2. Index 组件定义与入口标识

3. aboutToAppear 生命周期方法

4. 组件状态定义

5. myBuilder 构建函数

6. build 方法(核心界面构建逻辑)

👋实验小结


🎯学习小目标:实现如下的购物商城主页效果

📺演示效果:

image.gif

📖实验步骤及方法:

一、在src/main/ets文件中创建components文件夹并在其中创建Home.ets和HomeProduct.ets文件。

image.gif

二、在Home.ets文件中定义 Home 组件,进行商城主页的布局与相关功能的部署。

实现代码如下:

import font from '@ohos.font'
import HomeProduct from '../components/HomeProduct'
@Component
export default struct Home{
  aboutToAppear(): void {
    font.registerFont({
      familyName: 'myFont',
      familySrc: '/fonts/iconfont.ttf'
    })
  }
  build(){
    Column(){
      Row(){
        Image($r('app.media.logoCircle'))
          .width(40)
        Row(){
          TextInput({placeholder:"搜索内容"})
            .layoutWeight(1)
            .fontSize(16)
            .backgroundColor(Color.Transparent)
          Text("\ue679")
            .width(40)
            .height('100%')
            .backgroundColor('#fa2a83')
            .fontFamily('myFont')
            .fontSize(20)
            .fontColor('#fff')
            .fontWeight('bolder')
            .borderRadius({topLeft:0,topRight:20,bottomLeft:0,bottomRight:20})
            .textAlign(TextAlign.Center)
        }
        .height(35)
        .padding({left:5})
        .backgroundColor('#fff')
        .layoutWeight(1)
        .margin({left:3})
        .borderRadius(20)
      }
      .width('100%')
      .padding({top:10,left:'10%',right:"10%",bottom:10})
      .backgroundColor('#0966b4')
      //主体内容
      List(){
        ListItem(){
          Swiper(){
            Image($r('app.media.img01'))
            Image($r('app.media.img02'))
            Image($r('app.media.img03'))
            Image($r('app.media.img04'))
            Image($r('app.media.img05'))
            Image($r('app.media.img06'))
          }
          .width('100%')
          .aspectRatio(2)
          .loop(true)
          .autoPlay(true)
          .interval(3000)
          .indicator(
            Indicator.dot()
              .itemWidth(10)
              .itemHeight(10)
              .selectedItemWidth(20)
              .selectedItemHeight(10)
              .color(Color.White)
              .selectedColor(Color.Red)
          )
        }
        ListItem(){
          Grid(){
            GridItem(){
              Column(){
                Text('\ue67d')
                  .listItem()
                Text('店铺')
                  .icoText()
              }
            }
            GridItem(){
              Column(){
                Text('\ue632')
                  .listItem()
                Text('陶瓷')
                  .icoText()
              }
            }
            GridItem(){
              Column(){
                Text('\ue61f')
                  .listItem()
                Text('二手书')
                  .icoText()
              }
            }
            GridItem(){
              Column(){
                Text('\ue652')
                  .listItem()
                Text('服务')
                  .icoText()
              }
            }
          }
        .width('100%')
        .height('100%')
          .rowsTemplate('1fr')
          .columnsTemplate('1fr 1fr 1fr 1fr')
        }
        .width('100%')
        .height(100)
        .margin({top:5,bottom:5})
        //推荐标题
        ListItem(){
          Row(){
            Text('推荐好物')
              .fontSize('100%')
              .height(30)
              .fontWeight('bolder')
              .fontColor('#0966b4')
            Text('更多⇨')
              .fontSize(12)
              .fontColor('#0966b4')
          }
          .backgroundColor('#d1d1d1')
          .justifyContent(FlexAlign.SpaceBetween)
          .width('100%')
          .padding(10)
        }
        ListItem(){
          HomeProduct()
        }
      }
      .layoutWeight(1)
      .backgroundColor(Color.White)
    }
  }
}
@Extend(Text)function listItem(){
  .width(60)
  .height(60)
  .backgroundColor('#0966b4')
  .fontFamily('myFont')
  .fontSize(35)
  .fontColor('#fff')
  .borderRadius(30)
  .textAlign(TextAlign.Center)
}
@Extend(Text)function icoText(){
  .fontSize(15)
  .height(30)
  .fontWeight('bolder')
}
image.gif

该组件构建了一个具有特定布局和功能的界面,包含了搜索栏、轮播图、网格布局展示分类以及推荐好物相关展示等内容。

 

1. 导入模块

import font from '@ohos.font';
import HomeProduct from '../components/HomeProduct';
image.gif

       从 @ohos.font 导入了 font 模块,可能用于字体相关的操作,比如注册自定义字体等,从后续代码中对字体注册的使用可以印证这一点。

       导入了自定义的 HomeProduct 组件,推测这个组件用于展示具体的推荐好物等相关内容,不过代码中未给出 HomeProduct 的具体实现细节。

2.Home 组件定义

import font from '@ohos.font';
import HomeProduct from '../components/HomeProduct';
image.gif        使用 @Component 装饰器将 Home 结构体标记为一个组件,意味着它可以在界面构建中被当作一个独立的 UI 单元来使用,并且按照其内部定义的 build 方法来渲染具体的界面内容。

3.aboutToAppear 方法

aboutToAppear(): void {
  font.registerFont({
    familyName: 'myFont',
    familySrc: '/fonts/iconfont.ttf'
  })
}
image.gif

       这是一个生命周期相关的方法,在组件即将显示时被调用(根据名称和常见的组件生命周期逻辑推测)。

       它调用了 font 模块的 registerFont 方法,目的是注册一个名为 myFont 的自定义字体,字体文件来源指定为 /fonts/iconfont.ttf,这样后续就可以在组件中使用这个自定义字体来显示特定的文本样式了。

 

4.build 方法(核心界面构建逻辑)

(1)整体布局结构:

       整个界面构建基于 Column(列布局),在这个列布局内部嵌套了多个 Row(行布局)以及其他复杂的组件,来构建出最终的页面结构。

(2)搜索栏部分:

Row(){
  Image($r('app.media.logoCircle'))
   .width(40)
  Row(){
    TextInput({placeholder:"搜索内容"})
     .layoutWeight(1)
     .fontSize(16)
     .backgroundColor(Color.Transparent)
    Text("\ue679")
     .width(40)
     .height('100%')
     .backgroundColor('#fa2a83')
     .fontFamily('myFont')
     .fontSize(20)
     .fontColor('#fff')
     .fontWeight('bolder')
     .borderRadius({topLeft:0,topRight:20,bottomLeft:0,bottomRight:20})
     .textAlign(TextAlign.Center)
  }
 .height(35)
 .padding({left:5})
 .backgroundColor('#fff')
 .layoutWeight(1)
 .margin({left:3})
 .borderRadius(20)
}
.width('100%')
.padding({top:10,left:'10%',right:"10%",bottom:10})
.backgroundColor('#0966b4')
image.gif
  • 外层 Row 作为整体搜索栏的容器,设置了一定的内边距、背景颜色、外边距等样式,并且宽度占满父容器(width('100%'))。
  • 内部首先有一个 Image 组件,显示一个宽度为 40 的图片(可能是应用的 logo 之类的元素),其资源通过 $r('app.media.logoCircle') 方式引用(具体的资源加载机制依赖于框架实现)。
  • 接着又是一个 Row,里面包含 TextInput 和 Text 组件,构建了一个常见的搜索输入框搭配搜索图标样式。
  • TextInput 用于用户输入搜索内容,设置了占位符为 "搜索内容",占一定的布局权重(layoutWeight(1))以自适应宽度,背景透明等样式。
  • Text 组件显示一个特定的图标(通过 \ue679 这个 Unicode 编码对应的图标,可能是自定义字体图标集中的一个),设置了各种样式,如背景颜色、使用注册的 myFont 字体、字体大小、颜色、加粗以及特定的圆角样式等,整体看起来像是搜索按钮的样式呈现。

(3)主体内容部分(基于 List 组件)

List(){
  // 轮播图相关的 ListItem
  ListItem(){
    Swiper(){
      Image($r('app.media.img01'))
      Image($r('app.media.img02'))
      Image($r('app.media.img03'))
      Image($r('app.media.img04'))
      Image($r('app.media.img05'))
      Image($r('app.media.img06'))
    }
   .width('100%')
   .aspectRatio(2)
   .loop(true)
   .autoPlay(true)
   .interval(3000)
   .indicator(
      Indicator.dot()
       .itemWidth(10)
       .itemHeight(10)
       .selectedItemWidth(20)
       .selectedItemHeight(10)
       .color(Color.White)
       .selectedColor(Color.Red)
    )
  }
  // 网格布局分类展示的 ListItem
  ListItem(){
    Grid(){
      GridItem(){
        Column(){
          Text('\ue67d')
           .listItem()
          Text('店铺')
           .icoText()
        }
      }
      // 省略其他几个 GridItem 类似结构,都是展示不同分类
    }
   .width('100%')
   .height('100%')
   .rowsTemplate('1fr')
   .columnsTemplate('1fr 1fr 1fr 1fr')
  }
 .width('100%')
 .height(100)
 .margin({top:5,bottom:5})
  // 推荐标题的 ListItem
  ListItem(){
    Row(){
      Text('推荐好物')
       .fontSize('100%')
       .height(30)
       .fontWeight('bolder')
       .fontColor('#0966b4')
      Text('更多⇨')
       .fontSize(12)
       .fontColor('#0966b4')
    }
   .backgroundColor('#d1d1d1')
   .justifyContent(FlexAlign.SpaceBetween)
   .width('100%')
   .padding(10)
  }
  ListItem(){
    HomeProduct()
  }
}
.layoutWeight(1)
.backgroundColor(Color.White)
image.gif List 组件作为一个可滚动的列表容器,里面包含多个 ListItem,每个 ListItem 呈现不同的内容块。
  • 轮播图 ListItem
  • 内部的 Swiper 组件用于实现图片轮播效果,添加了多个 Image 组件(资源通过类似 $r('app.media.imgXX') 引用)。
  • Swiper 设置了宽度占满父容器、固定的宽高比(aspectRatio(2)),开启循环播放(loop(true))、自动播放(autoPlay(true))且设置了轮播间隔为 3000 毫秒,同时配置了轮播指示器(Indicator.dot() 相关配置),用于显示当前轮播图片的索引等信息,以小圆点形式呈现,并且区分了选中和未选中状态的样式。
  • 网格布局分类展示 ListItem
  • 使用 Grid 组件构建一个网格布局,里面有多个 GridItem,每个 GridItem 又包含 Column 布局,用于垂直排列图标(通过自定义字体图标对应的 Text 组件且应用 listItem 扩展样式)和对应的文字说明(应用 icoText 扩展样式),展示不同的分类,比如店铺、陶瓷等。
  • Grid 设置了宽度、高度占满父容器,以及行列模板,定义了一行四列且均匀分配空间的布局形式。
  • 推荐标题 ListItem
  • 通过 Row 布局包含两个 Text 组件,分别显示 "推荐好物"(样式上做了字体大小、加粗、颜色等设置)和 "更多⇨"(相对小一点字体且同样设置了颜色),整体 Row 设置了背景颜色、两端对齐(justifyContent(FlexAlign.SpaceBetween))以及内边距等样式,用于呈现一个推荐好物的标题栏效果,并且可以点击 "更多⇨" 可能跳转到更多推荐内容页面(具体取决于相关交互逻辑实现,代码中未体现)。

最后一个 ListItem 使用了导入的 HomeProduct 组件,用于展示具体的推荐好物详细内容,不过具体呈现依赖于 HomeProduct 组件自身的实现。

 

5. 自定义文本样式扩展函数

@Extend(Text)function listItem(){
 .width(60)
 .height(60)
 .backgroundColor('#0966b4')
 .fontFamily('myFont')
 .fontSize(35)
 .fontColor('#fff')
 .borderRadius(30)
 .textAlign(TextAlign.Center)
}
@Extend(Text)function icoText(){
 .fontSize(15)
 .height(30)
 .fontWeight('bolder')
}
image.gif

通过 @Extend(Text) 装饰器为 Text 组件扩展了两个自定义样式函数。

  • listItem 函数主要用于给 Text 组件设置特定的宽高、背景颜色(使用之前注册的 myFont 字体、较大字体、白色字体颜色、圆形边框以及文本居中对齐等样式,从代码使用场景来看,可能用于那些作为图标样式展示的 Text 组件)。
  • icoText 函数则是给 Text 组件设置相对小一点的字体大小、固定高度以及加粗字体样式,用于那些配合图标展示的文字说明部分,使整体界面文字显示更规范统一且美观。

三、HomePruduct.ets文件中定义 HomeProduct 组件,展示商城主页中的内容。

实现代码如下:

interface  Data{
  src:ResourceStr
  txt:string
  price:number
}
@Component
export default struct HomeProduct{
  @State datas: Data[] = []
  @State template: string = '1fr 1fr'
  aboutToAppear(): void {
    for(let i=1;i<=20;i++){
      this.datas.push({
        src:i%2==0? $r('app.media.product7'):$r('app.media.product1'),
        txt:'陶瓷产品'+i,
        price:15
      })
    }
  }
  @Builder
  getItem(src:ResourceStr,txt:string,price:number){
    Column(){
      Image(src).width('100%').borderRadius(5)
      Text(txt).fontSize(15).fontWeight(FontWeight.Bold).margin({top:10})
      Text(){
        Span('¥ ')
          .fontColor(Color.Red)
          .fontSize(10)
        Span(price?.toFixed(2))
          .fontColor(Color.Red)
          .fontWeight(FontWeight.Bold)
      }
    }.width('100%')
  }
  build() {
    Stack(){
      Column(){
        WaterFlow(){
          ForEach(this.datas,(item:Data)=>{
            FlowItem(){
              this.getItem(item.src,item.txt,item.price)
            }
          },(item:Data)=>JSON.stringify(item))
        }.columnsTemplate(this.template)
        .rowsGap(10)
        .columnsGap(10)
        .nestedScroll({
          scrollForward: NestedScrollMode.PARENT_FIRST,
          scrollBackward:NestedScrollMode.SELF_FIRST
        })
      }
      .height('100%')
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .padding(5)
    .alignContent(Alignment.Bottom)
  }
}
image.gif

该组件用于展示一系列产品相关的信息,包括产品图片、名称以及价格等内容。组件内部实现了数据初始化、单个产品项的构建以及整体产品列表的布局展示等功能。

 

1. 接口定义

interface Data{
  src:ResourceStr
  txt:string
  price:number
}
image.gif

定义了一个名为 Data 的接口,用于规范表示产品数据的结构。其中包含三个属性:

  • src:类型为 ResourceStr,推测是用于引用资源(可能是图片资源等)的一种特定类型,用于指定产品对应的图片资源。
  • txt:字符串类型,用于存放产品的文字描述,比如产品名称等相关信息。
  • price:数值类型,用于表示产品的价格信息。

2.HomeProduct 组件定义

@Component
export default struct HomeProduct{
  //...
}
image.gif 使用 @Component 装饰器将 HomeProduct 结构体标记为一个组件,意味着它可作为独立的 UI 单元参与界面构建,其界面呈现由内部的 build 方法来定义,同时还有相关的状态管理和生命周期方法等。

3. 组件状态定义

@State datas: Data[] = []
@State template: string = '1fr 1fr'
image.gif
  • @State 装饰器用于定义组件的响应式状态变量。
  • datas:是一个 Data 类型的数组,初始化为空数组,用于存储要展示的多个产品的数据信息,后续会在组件的生命周期方法中进行数据填充。
  • template:是一个字符串类型的状态变量,初始值为 '1fr 1fr',从后续使用情况看,可能用于控制产品列表布局中列的模板(比如在 WaterFlow 布局里控制列的分布比例等情况)。

4.aboutToAppear 生命周期方法

aboutToAppear(): void {
  for(let i=1;i<=20;i++){
    this.datas.push({
      src:i%2==0? $r('app.media.product7'):$r('app.media.product1'),
      txt:'陶瓷产品'+i,
      price:15
    })
  }
}
image.gif

aboutToAppear 方法通常在组件即将显示在界面上时被触发(是组件生命周期的一部分)。

  • 在这个方法中,通过一个循环(从 120)往 datas 数组中添加模拟的产品数据。对于每个产品:
  • src 属性根据索引 i 的奇偶性来选择不同的图片资源(通过 $r('app.media.productX') 方式引用,具体资源加载机制依赖框架实现),这里简单地实现了交替使用两种图片资源来模拟不同产品的图片。
  • txt 属性设置为 '陶瓷产品' 加上当前的索引值,形成一个简单的产品名称描述。
  • price 属性统一设置为 15,模拟产品价格。

5.getItem 构建函数

@Builder
getItem(src:ResourceStr,txt:string,price:number){
  Column(){
    Image(src).width('100%').borderRadius(5)
    Text(txt).fontSize(15).fontWeight(FontWeight.Bold).margin({top:10})
    Text(){
      Span('¥ ')
       .fontColor(Color.Red)
       .fontSize(10)
      Span(price?.toFixed(2))
       .fontColor(Color.Red)
       .fontWeight(FontWeight.Bold)
    }
  }.width('100%')
}
image.gif

使用 @Builder 装饰器,表明这是一个用于构建 UI 片段的函数,它接收产品的相关数据(图片资源、名称、价格)作为参数来构建一个产品项的 UI 结构。

  • 内部通过 Column(列布局)来组织产品项的内容:
  • 首先是一个 Image 组件,使用传入的 src 参数来显示产品图片,设置宽度占满父容器并且添加了圆角样式(borderRadius(5)),使其显示更美观。
  • 接着是一个 Text 组件,用于显示产品的名称(传入的 txt 参数),设置了字体大小为 15,加粗字体(FontWeight.Bold)以及顶部有一定的外边距,使其与图片有间隔。
  • 然后又是一个 Text 组件,内部使用了 Span 来分别构建价格显示的两部分(货币符号和具体价格数值),货币符号部分设置了红色字体颜色、较小的字体大小,价格数值部分同样设置为红色字体颜色并且加粗字体,整体用于清晰美观地展示产品价格信息,并且整个列布局宽度占满父容器。

6.build 方法(核心界面构建逻辑)

build() {
  Stack(){
    Column(){
      WaterFlow(){
        ForEach(this.datas,(item:Data)=>{
          FlowItem(){
            this.getItem(item.src,item.txt,item.price)
          }
        },(item:Data)=>JSON.stringify(item))
      }.columnsTemplate(this.template)
     .rowsGap(10)
     .columnsGap(10)
     .nestedScroll({
        scrollForward: NestedScrollMode.PARENT_FIRST,
        scrollBackward:NestedScrollMode.SELF_FIRST
      })
    }
   .height('100%')
   .width('100%')
  }
 .width('100%')
 .height('100%')
 .padding(5)
 .alignContent(Alignment.Bottom)
}
image.gif

整个界面构建基于 Stack(层叠布局),里面包含一个 Column(列布局),用于组织产品列表等内容。

  • Column 内部使用了 WaterFlow(瀑布流布局)组件来展示产品列表:
  • 通过 ForEach 循环遍历 datas 数组中的每个产品数据项(Data 类型),对于每个数据项,在 FlowItem 中调用 getItem 函数来构建对应的产品项 UI 结构,从而实现根据数据动态生成多个产品展示项的效果。同时传递了一个用于唯一标识每个数据项的函数(这里简单地将数据项转换为 JSON 字符串来作为标识)。
  • WaterFlow 组件设置了 columnsTemplatethis.template,即根据前面定义的 template 状态变量来确定列的布局模板(比如列的宽度分配比例等情况),设置了行与列之间的间隔(rowsGap(10)columnsGap(10)),并且配置了嵌套滚动相关的模式(nestedScroll),用于处理滚动行为,比如规定了向前滚动(scrollForward)和向后滚动(scrollBackward)时采用的滚动模式(分别是 PARENT_FIRSTSELF_FIRST,涉及到和父容器滚动交互等情况)。
  • 外层的 Column 设置了高度和宽度占满父容器,而最外层的 Stack 同样设置了宽度和高度占满父容器,并且添加了一定的内边距(padding(5))以及内容对齐方式为底部对齐(alignContent(Alignment.Bottom)),整体构建出产品列表展示的完整界面布局结构。

四、在src/main/ets/pages/Index.ets文件中,定义Index 组件作为应用的入口组件。

实现代码如下:

import font from '@ohos.font'
import Home from "../components/Home"
@Entry
@Component
struct Index {
  aboutToAppear(): void {
    font.registerFont({
      familyName: 'myFont',
      familySrc: '/fonts/iconfont.ttf'
    })
  }
  @State selectedIndex: number = 0
  @Builder
  myBuilder(itemIndex: number, title: string, ico: string) {
    Column() {
      Text(ico)
        .width(30)
        .height(30)
        .fontFamily('myFont')
        .fontSize(30)
        .textAlign(TextAlign.Center)
        .fontColor(itemIndex == this.selectedIndex ?'#fa2a83' : Color.Black)
      Text(title)
        .fontColor(itemIndex == this.selectedIndex ? '#fa2a83' : Color.Black)
    }
  }
  build() {
    Tabs({ barPosition: BarPosition.End }) {
      TabContent() {
        Home()
      }
      .tabBar(this.myBuilder(0, '首页', '\ue64c'))
      TabContent() {
        Text("分类内容")
      }
      .tabBar(this.myBuilder(1, '分类', '\ue626'))
      TabContent() {
        Text("购物内容")
      }
      .tabBar(this.myBuilder(2, '购物', '\ue604'))
      TabContent() {
        Text("我的内容")
      }
      .tabBar(this.myBuilder(3, '我的', '\ue61e'))
    }
    .onChange((index: number) => {
      this.selectedIndex = index
    })
  }
}
image.gif

定义了一个名为 Index 的组件,它作为应用的入口组件(通过 @Entry 装饰器标识),构建了一个带有底部导航栏(通过 Tabs 组件实现)的界面结构,导航栏包含多个选项卡,点击不同选项卡可切换显示不同的内容页面,同时在切换时还实现了相应的状态更新及样式变化等功能。

1. 导入模块

import font from '@ohos.font'
import Home from "../components/Home"
image.gif

       从 @ohos.font 导入了 font 模块,大概率用于字体相关操作,后续代码中会使用它来注册自定义字体,以满足界面中特定字体显示需求。

       导入了自定义的 Home 组件,从代码结构推测,Home 组件应该是展示应用首页相关内容的一个独立组件,这里会被整合到 Tabs 所构建的多页面切换体系当中。

2.Index 组件定义与入口标识

@Entry
@Component
struct Index {
  //...
}
image.gif        使用 @Entry 装饰器将 Index 结构体标记为整个应用的入口组件,意味着应用启动时会首先渲染这个组件所定义的界面内容。同时, @Component 装饰器表明它是一个符合组件规范的 UI 单元,其界面呈现由内部的 build 方法来确定。

3.aboutToAppear 生命周期方法

aboutToAppear(): void {
  font.registerFont({
    familyName: 'myFont',
    familySrc: '/fonts/iconfont.ttf'
  })
}
image.gif

       这是组件生命周期中在即将显示时触发的方法。在这里调用了 font 模块的 registerFont 方法,注册了一个名为 myFont 的自定义字体,字体文件来源指定为 /fonts/iconfont.ttf。注册这个字体后,后续就可以在界面中使用该字体来展示特定的文本样式了,例如显示一些自定义的图标字体等内容。

4. 组件状态定义

@State selectedIndex: number = 0
image.gif        通过 @State 装饰器定义了一个名为 selectedIndex 的响应式状态变量,其初始值设置为 0。这个变量用于记录当前选中的选项卡索引,在后续选项卡切换以及相应 UI 样式更新时会起到关键作用,因为界面上需要根据当前选中的选项卡来展示不同的样式效果,比如改变图标和文字的颜色等。

5.myBuilder 构建函数

@Builder
myBuilder(itemIndex: number, title: string, ico: string) {
  Column() {
    Text(ico)
     .width(30)
     .height(30)
     .fontFamily('myFont')
     .fontSize(30)
     .textAlign(TextAlign.Center)
     .fontColor(itemIndex == this.selectedIndex?'#fa2a83' : Color.Black)
    Text(title)
     .fontColor(itemIndex == this.selectedIndex? '#fa2a83' : Color.Black)
  }
}
image.gif

       使用 @Builder 装饰器表明这是一个用于构建 UI 片段的函数。

       该函数接收三个参数:

  • itemIndex(表示当前选项卡的索引)
  • title(选项卡对应的标题文本)
  • ico(用于显示的图标对应的字符编码,通常结合自定义字体来显示图标样式),并基于这些参数构建一个包含图标和标题文本的 Column(列布局)UI 结构。

       对于图标对应的 Text 组件:

       设置了固定的宽度和高度(width(30)height(30)),指定使用之前注册的 myFont 字体,字体大小为 30,文本居中对齐(textAlign(TextAlign.Center)),并且关键的是,根据当前选项卡索引(itemIndex)与记录选中索引的 selectedIndex 是否相等,来动态设置字体颜色,如果相等则显示为 #fa2a83 颜色(可能是一种突出显示的颜色,用于标识选中状态),否则显示为黑色(普通未选中状态的颜色)。

       对于标题文本对应的 Text 组件,同样根据索引是否相等来动态设置字体颜色,以实现选中和未选中状态下文字颜色的不同显示效果,整体通过这个函数构建出每个选项卡对应的底部导航栏子项的展示样式。

6.build 方法(核心界面构建逻辑)

build() {
  Tabs({ barPosition: BarPosition.End }) {
    TabContent() {
      Home()
    }
   .tabBar(this.myBuilder(0, '首页', '\ue64c'))
    TabContent() {
      Text("分类内容")
    }
   .tabBar(this.myBuilder(1, '分类', '\ue626'))
    TabContent() {
      Text("购物内容")
    }
   .tabBar(this.myBuilder(2, '购物', '\ue604'))
    TabContent() {
      Text("我的内容")
    }
   .tabBar(this.myBuilder(3, '我的', '\ue61e'))
  }
.onChange((index: number) => {
    this.selectedIndex = index
  })
}
image.gif

  整个界面构建基于 Tabs 组件,用于创建多选项卡切换的布局效果,并且通过 { barPosition: BarPosition.End } 参数设置选项卡栏的位置为底部(BarPosition.End 表示底部位置,还有其他可能的位置选项如顶部等)。

       在 Tabs 组件内部,有多个 TabContent 子组件,每个 TabContent 对应一个选项卡的内容页面:

  • 第一个 TabContent 中放置了之前导入的 Home 组件,作为应用的首页内容展示,并且通过 .tabBar(this.myBuilder(0, '首页', '\ue64c')) 调用 myBuilder 函数来构建对应的底部导航栏子项样式,传入索引 0、标题 '首页' 以及对应的图标字符编码 '\ue64c',用于显示首页对应的图标和文字样式,并且能根据选中状态改变颜色。
  • 后续的几个 TabContent 结构类似,分别展示简单的文本内容(如 '分类内容''购物内容''我的内容' 等),同样通过调用 myBuilder 函数传入不同的参数来构建各自对应的底部导航栏子项样式,每个选项卡都有自己对应的图标和文字,以及相应的选中 / 未选中状态样式变化。
  • 最后,通过 .onChange((index: number) => { this.selectedIndex = index })Tabs 组件注册了一个选项卡切换的回调函数,当用户点击切换选项卡时,会触发这个回调,将当前选中的选项卡索引更新到 selectedIndex 这个状态变量中,这样就能实时根据选中情况更新界面上相关元素(如底部导航栏图标和文字颜色)的样式了,保证 UI 展示与用户操作的一致性。

👋实验小结

本次实验成功构建了具有首页及底部导航栏多页面切换功能的应用界面。首页包含搜索栏、轮播图、分类网格与推荐好物列表等丰富内容,底部导航栏切换流畅且能实现选中状态样式更新。在技术层面,深入理解并运用组件化开发提升代码可维护性与复用性,像 Home 和 HomeProduct 组件各司其职;通过 @ohos.font 模块注册自定义字体用于图标展示,增强界面特色;灵活采用多种布局组件构建复杂结构,如 Column、Row 等布局的巧妙嵌套;借助响应式状态变量与数据循环达成数据驱动 UI,确保数据与界面显示一致。实验中遇到字体资源加载、布局适配及数据与 UI 同步等问题,均通过仔细检查路径、优化布局属性设置及遵循响应式编程最佳实践得以解决。此次实验收获颇丰,不仅熟练掌握组件化、布局构建与数据驱动等关键技术,还提升了问题解决能力,为后续应用开发积累了宝贵经验。

image.gif

目录
相关文章
|
6天前
|
供应链 监控 安全
对话|企业如何构建更完善的容器供应链安全防护体系
随着云计算和DevOps的兴起,容器技术和自动化在软件开发中扮演着愈发重要的角色,但也带来了新的安全挑战。阿里云针对这些挑战,组织了一场关于云上安全的深度访谈,邀请了内部专家穆寰、匡大虎和黄竹刚,深入探讨了容器安全与软件供应链安全的关系,分析了当前的安全隐患及应对策略,并介绍了阿里云提供的安全解决方案,包括容器镜像服务ACR、容器服务ACK、网格服务ASM等,旨在帮助企业构建涵盖整个软件开发生命周期的安全防护体系。通过加强基础设施安全性、技术创新以及倡导协同安全理念,阿里云致力于与客户共同建设更加安全可靠的软件供应链环境。
131532 10
|
14天前
|
弹性计算 人工智能 安全
对话 | ECS如何构筑企业上云的第一道安全防线
随着中小企业加速上云,数据泄露、网络攻击等安全威胁日益严重。阿里云推出深度访谈栏目,汇聚产品技术专家,探讨云上安全问题及应对策略。首期节目聚焦ECS安全性,提出三道防线:数据安全、网络安全和身份认证与权限管理,确保用户在云端的数据主权和业务稳定。此外,阿里云还推出了“ECS 99套餐”,以高性价比提供全面的安全保障,帮助中小企业安全上云。
201925 14
对话 | ECS如何构筑企业上云的第一道安全防线
|
3天前
|
供应链 监控 安全
|
6天前
|
SQL 安全 前端开发
预编译为什么能防止SQL注入?
SQL注入是Web应用中常见的安全威胁,攻击者通过构造恶意输入执行未授权的SQL命令。预编译语句(Prepared Statements)是一种有效防御手段,它将SQL代码与数据分离,确保用户输入不会被解释为SQL代码的一部分。本文详细介绍了SQL注入的危害、预编译语句的工作机制,并结合实际案例和多语言代码示例,展示了如何使用预编译语句防止SQL注入,强调了其在提升安全性和性能方面的重要性。
|
9天前
|
搜索推荐 物联网 PyTorch
Qwen2.5-7B-Instruct Lora 微调
本教程介绍如何基于Transformers和PEFT框架对Qwen2.5-7B-Instruct模型进行LoRA微调。
417 34
Qwen2.5-7B-Instruct Lora 微调
|
1月前
|
人工智能 自然语言处理 前端开发
从0开始打造一款APP:前端+搭建本机服务,定制暖冬卫衣先到先得
通义灵码携手科技博主@玺哥超carry 打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用 AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。
9940 29
|
3天前
|
机器学习/深度学习 存储 人工智能
【科普向】我们所说的AI模型训练到底在训练什么?
人工智能(AI)模型训练类似于厨师通过反复实践来掌握烹饪技巧。它通过大量数据输入,自动优化内部参数(如神经网络中的权重和偏置),以最小化预测误差或损失函数,使模型在面对新数据时更加准确。训练过程包括前向传播、计算损失、反向传播和更新权重等步骤,最终生成权重文件保存模型参数,用于后续的应用和部署。理解生物神经网络的工作原理为人工神经网络的设计提供了灵感,后者广泛应用于图像识别、自然语言处理等领域。
|
2天前
|
人工智能 算法 搜索推荐
阿里云百炼xWaytoAGI共学课开课:手把手学AI,大咖带你从零搭建AI应用
阿里云百炼xWaytoAGI共学课开课啦。大咖带你从零搭建AI应用,玩转阿里云百炼大模型平台。3天课程,涵盖企业级文本知识库案例、多模态交互应用实操等,适合有开发经验的企业或独立开发者。直播时间:2025年1月7日-9日 20:00,地点:阿里云/WaytoAGI微信视频号。参与课程可赢取定制保温杯、雨伞及磁吸充电宝等奖品。欢迎加入钉钉共学群(群号:101765012406),与百万开发者共学、共享、共实践!
|
15天前
|
机器学习/深度学习 人工智能 安全
通义视觉推理大模型QVQ-72B-preview重磅上线
Qwen团队推出了新成员QVQ-72B-preview,这是一个专注于提升视觉推理能力的实验性研究模型。提升了视觉表示的效率和准确性。它在多模态评测集如MMMU、MathVista和MathVision上表现出色,尤其在数学推理任务中取得了显著进步。尽管如此,该模型仍存在一些局限性,仍在学习和完善中。

热门文章

最新文章

下一篇
开通oss服务