一.实行配景
本次项目为华为云鸿蒙应用入门级开发者认证的实行项目,借此来巩固对ArkTS的学习。
实行源地点
开发者云实行_云实行KooLabs_在线实行_上云实践_云盘算实行_AI实行_华为云官方实行平台-华为云
实行目标
本实行一共需要完成以下三个部门的使命:本实行将模仿制作华为商城应用商品展示界,该款应用需要实现如下功能结果。
- 用户切换左侧导航的商品类别,右侧滚动到对应的内容。
- 滚动右侧详细商品的内容,左侧会切换对应的商品类别导航。
实行结果
二.实行详细解析
GoodsModel.ets
- export default class GoodsModel {
- CommodityId: number; // 所属商品类别id
- goodsId: number; // 商品id
- goodsName: string; // 商品名称
- imageUrl: Resource; // 商品展示图片
- price: number; // 商品价格
- // 使用构造函数创建商品数据模型
- constructor(CommodityId: number, goodsId: number, goodsName: string, imageUrl: Resource, price: number) {
- this.CommodityId = CommodityId;
- this.goodsId = goodsId;
- this.goodsName = goodsName;
- this.imageUrl = imageUrl;
- this.price = price;
- }
- }
复制代码 这里是使用ArkTS语法创建了一个GoodsModel类,constructor作为其构造器,用来记载商品品类,使用并默认导出这个类。
- export:用于导出模块中的函数、类、接口或变量。
- 一个模块可以有多个 export。
- 导出时,你必须指定导出的名称,这些名称在导入时必须划一。
- 导入时,需要使用花括号 {} 来指定导入的项,并且可以重命名导入的项。
- export default:用于导出模块中的默认值。这个默认值可以是函数、类、对象等。
- 每个模块只能有一个默认导出。
- 导出时,不需要指定名称。
- 导入时,不需要花括号,并且可以为导入的项指定任何名称。
NavigationModel.ets
此文件中也是创建了一个NavigationModel的默认导出类。记载商品分类。
LinkDataModel.ets
创建了一个LinkDataModel联合类。将商品与分类联合起来。
NavigationViewModel.ets
创建NavigationViewModel类,获取商品分类列表数据,返回商品类别的ID以及对应连接的详细商品信息列表。
- //导入先前创建好的三个数据模型
- //TODO
- import NavigationModel from './NavigationModel';
- import GoodsModel from './GoodsModel';
- import LinkDataModel from './LinkDataModel';
- class NavigationViewModel {
- /**
- * 获取商品分类列表数据
- *
- * 返回 Array<商品分类模型> 商品类别的ID 以及 对应连接的具体商品信息列表 linkDataList
- */
- getLinkData(): Array<NavigationModel> {
- let linkDataList: Array<NavigationModel> = [];
- let superId: number = 0;
- //TODO
- LINK_DATA.forEach((item: LinkDataModel) => {
- if (superId !== item.superId) {
- // 如果该分类id不存在,则添加此商品分类模型中
- let GoodsItem: NavigationModel = new NavigationModel(item.superId, item.superName, []);
- linkDataList.push(GoodsItem);
- }
- // 添加具体商品信息模型
- let GoodsItem: GoodsModel = new GoodsModel(superId, item.id, item.GoodsName, item.imageUrl, item.price);
- linkDataList[linkDataList.length-1].GoodsList.push(GoodsItem);
- superId = item.superId;
- });
- return linkDataList;
- }
- }
- let navigationViewModel = new NavigationViewModel();
- export default navigationViewModel as NavigationViewModel;
- // 创建所链接具体数据
- const LINK_DATA: LinkDataModel[] = [
- new LinkDataModel(1, '热门商品', 1, 'HUAWEI P60 Art', $r('app.media.ic_img_1'), 0),
- new LinkDataModel(1, '热门商品', 2, 'HUAWEI P60 Pro', $r('app.media.ic_img_2'), 5000),
- new LinkDataModel(1, '热门商品', 3, 'HUAWEI Mate 50 Pro 4G', $r('app.media.ic_img_3'), 5000),
- ...,
- new LinkDataModel(6, '智慧屏', 36, '华为智慧屏 V98', $r('app.media.ic_img_10'),3000),
- new LinkDataModel(6, '智慧屏', 37, '华为智慧屏 V85', $r('app.media.ic_img_11'), 5000),
- ]
复制代码 CommodityItem.ets
创建导航栏组件,显示右侧商品导航栏。
- // 引入先前构建好的常量类别,框架代码中已提供
- import Constants from '../common/constants/Constants';
- @Component
- export default struct CommodityItem {
- CommodityName?: string;
- @Prop isSelected: boolean = false;
- onClickAction = () => {
- // TODO
- }
- build() {
- Text(this.CommodityName)
- .fontSize($r('app.float.normal_font_size'))
- .fontColor(this.isSelected ? $r('app.color.base_font_color') : $r('app.color.normal_font_color'))
- .fontFamily(this.isSelected ? $r('app.string.hei_ti_medium') : $r('app.string.hei_ti'))
- .fontWeight(this.isSelected ? Constants.TITLE_FONT_WEIGHT : Constants.LABEL_FONT_WEIGHT)
- .textAlign(TextAlign.Center)
- .backgroundColor(this.isSelected ? $r('app.color.base_background') : '')
- .width(Constants.FULL_PERCENT)
- .height($r('app.float.classify_item_height'))
- .onClick(this.onClickAction)
- }
- }
复制代码 GoodsItem.ets
创建商品组件,显示商品。
- // 引入先前构建好的常量类别,框架代码中已提供
- import Constants from '../common/constants/Constants';
- // 引入之前创建好的具体商品数据模型
- //TODO
- import GoodsModel from '../viewmodel/GoodsModel';
- // 构建具体商品信息子项
- @Component
- export default struct GoodsItem {
- //TODO
- @Prop itemStr:string = '';
- item?:GoodsModel;
- // 在创建自定义组件的新实例后,在执行其build()函数之前执行。
- //TODO
- aboutToAppear() {
- this.item = JSON.parse(this.itemStr);
- }
- build() {
- Row() {
- Row(){
- //TODO
- Image(this.item !== undefined ? this.item?.imageUrl : '')
- .height(Constants.FULL_PERCENT)
- .aspectRatio(1)
- }.height(70)
- .width(70)
- Column() {
- //TODO
- Text( )
- .fontSize($r('app.float.normal_font_size'))
- .fontColor($r('app.color.base_font_color'))
- .fontFamily($r('app.string.hei_ti_medium'))
- .maxLines(Constants.TITLE_LINE_NUMBER)
- .textOverflow({ overflow: TextOverflow.Clip })
- .lineHeight($r('app.float.title_line_height'))
- .width(Constants.FULL_PERCENT)
- // 当商品的价格等于0时,这显示免费,否则显示具体价格。
- //TODO
- Text(this.item?.price === 0 ? $r('app.string.free_price') : $r('app.string.price_str', this.item?.price))
- .fontSize($r('app.float.header_font_size'))
- .fontColor($r('app.color.price_color'))
- .fontFamily($r('app.string.hei_ti_medium'))
- }
- .padding($r('app.float.course_item_padding'))
- .layoutWeight(1)
- .alignItems(HorizontalAlign.Start)
- .justifyContent(FlexAlign.SpaceBetween)
- .height(Constants.FULL_PERCENT)
- }
- .clip(true)
- .borderRadius($r('app.float.normal_border_radius'))
- .backgroundColor($r('app.color.start_window_background'))
- .width('100%')
- .height($r('app.float.course_item_height'))
- }
- }
复制代码
MainPage.ets
此文件为程序入口页面,将上述所有文件调度起来形成页面。
- aboutToAppear()主要作加载过程,setTimeout()在延Constants.LOADING_DURATION时间后被实行,() => { ... }在这里本质上是一个回调函数。
- @Builder允许在自界说组件内界说一个或多个自界说构建函数,同时只允许组内调用。
- build()中还使用了列表List()组件,在其中通过ForEach遍历生成了多个商品项。
- // 导入数据模型以及创建好的自定义组件
- import Constants from '../common/constants/Constants';
- //TODO
- import NavigationModel from '../viewmodel/NavigationModel';
- import GoodsModel from '../viewmodel/GoodsModel';
- import GoodsItem from '../view/GoodsItem';
- import CommodityItem from '../view/CommodityItem';
- import NavigationViewModel from '../viewmodel/NavigationViewModel';
- @Entry
- @Component
- struct IndexPage {
- //TODO
- @State currentCommodity: number = 0; // 被选中的左侧商品分类序号
- @State requestSuccess: boolean = false; // 是否加载成功
- private CommodityList: Array<NavigationModel> = []; // 左侧商品分类列表
- private CommodityScroller: Scroller = new Scroller(); // 左侧商品类别导航滚动条
- private scroller: Scroller = new Scroller(); // 右侧商品信息导航条
- // 搜索框
- @State changeValue: string = ''
- @State submitValue: string = ''
- controller: SearchController = new SearchController() // 搜索框控制器
- // 生命周期函数
- //TODO
- aboutToAppear() {
- // 在应用加载页面上显示“正在加载中”的文字提示
- setTimeout(() => {
- this.CommodityList = NavigationViewModel.getLinkData();
- this.requestSuccess = true;
- }, Constants.LOADING_DURATION);
- }
- // @Builder可以在自定义组件内定义一个或多个自定义构建函数,这里创建了左侧导航栏商品分类被选中时的样式。
- // 自定义构建函数可以在所属组件的build方法和其他自定义构建函数中调用,但不允许在组件外调用。
- // TODO
- @Builder CommodityHeader(CommodityName: string) {
- Row() {
- Text(CommodityName)
- .fontSize($r('app.float.header_font_size'))
- .fontColor($r('app.color.base_font_color'))
- .fontFamily($r('app.string.hei_ti_medium'))
- .fontWeight(Constants.TITLE_FONT_WEIGHT)
- }
- .padding({ left: $r('app.float.item_padding_left') })
- .height($r('app.float.classify_item_height'))
- .width(Constants.FULL_PERCENT)
- .backgroundColor($r('app.color.base_background'))
- }
- // 定义当切换样式改变函数
- //TODO
- CommodityChangeAction(index: number, isCommodity: boolean) {
- if (this.currentCommodity !== index) {
- // 更改商品分类状态
- this.currentCommodity = index;
- if (isCommodity) {
- // 滚动右侧商品滚动条,滚动到选中区域
- this.scroller.scrollToIndex(index);
- } else {
- // 滚动左侧商品导航条,滚动到选中区域
- this.CommodityScroller.scrollToIndex(index);
- }
- }
- }
- build() {
- Column(){
- Row(){
- // 搜索框
- //TODO
- Search({ value: this.changeValue, placeholder: '手表', controller: this.controller })
- .searchButton('搜索')
- .width(350)
- .height(40)
- .backgroundColor('#F5F5F5')
- .placeholderColor(Color.Grey)
- .placeholderFont({ size: 14, weight: 400 })
- .textFont({ size: 14, weight: 400 })
- .onSubmit((value: string) => {
- this.submitValue = value
- })
- .onChange((value: string) => {
- this.changeValue = value
- })
- }.backgroundColor($r('app.color.classify_background'))
- // 界面整体使用Row组件实现横向布局,分为左右两部分。
- // 均使用List组件实现对导航和内容的数据展示,导航部分固定宽度,内容部分自适应屏幕剩余宽度并用ListItemGroup完成每个导航下的内容布局。
- Row() {
- if (this.requestSuccess) {
- List({ scroller: this.CommodityScroller }) {
- // 循环商品分类导航列表
- //TODO
- ForEach(this.CommodityList, (item: NavigationModel, index?: number) => {
- ListItem() {
- CommodityItem({
- CommodityName: item.goodsName,
- isSelected: this.currentCommodity === index,
- onClickAction: () => {
- if (index !== undefined) {
- // 调用切换函数
- this.CommodityChangeAction(index, true);
- }
- }
- })
- }
- }, (item: NavigationModel) => item.goodsName + this.currentCommodity)
- }
- .height(Constants.FULL_PERCENT)
- .width($r('app.float.classify_item_width'))
- .backgroundColor($r('app.color.classify_background'))
- .scrollBar(BarState.Off)
- // 循环具体商品信息列表
- List({ scroller: this.scroller }) {
- //TODO
- ForEach(this.CommodityList, (CommodityItem: NavigationModel) => {
- ListItemGroup({
- header: this.CommodityHeader(CommodityItem.goodsName),
- space: Constants.GOODS_ITEM_PADDING
- }) {
- ForEach(CommodityItem.GoodsList, (courseItem: GoodsModel) => {
- ListItem() {
- GoodsItem({ itemStr: JSON.stringify(courseItem) })
- }
- }, (courseItem: GoodsModel) => `${courseItem.goodsId}`)
- }
- }, (item: NavigationModel) => `${item.commodityId}`)
- }
- .padding({ left: $r('app.float.item_padding_left'), right: $r('app.float.course_item_padding') })
- .sticky(StickyStyle.Header)
- .layoutWeight(1)
- .edgeEffect(EdgeEffect.None)
- .onScrollIndex((start: number) => this.CommodityChangeAction(start, false))
- } else {
- Text($r('app.string.loading'))
- .fontFamily($r('app.string.hei_ti_medium'))
- .textAlign(TextAlign.Center)
- .height(Constants.FULL_PERCENT)
- .width(Constants.FULL_PERCENT)
- }
- }
- }.backgroundColor($r('app.color.base_background'))
- }
- }
复制代码 三.总结
笔者在此只是大概对某些地方做了粗略的总结与表明,对于别的有疑问的地方笔者可自行到华为官网搜刮查询。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |