大家好,我是中原工学院的张博士,学习了一段时间鸿蒙课,做了一个功能简单的购物app,以下是我的鸿蒙结课报告,希望对学习的鸿蒙开发的童靴有帮助。

一、引言

在过去的几个月中,我参加了一门关于鸿蒙开发的课程,并完成了一个功能简单的购物App的开发。通过这个项目,我对鸿蒙开发平台有了更深入的了解,并提升了自己的编程技能和项目开发能力。在本报告中,我将分享我的项目经历和收获,以及对鸿蒙开发的总结和展望。

二、项目介绍

我的项目是一个功能简单的购物App,旨在为用户提供方便的在线购物体验。主要功能包括登录、功能区,商品列表展示、个人设置界面等。通过使用鸿蒙开发平台的功能和特性,我成功地实现了这些功能,并对用户界面进行了简洁而美观的设计。

三、开发过程

1.环境搭建

软件要求

DevEco Studio版本:DevEco Studio 3.1 Release。

HarmonyOS SDK版本:API version 9。

硬件要求

设备类型:华为手机或运行在DevEco Studio上的华为手机设备模拟器。

HarmonyOS系统:3.1.0 Developer Release。

环境搭建

  1. 安装DevEco Studio,详情请参考下载和安装软件
  2. 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
  • 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
  • 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境
  1. 开发者可以参考以下链接,完成设备调试的相关配置:

2.代码结构

『江鸟中原』鸿蒙——简易商城搭建_ArkUI

3.用到的组件

Image、Text、TextInput、Button、LoadingProgress、Tabs、Swiper、Grid、Toggle等

4.登录页面LoginPage.ets实现

页面使用Column容器组件布局,由Image、Text、TextInput、Button、LoadingProgress等基础组件构成。

1.获取用户账户与密码输入框,给TextInput设置onChange事件,在onChange事件里面实时获取用户输入的文本信息,并用自定义的线分割。具体主要代码实现:

  TextInput({ placeholder: $r('app.string.account') })
        .maxLength(CommonConstants.INPUT_ACCOUNT_LENGTH)
        .type(InputType.Number)
        .inputStyle()
        .onChange((value: string) => {
          this.account = value;
        })
      Line().lineStyle()

      TextInput({ placeholder: $r('app.string.password') })
        .maxLength(CommonConstants.INPUT_PASSWORD_LENGTH)
        .type(InputType.Password)
        .inputStyle()
        .onChange((value: string) => {
          this.password = value;
        })
      Line().lineStyle()

2.给登录按钮设置点击事件,判断用户是否输入为空,空则提示输入不能为空,否则登录成功,使用router.replaceUrl()方法实现页面跳转实现跳转到商城首页,后期会继续改进判断账号密码是否正确。具体主要代码实现:

 Button($r('app.string.login'), { type: ButtonType.Capsule })
        .width(CommonConstants.BUTTON_WIDTH)
        .height($r('app.float.login_button_height'))
        .fontSize($r('app.float.normal_text_size'))
        .fontWeight(FontWeight.Medium)
        .backgroundColor($r('app.color.login_button_color'))
        .margin({ top: $r('app.float.login_button_margin_top'), bottom: $r('app.float.login_button_margin_bottom') })
        .onClick(() => {
          this.login();
        })
   login(): void {
    if (this.account === '' || this.password === '') {
      prompt.showToast({
        message: $r('app.string.input_empty_tips')
      })
    } else {
      this.isShowProgress = true;
      if (this.timeOutId === -1) {
        this.timeOutId = setTimeout(() => {
          this.isShowProgress = false;
          this.timeOutId = -1;
          router.replaceUrl({ url: 'pages/MainPage' });
        }, CommonConstants.LOGIN_DELAY_TIME);
      }
    }
  }

3.给登录按钮绑定onClick事件,调用login方法模拟登录。定义变量isShowProgress结合条件渲染if用来控制LoadingProgress的显示和隐藏。当用户点击按钮时设置isShowProgress为true,即显示LoadingProgress;使用定时器setTimeout设置isShowProgress 2秒后为false,即隐藏LoadingProgress,然后执行跳转到首页的逻辑。具体主要代码实现:

aboutToDisappear() {
    clearTimeout(this.timeOutId);
    this.timeOutId = -1;
  }
  if (this.isShowProgress) {
        LoadingProgress()
          .color($r('app.color.loading_color'))
          .width($r('app.float.login_progress_size'))
          .height($r('app.float.login_progress_size'))
          .margin({ top: $r('app.float.login_progress_margin_top') })
      }

以上代码实现登录界面,效果如下:


『江鸟中原』鸿蒙——简易商城搭建_HarmonyOS_02

5.主界面MainViewModel.ets的实现

1.多处图片和文字的组合,设置ItemData类,主界面由两个tab页组成,使用Tabs组件来实现,提取tabBar的公共样式,同时设置TabContent和Tabs的backgroundColor来实现底部tabBar栏背景色突出的效果。具体主要代码实现:

  build() {
    Tabs({
      barPosition: BarPosition.End,
      controller: this.tabsController
    }) {
      TabContent() {
        Home()
      }
      .padding({ left: $r('app.float.mainPage_padding'), right: $r('app.float.mainPage_padding') })
      .backgroundColor($r('app.color.mainPage_backgroundColor'))
      .tabBar(this.TabBuilder(CommonConstants.HOME_TITLE, CommonConstants.HOME_TAB_INDEX,
        $r('app.media.home_selected'), $r('app.media.home_normal')))

      TabContent() {
        Setting()
      }
      .padding({ left: $r('app.float.mainPage_padding'), right: $r('app.float.mainPage_padding') })
      .backgroundColor($r('app.color.mainPage_backgroundColor'))
      .tabBar(this.TabBuilder(CommonConstants.MINE_TITLE, CommonConstants.MINE_TAB_INDEX,
        $r('app.media.mine_selected'), $r('app.media.mine_normal')))
    }
    .width(CommonConstants.FULL_PARENT)
    .backgroundColor(Color.White)
    .barHeight($r('app.float.mainPage_barHeight'))
    .barMode(BarMode.Fixed)
    .onChange((index: number) => {
      this.currentIndex = index;
    })
  }

2.首页First.ets的实现

“首页”由三部分内容组成分别是轮播图、2*4栅格图、列表。首先使用Swiper组件实现商品轮播图展示,无需设置图片大小。然后使用Grid组件实现24功能栅格图,最后使用List实现商品列表展示。为使页面美观,设置Scroll()的scrollBarWidth的值为0。Swiper组件、Grid组件、List组件结合ForEach语句来实现页面内容。具体主要代码实现:


@Preview
@Component
export default struct Home {
  private swiperController: SwiperController = new SwiperController();
  private items: Array<Item> = [
    new Item('商品1', $r('app.media.1'), 1000),
    new Item('商品2', $r('app.media.2'), 2000),
    new Item('商品3', $r('app.media.3'), 3000),
    new Item('商品4', $r('app.media.4'), 4000),
    new Item('商品5', $r('app.media.3'), 5000),
    new Item('商品6', $r('app.media.2'), 6000),
  ]

