#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作为动态路由的管理

#HarmonyOS NEXT 体验官#本地数据库实现《菜谱大全》app(二)-鸿蒙开发者社区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

#HarmonyOS NEXT 体验官#本地数据库实现《菜谱大全》app(二)-鸿蒙开发者社区

// 动态路由中需要加载的模块名和模块路径
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');
}

#HarmonyOS NEXT 体验官#本地数据库实现《菜谱大全》app(二)-鸿蒙开发者社区

3.创建一个新的module作为菜谱各个页面的展示

#HarmonyOS NEXT 体验官#本地数据库实现《菜谱大全》app(二)-鸿蒙开发者社区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"

#HarmonyOS NEXT 体验官#本地数据库实现《菜谱大全》app(二)-鸿蒙开发者社区

5.app主页面代码

主页面有四部分组成,TitleBar、选项卡、搜索框、瀑布流

#HarmonyOS NEXT 体验官#本地数据库实现《菜谱大全》app(二)-鸿蒙开发者社区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 === "厨神驾到") {

      }
    })
  }
}

#HarmonyOS NEXT 体验官#本地数据库实现《菜谱大全》app(二)-鸿蒙开发者社区

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()
    }
  }
}

#HarmonyOS NEXT 体验官#本地数据库实现《菜谱大全》app(二)-鸿蒙开发者社区

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 })
    }
  }
}

#HarmonyOS NEXT 体验官#本地数据库实现《菜谱大全》app(二)-鸿蒙开发者社区

列表页

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"))
  }
}

#HarmonyOS NEXT 体验官#本地数据库实现《菜谱大全》app(二)-鸿蒙开发者社区

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
标签
收藏
回复
举报
回复
    相关推荐