OpenHarmony开发实战:购物应用(ArkTS),2024年最新字节跳动面试编程题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
img

正文

  • model:存放封装好的数据实体。
  • ArsData:我的页签相关参数实体。
  • GoodsData:商品列表页商品实体。
  • GoodsDataModels:各种实体的具体数据以及获取数据的方法。
  • Menu:我的页签菜单实体。
  • pages:存放页面。
  • HomePage:应用主页面,包含商品列表页签。
  • MyPage:我的页签。
  • ShoppingCartPage:购物车页签。
  • ShoppingDetail:商品详情页。
  • resources :存放工程使用到的资源文件。
  • resources/base/media:存放工程中使用的图片资源。
  • config.json:配置文件。

搭建OpenHarmony环境

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以Hi3516DV300开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)

以3.0版本为例:


2. 搭建烧录环境

  1. 完成DevEco Device Tool的安装

  2. 完成Hi3516开发板的烧录

  3. 搭建开发环境

  4. 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。

  5. 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”),选择JS或者eTS语言开发。

  6. 工程创建完成后,选择使用真机进行调测

构建商品列表页签

在本节中,我们将完成商品列表页签的设计,效果图如下:

从效果图可以看出,商品列表页签主要由三个部分组成:

  1. 顶部的Tabs组件。
  2. 中间TabContent组件内包含List组件。其中List组件的item是一个水平布局,由一个垂直布局和一个Image组件组成;item中的垂直布局由3个Text组件组成。
  3. 底部的导航页签navigation组件。

实现步骤如下:

  1. 在pages目录下面新建一个ETS Page,命名为HomePage.ets,在config.json文件的pages属性中会自动添加“pages/HomePage”页面路由。

说明:

  • 页面文件名不能使用组件名称,比如:Text.ets、Button.ets等。
  • 每个页面文件中必须包含入口组件。
  1. 新建与pages文件夹同级的model文件夹,并在model目录下新建ArsData.ets、GoodsData.ets、Menu.ets和GoodsDataModels.ets文件,其中ArsData.ets、GoodsData.ets、Menu.ets是数据实体类,GoodsDataModels.ets是存放这三种实体数据集合,并定义了获取各种数据集合的方法。数据实体包含实体的属性和构造方法,可通过new ArsData(string,string) 来获取ArsData对象,ArsData.ets内容如下:

let NextId = 0;
export class ArsData {
id: string;
title: string;
content: string;

constructor(title: string, content: string) {
this.id = ${NextId++};
this.title = title;
this.content = content;
}
}

GoodsData.ets代码如下:

let NextId = 0;
export class GoodsData {
id: string;
title: string;
content: string;
price: number;
imgSrc: Resource;

constructor(title: string, content: string, price: number, imgSrc: Resource) {
this.id = ${NextId++};
this.title = title;
this.content = content;
this.price = price;
this.imgSrc = imgSrc;
}
}

一个文件中可以包含多个class ,Menu.ets中就包含了Menu类和ImageItem类,Menu.ets代码如下

let NextId = 0;
export class Menu {
id: string;
title: string;
num: number;

constructor(title: string, num: number) {
this.id = ${NextId++};
this.title = title;
this.num = num;
}
}

export class ImageItem {
id: string;
title: string;
imageSrc: Resource;

constructor(title: string, imageSrc: Resource) {
this.id = ${NextId++};
this.title = title;
this.imageSrc = imageSrc;
}
}

GoodsDataModels.ets代码如下:

import {GoodsData} from ‘./GoodsData’

import {Menu, ImageItem} from ‘./Menu’
import {ArsData} from ‘./ArsData’
//获取商品列表数据
export function initializeOnStartup(): Array {
let GoodsDataArray: Array = []
GoodsComposition.forEach(item => {
console.log(item.title);
GoodsDataArray.push(new GoodsData(item.title, item.content, item.price, item.imgSrc));
})
return GoodsDataArray;
}
//获取底部默认图片列表数据
export function getIconPath(): Array {
let IconPath: Array = [‘nav/icon-buy.png’,‘nav/icon-shopping-cart.png’,‘nav/icon-my.png’]

return IconPath;
}
//获取选中后图片列表数据
export function getIconPathSelect(): Array {
let IconPathSelect: Array = [‘nav/icon-home.png’,‘nav/icon-shopping-cart-select.png’,‘nav/icon-my-select.png’]

return IconPathSelect;
}
//获取商品详情页图片详情列表
export function getDetailImages(): Array {
let detailImages: Array = [‘computer/computer1.png’,‘computer/computer2.png’,‘computer/computer3.png’,‘computer/computer4.png’,‘computer/computer5.png’,‘computer/computer6.png’]

return detailImages;
}