  build() {
    Scroll() {
      Column({ space: CommonConstants.COMMON_SPACE }) {
        Column() {
          Text($r('app.string.mainPage_tabTitles_home'))
            .fontWeight(FontWeight.Medium)
            .fontSize($r('app.float.page_title_text_size'))
            .margin({ top: $r('app.float.mainPage_tabTitles_margin') })
            .padding({ left: $r('app.float.mainPage_tabTitles_padding') })
        }
        .width(CommonConstants.FULL_PARENT)
        .alignItems(HorizontalAlign.Start)

        Swiper(this.swiperController) {
          ForEach(mainViewModel.getSwiperImages(), (img: Resource) => {
            Image(img).borderRadius($r('app.float.home_swiper_borderRadius'))
          }, (img: Resource) => JSON.stringify(img.id))
        }
        .margin({ top: $r('app.float.home_swiper_margin') })
        .autoPlay(true)

        Grid() {
          ForEach(mainViewModel.getFirstGridData(), (item: ItemData) => {
            GridItem() {
              Column() {
                Image(item.img)
                  .width($r('app.float.home_homeCell_size'))
                  .height($r('app.float.home_homeCell_size'))
                Text(item.title)
                  .fontSize($r('app.float.little_text_size'))
                  .margin({ top: $r('app.float.home_homeCell_margin') })
              }
            }
          }, (item: ItemData) => JSON.stringify(item))
        }
        .columnsTemplate('1fr 1fr 1fr 1fr')
        .rowsTemplate('1fr 1fr')
        .columnsGap($r('app.float.home_grid_columnsGap'))
        .rowsGap($r('app.float.home_grid_rowGap'))
        .padding({ top: $r('app.float.home_grid_padding'), bottom: $r('app.float.home_grid_padding') })
        .height($r('app.float.home_grid_height'))
        .backgroundColor(Color.White)
        .borderRadius($r('app.float.home_grid_borderRadius'))

        Text('商品列表')
          .fontSize($r('app.float.normal_text_size'))
          .fontWeight(FontWeight.Medium)
          .width(CommonConstants.FULL_PARENT)
          .margin({ top: $r('app.float.home_text_margin') })

        List() {
          ForEach(
            this.items,
            (item: Item) => {
              ListItem() {
                Row({ space: 10 }) {
                  Image(item.image)
                    .width(100)
                  Column({ space: 4 }) {
                    Text(item.name)
                      .fontSize(20)
                      .fontWeight(FontWeight.Bold)
                    Text(item.price + '元')
                      .fontColor('#f36')
                      .fontSize(18)
                  }
                  .height('100%')
                  .alignItems(HorizontalAlign.Start)

                }
                .width('100%')
                .backgroundColor('#fff')
                .borderRadius(20)
                .height(120)
                .padding(10)
              }
            }
          )
        }
      }
    }
    .scrollBarWidth(0)
  }
}

效果如下:

『江鸟中原』鸿蒙——简易商城搭建_HarmonyOS_03

3.设置页Second.ets的实现

使用List组件结合ForEach语句来实现页面列表内容,其中引用了settingCell子组件,列表间的灰色分割线可以使用Divider属性实现。具体实现代码如下:


@Component
export default struct Setting {
  @Builder settingCell(item: ItemData) {
    Row() {
      Row({ space: CommonConstants.COMMON_SPACE }) {
        Image(item.img)
          .width($r('app.float.setting_size'))
          .height($r('app.float.setting_size'))
        Text(item.title)
          .fontSize($r('app.float.normal_text_size'))
      }

      if (item.others === true) {
        Image($r('app.media.right_grey'))
          .width($r('app.float.setting_jump_width'))
          .height($r('app.float.setting_jump_height'))
      } else {
        Toggle({ type: ToggleType.Switch, isOn: false })
      }
    }
    .justifyContent(FlexAlign.SpaceBetween)
    .width(CommonConstants.FULL_PARENT)
    .padding({
      left: $r('app.float.setting_settingCell_left'),
      right: $r('app.float.setting_settingCell_right')
    })
  }

