回复
#HarmonyOS NEXT 体验官#本地数据库实现《菜谱大全》app(二) 原创
一路向北545
发布于 2024-8-18 21:16
浏览
0收藏
本地数据库实现菜谱大全app主框架已经写好,那么接下来就是各个页面之间的代码编写了。
这里我们使用Navigation的动态路由功能进行各个页面之间的跳转。
1.获取本地数据库实例
export async function getRdbStore(context: Context, onGetStore: (store: relationalStore.RdbStore | undefined) => void) {
relationalStore.getRdbStore(context, {
name: Constant.DB_NAME,
securityLevel: relationalStore.SecurityLevel.S1
}, (err, store) => {
if (err) {
console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
return;
} else {
console.info(`Succeeded in getting RdbStore.`);
onGetStore(store)
console.log("=======version:" + store.version)
}
})
}
2.创建一个moudule作为动态路由的管理
cke_1201.png
import { logger } from '@ohos/base/Index';
import { RouterInfo } from '../constants/RouterInfo';
/**
* 动态路由
*
* 实现步骤:
* 1.将主模块的NavPathStack传入createRouter接口,注册路由
* 2.通过registerBuilder接口,将需要动态加载的模块注册到路由中
* 3.通过push接口,跳转到指定的模块页面
*/
const LOGGER_TAG: string = 'Dynamics import failed , reason : ';
export class DynamicsRouter {
// 管理需要动态导入的模块,key是模块名,value是WrappedBuilder对象,动态调用创建页面的接口
static builderMap: Map<string, WrappedBuilder<[object]>> = new Map<string, WrappedBuilder<[object]>>();
static navPathStack: NavPathStack = new NavPathStack();
// 通过数组实现自定义栈的管理
static routerStack: Array<RouterInfo> = new Array();
static referrer: string[] = [];
// 通过名称注册builder
private static registerBuilder(builderName: string, builder: WrappedBuilder<[object]>): void {
DynamicsRouter.builderMap.set(builderName, builder);
}
// 通过名称获取builder
public static getBuilder(builderName: string): WrappedBuilder<[object]> {
const builder = DynamicsRouter.builderMap.get(builderName);
if (!builder) {
const MSG = "not found builder";
logger.info(MSG + builderName);
}
return builder as WrappedBuilder<[object]>;
}
// 注册router
public static createNavPathStack(navPathStack: NavPathStack): void {
DynamicsRouter.navPathStack = navPathStack;
// 初始化时来源页为未定义
DynamicsRouter.routerStack.push(RouterInfo.HOME_PAGE)
logger.info(`DynamicsRouter create routerStack Home is: ${RouterInfo.HOME_PAGE.moduleName} + ${RouterInfo.HOME_PAGE.pageName}`);
}
// 通过名称获取router
private static getNavPathStack(): NavPathStack {
return DynamicsRouter.navPathStack;
}
// 获取路由来源页面栈
public static getRouterReferrer(): string[] {
return DynamicsRouter.referrer;
}
// 通过获取页面栈跳转到指定页面
public static async push(routerInfo: RouterInfo, param?: ESObject): Promise<void> {
const pageName: string = routerInfo.pageName;
const moduleName: string = routerInfo.moduleName;
const FullScreenArray: string[] = ['@ohos/foldablescreencases'];
// 动态加载模块是否成功
let isImportSucceed: boolean = false;
// 模块是否需要转场动画
let isNeedFullScreen: boolean = true;
// TODO:知识点:通过动态import的方式引入模块,在需要进入页面时才加载模块,可以减少主页面的初始化时间及占用的内存
await import(moduleName).then((result: ESObject) => {
// 动态加载模块成功时,通过模块中的harInit接口加载页面
result.harInit(pageName);
isImportSucceed = true;
if (FullScreenArray.includes(moduleName)) {
isNeedFullScreen = false;
}
}, (error: ESObject) => {
// 动态加载模块失败时,打印错误日志
logger.error(LOGGER_TAG, error);
});
if (isImportSucceed) {
// 使用moduleName和pageName生成builderName,通过pushPath加载页面
const builderName: string = moduleName + "/" + pageName;
// 查找到对应的路由栈进行跳转
DynamicsRouter.getNavPathStack().pushPath({ name: builderName, param: param }, isNeedFullScreen);
// 自定义栈也加入指定页面
DynamicsRouter.routerStack.push(routerInfo);
const referrerModel: RouterInfo = DynamicsRouter.routerStack[DynamicsRouter.routerStack.length - 2];
DynamicsRouter.referrer[0] = referrerModel.moduleName;
DynamicsRouter.referrer[1] = referrerModel.pageName;
logger.info(`From DynamicsRouter.routerStack push preview module name is + ${DynamicsRouter.referrer[0]}, path is ${DynamicsRouter.referrer[1]}`);
}
}
// 通过获取页面栈并pop
public static pop(): void {
// pop前记录的来源页为当前栈顶
const referrerModel: RouterInfo = DynamicsRouter.routerStack[DynamicsRouter.routerStack.length - 1];
DynamicsRouter.referrer[0] = referrerModel.moduleName;
DynamicsRouter.referrer[1] = referrerModel.pageName;
logger.info(`From DynamicsRouter.routerStack pop preview module name is + ${DynamicsRouter.referrer[0]}, path is ${DynamicsRouter.referrer[1]}`);
if (DynamicsRouter.routerStack.length > 1) {
DynamicsRouter.routerStack.pop();
} else {
logger.info("DynamicsRouter.routerStack is only Home.");
}
// 查找到对应的路由栈进行pop
DynamicsRouter.getNavPathStack().pop();
}
// 通过获取页面栈并将其清空
public static clear(): void {
// 查找到对应的路由栈进行pop
DynamicsRouter.getNavPathStack().clear();
}
/**
* 注册动态路由需要加载的页面
*
* @param pageName 页面名,需要动态加载的页面名称
* @param wrapBuilder wrapBuilder对象
*/
public static registerRouterPage(routerInfo: RouterInfo, wrapBuilder: WrappedBuilder<[object]>): void {
const builderName: string = routerInfo.moduleName + "/" + routerInfo.pageName;
if
// 动态路由中需要加载的模块名和模块路径
export class RouterInfo {
moduleName: string = '';
pageName: string = '';
constructor(moduleName: string, pageName: string) {
this.moduleName = moduleName;
this.pageName = pageName;
}
static readonly SEARCH: RouterInfo = new RouterInfo('@ohos/food', 'SearchView')
static readonly FOOD_DETAIL: RouterInfo = new RouterInfo('@ohos/food', 'FooDetailView');
static readonly COOK_METHOD: RouterInfo = new RouterInfo('@ohos/food', 'CookMethodView');
static readonly TASTES: RouterInfo = new RouterInfo('@ohos/food', 'TastesView');
static readonly FOOD_LIST: RouterInfo = new RouterInfo('@ohos/food', 'FoodListPage');
static readonly COLLECT_LIST: RouterInfo = new RouterInfo('@ohos/food', 'CollectListPage');
}
3.创建一个新的module作为菜谱各个页面的展示
cke_5601.png
4.entry依赖
将新建的moudle添加到entry主模块的依赖中去
{
"name": "entry",
"version": "1.0.0",
"description": "Please describe the basic information.",
"main": "",
"author": "",
"license": "",
"dependencies": {
"@ohos/base": "file:../../common/utils",
// 动态路由模块
"@ohos/routermodule": "file:../../feature/routermodule",
//菜谱相关页面
"@ohos/food": "file:../../feature/food",
"@ohos/account": "file:../../feature/account"
5.app主页面代码
主页面有四部分组成,TitleBar、选项卡、搜索框、瀑布流
cke_19283.png
import { MenuBean, menuModel, TitleBar } from '@ohos/base/Index'
import { relationalStore } from '@kit.ArkData'
import { Constant } from '@ohos/base/src/main/ets/constant/Constant'
import { Food } from '@ohos/base/src/main/ets/model/Food'
import { DynamicsRouter, RouterInfo } from '@ohos/routermodule/Index'
@Component
export struct MenuView {
@Consume store: relationalStore.RdbStore
@State menus: Array<MenuBean> =
[new MenuBean("烹饪方法", $r("app.media.menu_method")), new MenuBean("口味", $r("app.media.menu_taste"))]
@State foods: Array<Food> = new Array<Food>()
getFoods() {
menuModel.getFoods(this.store, (res: Array<Food>) => {
if (res !== undefined && res.length > 0) {
this.foods = this.foods.concat(res)
}
})
}
aboutToAppear(): void {
setTimeout(() => {
this.getFoods()
}, 1000)
}
build() {
Column() {
TitleBar({ isShowBackButton: false, title: "烹饪" })
Scroll() {
Column() {
this.menuBuilder()
this.searchBuilder()
this.FoodListViewBuilder()
}
}
.layoutWeight(1)
.align(Alignment.TopStart)
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.Spring)
.onReachEnd(() => {
this.getFoods()
})
}.height("100%")
.backgroundColor($r("app.color.white"))
}
@Builder
FoodListViewBuilder() {
Column() {
WaterFlow() {
ForEach(this.foods, (item: Food, index: number) => {
FlowItem() {
this.foodItem(item)
}
})
}
.nestedScroll({ scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.SELF_FIRST })
.columnsTemplate("1fr 1fr")
.columnsGap(10)
.rowsGap(10)
.padding({ left: 15, right: 15, top: 10 })
}
}
@Builder
foodItem(food: Food) {
Column() {
Image(food.img)
.borderRadius(10)
.objectFit(ImageFit.Contain)
Text(food.name)
.margin({ top: 8 })
}.alignItems(HorizontalAlign.Start)
.clickEffect({ level: ClickEffectLevel.MIDDLE, scale: Constant.CLICK_SCALE })
.onClick(() => {
DynamicsRouter.push(RouterInfo.FOOD_DETAIL, { foodId: food.meauID })
})
}
@Builder
searchBuilder() {
Row() {
Text("搜索美食")
.margin({ left: 15, right: 15 })
.clickEffect({ level: ClickEffectLevel.MIDDLE, scale: Constant.CLICK_SCALE })
.fontSize($r("app.float.middle_text_size"))
.fontColor($r("app.color._999999"))
.borderRadius(90)
.backgroundColor($r("app.color.window_bg"))
.padding(15)
.layoutWeight(1)
.onClick(() => {
DynamicsRouter.push(RouterInfo.SEARCH)
})
}.width("100%")
}
@Builder
menuBuilder() {
Flex({ direction: FlexDirection.Row }) {
ForEach(this.menus, (e: MenuBean) => {
this.menuItemBuilder(e)
})
}
.backgroundColor(Color.White)
.margin({
left: 10,
right: 10,
top: 15,
bottom: 15
})
}
@Builder
menuItemBuilder(menuBean: MenuBean) {
Column() {
Image(menuBean.imgRes)
.width(30)
.height(30)
Text(menuBean.title)
.margin({ top: 5 })
.fontColor($r("app.color.common_text"))
.fontSize($r("app.float.middle_text_size"))
}
.width("50%")
.borderRadius(10)
.margin({ left: 5, right: 5 })
.padding(10)
.shadow({ radius: 3, type: ShadowType.COLOR, color: $r("app.color.divider_color") })
.padding({ top: 10, bottom: 10 })
.clickEffect({ level: ClickEffectLevel.MIDDLE, scale: Constant.CLICK_SCALE })
.alignItems(HorizontalAlign.Center)
.onClick(() => {
if (menuBean.title === "烹饪方法") {
DynamicsRouter.push(RouterInfo.COOK_METHOD)
} else if (menuBean.title === "口味") {
DynamicsRouter.push(RouterInfo.TASTES)
} else if (menuBean.title === "初级入门") {
} else if (menuBean.title === "厨神驾到") {
}
})
}
}
TItleBar
import { Constant } from '../../constant/Constant'
@Component
@Preview
export struct TitleBar {
@State title: string = ""
@Prop rightTitle: string = ""
@Consume('pageStack') pageStack: NavPathStack
OnClickRightTitle?: (msg:string) => void
@State isShowBackButton?:boolean = true
build() {
Column() {
Row() {
Row() {
Image($r("app.media.come_back"))
.width(20)
.height(20)
.margin({ left: $r("app.float.common_margin") })
Text($r("app.string.back"))
.fontColor($r("app.color.white"))
.fontSize($r("app.float.big_text_size"))
.layoutWeight(1)
}.layoutWeight(1)
.clickEffect({ level: ClickEffectLevel.MIDDLE, scale: Constant.CLICK_ICON_SCALE })//点击效果
.onClick(() => {
// router.back()
this.pageStack.pop()
// DynamicsRouter.pop()
})
.visibility(this.isShowBackButton?Visibility.Visible:Visibility.Hidden)
Row() {
Text(this.title)
.fontSize($r("app.float.titleBar_text_size"))// .fontWeight(FontWeight.Bold)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}.layoutWeight(1)
.justifyContent(FlexAlign.Center)
Row() {
Text(this.rightTitle)
.fontColor($r("app.color.primary"))
.margin({ right: $r("app.float.common_margin") })
}.layoutWeight(1)
.justifyContent(FlexAlign.End)
.clickEffect({ level: ClickEffectLevel.MIDDLE, scale: Constant.CLICK_ICON_SCALE })
.onClick(()=>{
this.OnClickRightTitle&&this.OnClickRightTitle("")
})
}.width("100%")
.height(Constant.TITLE_BAR_HEIGHT)
.backgroundColor(Color.White)
Divider()
}
}
}
6.初级和高级Tab页
初级和高级类似使用的是相同的页面结构
import { Constant } from '@ohos/base/src/main/ets/constant/Constant'
import { FoodListView } from '@ohos/food/src/main/ets/widget/FoodListView'
@Component
export struct ChujiView {
build() {
Column() {
Tabs() {
TabContent() {
Column() {
FoodListView({ level: "新手尝试" })
}
}
.tabBar(this.tabBuilder("新手", 0))
TabContent() {
Column() {
FoodListView({ level: "初级入门" })
}
}
.tabBar(this.tabBuilder("初级", 1))
TabContent() {
Column() {
FoodListView({ level: "初中水平" })
}
}
.tabBar(this.tabBuilder("初中", 2))
}
.barMode(BarMode.Scrollable)
.onChange((index: number) => {
this.currentIndex = index
})
.onTabBarClick((index: number) => {
this.currentIndex = index
})
}.height("100%")
}
@State currentIndex: number = 0
@Builder
tabBuilder(title: string, targetIndex: number) {
Column() {
Text(title)
.fontColor(this.currentIndex === targetIndex ? Color.White : $r("app.color.primary"))
.backgroundColor(this.currentIndex === targetIndex ? $r("app.color.primary") : Color.White)
.width(60)
.height(32)
.textAlign(TextAlign.Center)
.border({ width: 1, color: $r("app.color.primary") })
.fontSize($r("app.float.common_margin"))
.borderRadius({
topLeft: targetIndex == 0 ? 5 : 0,
bottomLeft: targetIndex == 0 ? 5 : 0,
topRight: targetIndex == 2 ? 5 : 0,
bottomRight: targetIndex == 2 ? 5 : 0
})
.margin(0)
.clickEffect({ level: ClickEffectLevel.MIDDLE, scale: Constant.CLICK_SCALE })
}
}
}
列表页
import { menuModel } from '@ohos/base/src/main/ets/db/MenuModel'
import { Food } from '@ohos/base/src/main/ets/model/Food'
import { relationalStore } from '@kit.ArkData'
import { DynamicsRouter, RouterInfo } from '@ohos/routermodule/Index'
@Component
export struct FoodListView {
@State foods: Array<Food> = new Array()
@State method: string | undefined = undefined
@State taste: string | undefined = undefined
@State level:string|undefined= undefined
@Consume store: relationalStore.RdbStore
@State limit: number = 10
@State skip: number = 0
@State isNoMore: boolean = false
getFoods() {
if (!this.isNoMore) {
if (this.method) {
menuModel.getFoodsByMethod(this.store, this.method, (res: Array<Food>) => {
if (res && res.length > 0) {
this.foods = this.foods.concat(res)
this.skip = this.skip + this.limit
}
}, () => {
}, this.limit, this.skip)
} else if (this.taste) {
menuModel.getFoodsByTaste(this.store, this.taste, (res: Array<Food>) => {
if (res && res.length > 0) {
this.foods = this.foods.concat(res)
this.skip = this.skip + this.limit
}
}, () => {
}, this.limit, this.skip)
}else if (this.level) {
menuModel.getFoodsByLevel(this.store, this.level, (res: Array<Food>) => {
if (res && res.length > 0) {
this.foods = this.foods.concat(res)
this.skip = this.skip + this.limit
}
}, () => {
}, this.limit, this.skip)
}
}
}
aboutToAppear(): void {
this.getFoods()
}
build() {
Column() {
List() {
ForEach(this.foods, (f: Food) => {
ListItem() {
Row() {
Image(f.img)
.width(100)
.height(80)
.borderRadius(10)
Column() {
Text(f.name)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(f.taste ? f.taste : "" + " " + f.peopleNum ? f.peopleNum : "")
.fontColor($r("app.color._999999"))
.fontSize($r("app.float.small_text_size"))
Text("准备" + f.prepareTime + " 烹饪" + f.cookTime)
.fontColor($r("app.color._999999"))
.fontSize($r("app.float.small_text_size"))
}
.margin({ left: 10 })
.height(80)
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
.justifyContent(FlexAlign.SpaceAround)
}.backgroundColor(Color.White)
.borderRadius(10)
.padding(10)
.margin({ left: 15, right: 15, top: 10 })
}.onClick(() => {
DynamicsRouter.push(RouterInfo.FOOD_DETAIL, { foodId: f.meauID })
})
})
}.scrollBar(BarState.Off)
.onReachEnd(() => {
if (this.foods.length >= this.limit) {
this.getFoods()
}
})
.layoutWeight(1)
}.height("100%")
.backgroundColor($r("app.color.window_bg"))
}
}
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
标签
赞
收藏
回复
相关推荐