//获取菜单数据列表
export function getMenu(): Array

{
let MenuArray: Array = []
MyMenu.forEach(item => {
MenuArray.push(new Menu(item.title,item.num));
})
return MenuArray;
}
//获取MyTrans数据列表
export function getTrans(): Array {
let ImageItemArray: Array = []
MyTrans.forEach(item => {
ImageItemArray.push(new ImageItem(item.title,item.imageSrc));
})
return ImageItemArray;
}
//获取More数据列表
export function getMore(): Array {
let ImageItemArray: Array = []
MyMore.forEach(item => {
ImageItemArray.push(new ImageItem(item.title,item.imageSrc));
})
return ImageItemArray;
}
//获取参数列表
export function getArs(): Array {
let ArsItemArray: Array = []
ArsList.forEach(item => {
ArsItemArray.push(new ArsData(item.title,item.content));
})
return ArsItemArray;
}
//数据集合部分

  1. 在HomePage.ets文件中创建商品列表页签相关的组件,其中GoodsHome效果图如下:

代码如下:

@Component
@Component
struct GoodsHome {
private goodsItems: GoodsData[]

build() {
Column() {
Tabs() {
TabContent() {
GoodsList({ goodsItems: this.goodsItems });
}
.tabBar(“Top Sellers”)
.backgroundColor(Color.White)

TabContent() {
GoodsList({ goodsItems: this.goodsItems });
}
.tabBar(“Recommended”)
.backgroundColor(Color.White)

TabContent() {
GoodsList({ goodsItems: this.goodsItems });
}
.tabBar(“Lifestyle”)
.backgroundColor(Color.White)

TabContent() {
GoodsList({ goodsItems: this.goodsItems });
}
.tabBar(“Deals”)
.backgroundColor(Color.White)
}
.barWidth(540)
.barHeight(50)
.scrollable(true)
.barMode(BarMode.Scrollable)
.backgroundColor(‘#007DFF’)
.height(‘100%’)
}
.alignItems(HorizontalAlign.Start)
}
}

在GoodsHome中使用Tabs组件,在Tabs组件中设置4个TabContent,给每个TabContent设置tabBar属性,并设置TabContent容器中的内容GoodsList组件,GoodsList组件效果图如下:

代码如下:

@Component
struct GoodsList {
private goodsItems: GoodsData[]

build() {
Column() {
List() {
ForEach(this.goodsItems, item => {
ListItem() {
GoodsListItem({ goodsItem: item })
}
}, item => item.id.toString())
}
.height(‘100%’)
.width(‘100%’)
.align(Alignment.Top)
.margin({top: 5})
}
}
}

在GoodsList组件中遍历商品数据集合,ListItem组件中设置组件内容,并使用Navigator组件给每个Item设置顶级跳转路由,GoodsListItem组件效果图如下:

代码如下:

@Component
struct GoodsListItem {
private goodsItem: GoodsData

build() {
Navigator({ target: ‘pages/ShoppingDetail’ }) {
Row() {
Column() {
Text(this.goodsItem.title)
.fontSize(18)
Text(this.goodsItem.content)
.fontSize(14)
Text(‘¥’ + this.goodsItem.price)
.fontSize(18)
.fontColor(Color.Red)
}
.height(130)
.width(‘60%’)
.margin({ left: 20 })
.alignItems(HorizontalAlign.Start)

Image(this.goodsItem.imgSrc)
.objectFit(ImageFit.ScaleDown)
.height(130)
.width(‘30%’)
.renderMode(ImageRenderMode.Original)
.margin({ right: 10, left: 10 })

}
.backgroundColor(Color.White)

}
.params({ goodsData: this.goodsItem })
.margin({ right: 5 })
}
}