  build() {
    Scroll() {
      Column({ space: CommonConstants.COMMON_SPACE }) {
        Column(){
          Text($r('app.string.mainPage_tabTitles_mine'))
            .fontWeight(FontWeight.Medium)
            .fontSize($r('app.float.page_title_text_size'))
            .margin({ top: $r('app.float.mainPage_tabTitles_margin') })
            .padding({ left: $r('app.float.mainPage_tabTitles_padding') })
        }
        .width(CommonConstants.FULL_PARENT)
        .alignItems(HorizontalAlign.Start)

        Row() {
          Image($r('app.media.account'))
            .width($r('app.float.setting_account_size'))
            .height($r('app.float.setting_account_size'))
          Column() {
            Text($r('app.string.setting_account_name'))
              .fontSize($r('app.float.setting_account_fontSize'))
            Text($r('app.string.setting_account_email'))
              .fontSize($r('app.float.little_text_size'))
              .margin({ top: $r('app.float.setting_name_margin') })
          }
          .alignItems(HorizontalAlign.Start)
          .margin({ left: $r('app.float.setting_account_margin') })
        }
        .margin({ top: $r('app.float.setting_account_margin') })
        .alignItems(VerticalAlign.Center)
        .width(CommonConstants.FULL_PARENT)
        .height($r('app.float.setting_account_height'))
        .backgroundColor(Color.White)
        .padding({ left: $r('app.float.setting_account_padding') })
        .borderRadius($r('app.float.setting_account_borderRadius'))

        List() {
          ForEach(mainViewModel.getSettingListData(), (item: ItemData) => {
            ListItem() {
              this.settingCell(item)
            }
            .height($r('app.float.setting_list_height'))
          }, (item:ItemData) => JSON.stringify(item))
        }
        .backgroundColor(Color.White)
        .width(CommonConstants.FULL_PARENT)
        .height(CommonConstants.SET_LIST_WIDTH)
        .divider({
          strokeWidth: $r('app.float.setting_list_strokeWidth'),
          color: Color.Grey,
          startMargin: $r('app.float.setting_list_startMargin'),
          endMargin: $r('app.float.setting_list_endMargin')
        })
        .borderRadius($r('app.float.setting_list_borderRadius'))
        .padding({ top: $r('app.float.setting_list_padding'), bottom: $r('app.float.setting_list_padding') })

        Blank()

        Button($r('app.string.setting_button'), { type: ButtonType.Capsule })
          .width(CommonConstants.BUTTON_WIDTH)
          .height($r('app.float.login_button_height'))
          .fontSize($r('app.float.normal_text_size'))
          .fontColor($r('app.color.setting_button_fontColor'))
          .fontWeight(FontWeight.Medium)
          .backgroundColor($r('app.color.setting_button_backgroundColor'))
          .margin({ bottom: $r('app.float.setting_button_bottom')})
      }
      .height(CommonConstants.FULL_PARENT)
    }
  }
}

使用Toggle组件,根据Item类的boolean类型others值是否为true,来为列表项设置开关。

if (item.others === true) {
        Image($r('app.media.right_grey'))
          .width($r('app.float.setting_jump_width'))
          .height($r('app.float.setting_jump_height'))
      } else {
        Toggle({ type: ToggleType.Switch, isOn: false })
      }
 
export default class PageResource {
 
  title: Resource;

  img: Resource;
 
  others?:Boolean;

  constructor(title: Resource, img: Resource, others?: Boolean) {
    this.title = title;
    this.img = img;
    this.others = others;
  }
}

效果如下:

『江鸟中原』鸿蒙——简易商城搭建_移动开发_04

四、总结

1.程序仅实现了简单的功能,还有很多不足的地方需要后续进行完善与改进,还只是处于开发的初级阶段。

2.仅供学习使用。

五、学习鸿蒙课程的收获

在学习鸿蒙课程过程中的收获:

  1. 了解了鸿蒙的核心理念和优势:在课程中,我深入了解了鸿蒙操作系统的核心理念,例如分布式能力、全场景智能互联、跨设备开发等。这些概念让我认识到鸿蒙的优势并了解如何应用于实际开发中。
  2. 掌握了鸿蒙开发平台和工具的使用:课程帮助我熟悉了鸿蒙开发平台和工具,例如HarmonyOS Studio IDE、DevEco Studio等。我学会了如何创建项目、编写代码、调试和运行应用程序等基本操作。
  3. 学习了鸿蒙应用开发的基本原理和方法:通过练习和实践,我了解了鸿蒙应用开发的基本原理,包括UI布局、事件处理、数据存储和网络通信等。这些知识帮助我更好地构建应用程序并实现所需的功能。