大家好,我是中原工学院的张博士,学习了一段时间鸿蒙课,做了一个功能简单的购物app,以下是我的鸿蒙结课报告,希望对学习的鸿蒙开发的童靴有帮助。
一、引言
在过去的几个月中,我参加了一门关于鸿蒙开发的课程,并完成了一个功能简单的购物App的开发。通过这个项目,我对鸿蒙开发平台有了更深入的了解,并提升了自己的编程技能和项目开发能力。在本报告中,我将分享我的项目经历和收获,以及对鸿蒙开发的总结和展望。
二、项目介绍
我的项目是一个功能简单的购物App,旨在为用户提供方便的在线购物体验。主要功能包括登录、功能区,商品列表展示、个人设置界面等。通过使用鸿蒙开发平台的功能和特性,我成功地实现了这些功能,并对用户界面进行了简洁而美观的设计。
三、开发过程
1.环境搭建
软件要求
DevEco Studio版本:DevEco Studio 3.1 Release。
HarmonyOS SDK版本:API version 9。
硬件要求
设备类型:华为手机或运行在DevEco Studio上的华为手机设备模拟器。
HarmonyOS系统:3.1.0 Developer Release。
环境搭建
- 安装DevEco Studio,详情请参考下载和安装软件。
- 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
- 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
- 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
- 开发者可以参考以下链接,完成设备调试的相关配置:
2.代码结构
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') })
}
以上代码实现登录界面,效果如下:
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)
}
}
效果如下:
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;
}
}
效果如下:
四、总结
1.程序仅实现了简单的功能,还有很多不足的地方需要后续进行完善与改进,还只是处于开发的初级阶段。
2.仅供学习使用。
五、学习鸿蒙课程的收获
在学习鸿蒙课程过程中的收获:
- 了解了鸿蒙的核心理念和优势:在课程中,我深入了解了鸿蒙操作系统的核心理念,例如分布式能力、全场景智能互联、跨设备开发等。这些概念让我认识到鸿蒙的优势并了解如何应用于实际开发中。
- 掌握了鸿蒙开发平台和工具的使用:课程帮助我熟悉了鸿蒙开发平台和工具,例如HarmonyOS Studio IDE、DevEco Studio等。我学会了如何创建项目、编写代码、调试和运行应用程序等基本操作。
- 学习了鸿蒙应用开发的基本原理和方法:通过练习和实践,我了解了鸿蒙应用开发的基本原理,包括UI布局、事件处理、数据存储和网络通信等。这些知识帮助我更好地构建应用程序并实现所需的功能。