  1. 在HomePage.ets中创建文件入口组件(Index)以及底部页签导航组件(Navigation),导入需要使用到的数据实体类以及需要使用的方法和组件,每个page文件都必须包含一个入口组件,使用@Entry修饰,HomePage文件中的入口组件(Index)代码如下:

import { GoodsData, IconImage } from ‘…/model/GoodsData’
import { initializeOnStartup, getIconPath, getIconPathSelect } from ‘…/model/GoodsDataModels’
import { ShoppingCart } from ‘./ShoppingCartPage.ets’
import { MyInfo } from ‘./MyPage.ets’
import router from ‘@system.router’;

@Entry
@Component
struct Index {
@Provide currentPage: number = 1
private goodsItems: GoodsData[] = initializeOnStartup()
@State Build: Array = [
{
icon: $r(‘app.media.icon_home’),
icon_after: $r(‘app.media.icon_buy1’),
text: ‘首页’,
num: 0
},
{
icon: $r(‘app.media.icon_shopping_cart’),
icon_after: $r(‘app.media.icon_shopping_cart_select’),
text: ‘购物车’,
num: 1
},
{
icon: $r(‘app.media.icon_my’),
icon_after: $r(‘app.media.icon_my_select’),
text: ‘我的’,
num: 2
}
]

@Builder NavigationToolbar() {
Flex({direction:FlexDirection.Row,wrap:FlexWrap.NoWrap,justifyContent:FlexAlign.SpaceAround}) {
ForEach(this.Build, item => {
Column() {
Image(this.currentPage == item.num ? item.icon_after : item.icon)
.width(25)
.height(25)
Text(item.text)
.fontColor(this.currentPage == item.num ? “#ff7500” : “#000000”)
}
.onClick(() => {
this.currentPage = item.num
})
})
}
}

build() {
Column() {
Navigation() {
Flex() {
if (this.currentPage == 0) {
GoodsHome({ goodsItems: this.goodsItems })
}
if (this.currentPage == 1) {
ShoppingCart() //购物车列表
}
if (this.currentPage == 2) {
MyInfo() //我的
}
}
.width(‘100%’)
.height(‘100%’)
}
.toolBar(this.NavigationToolbar)
.title(“购物车”)
.hideTitleBar(this.currentPage == 1 ? false : true)
.hideBackButton(true)
}
}
}

从入口组件的代码中可以看出,我们定义了一个全局变量currentPage ,当currentPage发生变化的时候,会显示不同的页签。在入口组件中,通initializeOnStartup获取商品列表数据(goodsItems)并传入GoodsHome组件中。效果图如下:

构建购物车页签

从上面效果图可以看出,主界面购物车页签主要由下面三部分组成:

  1. 顶部的title,由Navigation组件title属性设置。
  2. 中间的List组件,其中List组件的item是一个水平的布局内包含一个toggle组件,一个Image组件和一个垂直布局,其item中的垂直布局是由2个Text组件组成。
  3. 底部一个水平布局包含两个Text组件。

在本任务中我们主要是构建一个购物车页签,给商品列表的每个商品设置一个单选框,可以选中与取消选中,底部Total值也会随之增加或减少,点击Check Out时会触发弹窗。下面我们来完成ShoppingCart页签。

  1. 在pages目录下面新建一个ETS Page ,命名为ShoppingCart.ets,config.json文件pages属性中也会自动添加“pages/ShoppingCart”页面路由。
  2. 在ShoppingCartPage.ets文件中添加入口组件(ShoppingCart),并导入需要使用到的数据实体类、方法和组件。ShoppingCart组件代码如下:

import {GoodsData} from ‘…/model/GoodsData’
import {initializeOnStartup} from ‘…/model/GoodsDataModels’
import prompt from ‘@system.prompt’;

@Entry
@Component
export struct ShoppingCart {
@Provide totalPrice: number = 0
private goodsItems: GoodsData[] = initializeOnStartup()

build() {
Column() {
ShopCartList({ goodsItems: this.goodsItems });
ShopCartBottom()
}
.height(‘100%’)
.width(‘100%’)
.alignItems(HorizontalAlign.Start)
}
}

  1. 新建ShopCartList组件用于存放购物车商品列表,ShopCartList组件效果图如下:

代码如下:

@Component
struct ShopCartList {
private goodsItems: GoodsData[]

build() {
Column() {
List() {
ForEach(this.goodsItems, item => {
ListItem() {
ShopCartListItem({ goodsItem: item })
}
}, item => item.id.toString())
}
.height(‘100%’)
.width(‘100%’)
.align(Alignment.Top)
.margin({ top: 5 })
}
.height(‘90%’)
}
}

在ShopCartListItem中使用Toggle的单选框类型来实现每个item的选择和取消选择,在Toggle的onChage事件中来改变totalPrice的数值。ShopCartListItem组件效果图如下:

代码如下:

@Component
struct ShopCartListItem {
@Consume totalPrice: number
private goodsItem: GoodsData

build() {
Row() {
Toggle({ type: ToggleType.Checkbox })
.width(13)
.height(13)
.onChange((isOn: boolean) => {
if (isOn) {
this.totalPrice += parseInt(this.goodsItem.price + ‘’, 0)
} else {
this.totalPrice -= parseInt(this.goodsItem.price + ‘’, 0)
}
})
Image(this.goodsItem.imgSrc)
.objectFit(ImageFit.ScaleDown)
.height(130)
.width(100)
.renderMode(ImageRenderMode.Original)
Column() {
Text(this.goodsItem.title)
.fontSize(18)
Text(‘¥’ + this.goodsItem.price)
.fontSize(18)
.fontColor(Color.Red)
}
.margin({left:40})
}
.height(100)
.width(‘100%’)
.margin({ left: 20 })
.alignItems(VerticalAlign.Center)
.backgroundColor(Color.White)
}
}

  1. 新建ShopCartBottom组件,ShopCartBottom组件效果图如下:

代码如下:

@Component
struct ShopCartBottom {
@Consume totalPrice: number

build() {
Row() {
Text(‘Total: ¥’ + this.totalPrice)
.fontColor(Color.Red)
.fontSize(18)
.margin({ left: 20 })
.width(150)
Text(‘Check Out’)
.fontColor(Color.Black)
.fontSize(18)
.margin({ right: 20, left: 180 })
.onClick(() => {
prompt.showToast({
message: ‘Checking Out’,
duration: 10,
bottom: 100
})
})
}
.height(30)
.width(‘100%’)
.backgroundColor(‘#FF7FFFD4’)
.alignItems(VerticalAlign.Bottom)
}
}

构建我的页签

从上面效果图可以看出,主界面我的页签主要由下面四部分组成:

  1. 顶部的水平布局。
  2. 顶部下面的文本加数字的水平List。
  3. My Transactio模块,图片加文本的水平List。
  4. More模块,图片加文本的Grid。

在本任务中,我们构建主页我的页签,主要可以划分成下面几步:

  1. 在pages目录下面新建一个ETS Page 命名为MyPage.ets,在config.json文件pages属性中也会自动添加“pages/MyPage”页面路由。
  2. 在MyPage.ets文件中添加入口组件(MyInfo),组件内容如下:

import {getMenu,getTrans,getMore} from ‘…/model/GoodsDataModels’
import {Menu, ImageItem} from ‘…/model/Menu’
@Entry
@Component
export struct MyInfo {
build() {
Column() {
Row() {
Image($r(‘app.media.icon_user’))
.objectFit(ImageFit.Contain)
.height(50)
.width(50)
.margin({left:10})
.renderMode(ImageRenderMode.Original)
Column() {
Text(‘John Doe’)
.fontSize(15)
Text(‘Member Name : John Doe >’)
.fontSize(15)
}
.height(60)
.margin({ left: 20, top: 10 })
.alignItems(HorizontalAlign.Start)
}

TopList()
MyTransList()
MoreGrid()

}
.alignItems(HorizontalAlign.Start)
.width(‘100%’)
.height(‘100%’)
.flexGrow(1)
}
}

入口组件中还包含TopList,MyTransList和MoreGrid三个子组件。
3. 在MyPage.ets文件中新建TopList组件,效果图如下:

代码如下:

@Component
struct TopList {
private menus: Menu1[] = getMenu()

build() {
Row() {
List() {
ForEach(this.menus, item => {
ListItem() {
MenuItem({ menu: item })
}
}, item => item.id.toString())
}
.height(‘100%’)
.width(‘100%’)
.margin({ top: 5,left: 10})
.edgeEffect(EdgeEffect.None)
.listDirection(Axis.Horizontal)
}
.width(‘100%’)
.height(50)
}
}

getMenu()方法在上文中已有定义,是获取菜单列表的方法,TopList的子组件MenuItem内容如下:

@Component
struct MenuItem {
private menu: Menu1

build() {
Column() {
Text(this.menu.title)
.fontSize(15)
Text(this.menu.num + ‘’)
.fontSize(13)

}
.height(50)
.width(100)
.margin({ left: 8, right: 8 })
.alignItems(HorizontalAlign.Start)
.backgroundColor(Color.White)
}
}

  1. 在MyPage.ets文件中新建MyTransList组件和MoreGrid组件,MyTransList组件效果如如下:

代码如下:

@Component
struct MyTransList {
private imageItems: ImageItem[] = getTrans()

build() {
Column() {
Text(‘My Transaction’)
.fontSize(20)
.margin({ left: 10 })
.width(‘100%’)
.height(30)
Row() {
List() {
ForEach(this.imageItems, item => {
ListItem() {
DataItem({ imageItem: item })
}
}, item => item.id.toString())
}
.height(70)
.width(‘100%’)
.edgeEffect(EdgeEffect.None)
.margin({ top: 5 })
.padding({ left: 16, right: 16 })
.listDirection(Axis.Horizontal)
}
}
.height(120)
}
}

MoreGrid组件效果图如下:

代码如下:

@Component
struct MoreGrid {
private gridRowTemplate: string = ‘’
private imageItems: ImageItem[] = getMore()
private heightValue: number

aboutToAppear() {
var rows = Math.round(this.imageItems.length / 3);
this.gridRowTemplate = '1fr '.repeat(rows);
this.heightValue = rows * 75;
}

build() {
Column() {
Text(‘More’)
.fontSize(20)
.margin({ left: 10 })
.width(‘100%’)
.height(30)
Scroll() {
Grid() {
ForEach(this.imageItems, (item: ImageItem) => {
GridItem() {
DataItem({ imageItem: item })
}
}, (item: ImageItem) => item.id.toString())
}
.rowsTemplate(this.gridRowTemplate)
.columnsTemplate(‘1fr 1fr 1fr’)
.columnsGap(8)
.rowsGap(8)
.height(this.heightValue)
}
.padding({ left: 16, right: 16 })
}
.height(400)
}
}

在MyTransList和MoreGrid组件中都包含子组件DataItem,为避免的重复代码,可以把多次要用到的结构体组件化,这里的结构体就是图片加上文本的上下结构体,DataItem组件内容如下:

@Component
struct DataItem {
private imageItem: ImageItem

build() {
Column() {
Image(this.imageItem.imageSrc)
.objectFit(ImageFit.Contain)
.height(50)
.width(50)
.renderMode(ImageRenderMode.Original)
Text(this.imageItem.title)
.fontSize(15)

}
.height(70)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ring())
}
.rowsTemplate(this.gridRowTemplate)
.columnsTemplate(‘1fr 1fr 1fr’)
.columnsGap(8)
.rowsGap(8)
.height(this.heightValue)
}
.padding({ left: 16, right: 16 })
}
.height(400)
}
}

在MyTransList和MoreGrid组件中都包含子组件DataItem,为避免的重复代码,可以把多次要用到的结构体组件化,这里的结构体就是图片加上文本的上下结构体,DataItem组件内容如下:

@Component
struct DataItem {
private imageItem: ImageItem

build() {
Column() {
Image(this.imageItem.imageSrc)
.objectFit(ImageFit.Contain)
.height(50)
.width(50)
.renderMode(ImageRenderMode.Original)
Text(this.imageItem.title)
.fontSize(15)

}
.height(70)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-QhRmOqJO-1713315834614)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 21
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值