99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

HarmonyOS開(kāi)發(fā)案例:【生活健康app之首頁(yè)啟動(dòng)】(1)

jf_46214456 ? 來(lái)源:jf_46214456 ? 作者:jf_46214456 ? 2024-05-10 11:49 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

介紹

如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的健康生活應(yīng)用,主要功能包括:

  1. 用戶可以創(chuàng)建最多6個(gè)健康生活任務(wù)(早起,喝水,吃蘋(píng)果,每日微笑,刷牙,早睡),并設(shè)置任務(wù)目標(biāo)、是否開(kāi)啟提醒、提醒時(shí)間、每周任務(wù)頻率。
  2. 用戶可以在主頁(yè)面對(duì)設(shè)置的健康生活任務(wù)進(jìn)行打卡,其中早起、每日微笑、刷牙和早睡只需打卡一次即可完成任務(wù),喝水、吃蘋(píng)果需要根據(jù)任務(wù)目標(biāo)量多次打卡完成。
  3. 主頁(yè)可顯示當(dāng)天的健康生活任務(wù)完成進(jìn)度,當(dāng)天所有任務(wù)都打卡完成后,進(jìn)度為100%,并且用戶的連續(xù)打卡天數(shù)加一。
  4. 當(dāng)用戶連續(xù)打卡天數(shù)達(dá)到3、7、30、50、73、99天時(shí),可以獲得相應(yīng)的成就。成就在獲得時(shí)會(huì)以動(dòng)畫(huà)形式彈出,并可以在“成就”頁(yè)面查看。
  5. 用戶可以查看以前的健康生活任務(wù)完成情況。

本應(yīng)用的運(yùn)行效果如下圖所示:

%E5%81%A5%E5%BA%B7%E7%94%9F%E6%B4%BB.gif

相關(guān)概念

  • [@Observed 和 @ObjectLink]:@Observed適用于類,表示類中的數(shù)據(jù)變化由UI頁(yè)面管理;@ObjectLink應(yīng)用于被@Observed裝飾類的對(duì)象。
  • [@Consume 和 @Provide]:@Provide作為數(shù)據(jù)提供者,可以更新子節(jié)點(diǎn)的數(shù)據(jù),觸發(fā)頁(yè)面渲染。@Consume檢測(cè)到@Provide數(shù)據(jù)更新后,會(huì)發(fā)起當(dāng)前視圖的重新渲染。
  • [Flex]:一個(gè)功能強(qiáng)大的容器組件,支持橫向布局,豎向布局,子組件均分和流式換行布局。
  • [List]:List是很常用的滾動(dòng)類容器組件之一,它按照水平或者豎直方向線性排列子組件, List的子組件必須是ListItem,它的寬度默認(rèn)充滿List的寬度。
  • [TimePicker]:TimePicker是選擇時(shí)間的滑動(dòng)選擇器組件,默認(rèn)以00:00至23:59的時(shí)間區(qū)創(chuàng)建滑動(dòng)選擇器。
  • [Toggle]:組件提供勾選框樣式、狀態(tài)按鈕樣式及開(kāi)關(guān)樣式。
  • [后臺(tái)代理提醒]:使用后臺(tái)代理提醒能力后,應(yīng)用可以被凍結(jié)或退出,計(jì)時(shí)和彈出提醒的功能將被后臺(tái)系統(tǒng)服務(wù)代理。
  • [關(guān)系型數(shù)據(jù)庫(kù)(Relational Database,RDB)]:一種基于關(guān)系模型來(lái)管理數(shù)據(jù)的數(shù)據(jù)庫(kù)。

環(huán)境搭建

軟件要求

  • [DevEco Studio]版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。

硬件要求

  • 開(kāi)發(fā)板類型:[潤(rùn)和RK3568開(kāi)發(fā)板]。
  • OpenHarmony系統(tǒng):3.2 Release。

環(huán)境搭建

完成本篇Codelab我們首先要完成開(kāi)發(fā)環(huán)境的搭建,本示例以RK3568開(kāi)發(fā)板為例,參照以下步驟進(jìn)行:

  1. [獲取OpenHarmony系統(tǒng)版本]:標(biāo)準(zhǔn)系統(tǒng)解決方案(二進(jìn)制)。以3.2 Release版本為例:
  2. 搭建燒錄環(huán)境。
    1. [完成DevEco Device Tool的安裝]
    2. [完成RK3568開(kāi)發(fā)板的燒錄](méi)
  3. 搭建開(kāi)發(fā)環(huán)境。
    1. 開(kāi)始前請(qǐng)參考[工具準(zhǔn)備],完成DevEco Studio的安裝和開(kāi)發(fā)環(huán)境配置。
    2. 開(kāi)發(fā)環(huán)境配置完成后,請(qǐng)參考[使用工程向?qū)創(chuàng)建工程(模板選擇“Empty Ability”),選擇JS或者eTS語(yǔ)言開(kāi)發(fā)。
    3. 工程創(chuàng)建完成后,選擇使用[真機(jī)進(jìn)行調(diào)測(cè)]。
    4. 開(kāi)發(fā)前請(qǐng)熟悉鴻蒙開(kāi)發(fā)指導(dǎo)文檔:[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md]

代碼結(jié)構(gòu)解讀

本篇Codelab只對(duì)核心代碼進(jìn)行講解,完整代碼可以直接從gitee獲取。

├─entry/src/main/ets                 // 代碼區(qū)
│  ├─common
│  │  ├─constants
│  │  │  └─CommonConstants.ets       // 公共常量
│  │  ├─database
│  │  │  ├─rdb                       // 數(shù)據(jù)庫(kù)
│  │  │  │  ├─RdbHelper.ets
│  │  │  │  ├─RdbHelperImp.ets
│  │  │  │  ├─RdbUtil.ets
│  │  │  │  └─TableHelper.ets
│  │  │  └─tables                    // 數(shù)據(jù)庫(kù)接口
│  │  │     ├─DayInfoApi.ets
│  │  │     ├─GlobalInfoApi.ets
│  │  │     └─TaskInfoApi.ets
│  │  └─utils
│  │     ├─BroadCast.ets             // 通知
│  │     ├─GlobalContext.ets         // 全局上下文
│  │     ├─HealthDataSrcMgr.ets      // 數(shù)據(jù)管理單例
│  │     ├─Logger.ets                // 日志類
│  │     └─Utils.ets                 // 工具類
│  ├─entryability
│  │  └─EntryAbility.ets             // 程序入口類
│  ├─model                           // model
│  │  ├─AchieveModel.ets
│  │  ├─DatabaseModel.ets            // 數(shù)據(jù)庫(kù)model
│  │  ├─Mine.ets
│  │  ├─NavItemModel.ets             // 菜單欄model
│  │  ├─RdbColumnModel.ets           // 數(shù)據(jù)庫(kù)表數(shù)據(jù)
│  │  ├─TaskInitList.ets
│  │  └─WeekCalendarModel.ets        // 日歷model
│  ├─pages
│  │  ├─AdvertisingPage.ets          // 廣告頁(yè)
│  │  ├─MainPage.ets                 // 應(yīng)用主頁(yè)面
│  │  ├─MinePage.ets                 // 我的頁(yè)面
│  │  ├─SplashPage.ets               // 啟動(dòng)頁(yè)
│  │  ├─TaskEditPage.ets             // 任務(wù)編輯頁(yè)面
│  │  └─TaskListPage.ets             // 任務(wù)列表頁(yè)面
│  ├─service
│  │  └─ReminderAgent.ets            // 后臺(tái)提醒
│  ├─view
│  │  ├─dialog                       // 彈窗組件
│  │  │  ├─AchievementDialog.ets     // 成就彈窗
│  │  │  ├─CustomDialogView.ets      // 自定義彈窗
│  │  │  ├─TaskDetailDialog.ets      // 打卡彈窗
│  │  │  ├─TaskDialogView.ets        // 任務(wù)對(duì)話框
│  │  │  ├─TaskSettingDialog.ets     // 任務(wù)編輯相關(guān)彈窗
│  │  │  └─UserPrivacyDialog.ets
│  │  ├─home                         // 主頁(yè)面相關(guān)組件
│  │  │  ├─AddBtnComponent.ets       // 添加任務(wù)按鈕組件
│  │  │  ├─HomeTopComponent.ets      // 首頁(yè)頂部組件
│  │  │  ├─TaskCardComponent.ets     // 任務(wù)item組件件
│  │  │  └─WeekCalendarComponent.ets // 日歷組件
│  │  ├─task                         // 任務(wù)相關(guān)組件
│  │  │  ├─TaskDetailComponent.ets   // 任務(wù)編輯詳情組件
│  │  │  ├─TaskEditListItem.ets      // 任務(wù)編輯行內(nèi)容
│  │  │  └─TaskListComponent.ets     // 任務(wù)列表組件
│  │  ├─AchievementComponent.ets     // 成就頁(yè)面
│  │  ├─BadgeCardComponent.ets       // 勛章卡片組件
│  │  ├─BadgePanelComponent.ets      // 勛章面板組件
│  │  ├─HealthTextComponent.ets      // 自定義text組件
│  │  ├─HomeComponent.ets            // 首頁(yè)頁(yè)面
│  │  ├─ListInfo.ets                 // 用戶信息列表
│  │  ├─TitleBarComponent.ets        // 成就標(biāo)題組件
│  │  └─UserBaseInfo.ets             // 用戶基本信息
│  └─viewmodel                       // viewmodel
│     ├─AchievementInfo.ets          // 成就信息
│     ├─AchievementMapInfo.ets       // 成就map信息
│     ├─AchievementViewModel.ets     // 成就相關(guān)模塊
│     ├─BroadCastCallBackInfo.ets    // 通知回調(diào)信息
│     ├─CalendarViewModel.ets        // 日歷相關(guān)模塊
│     ├─CardInfo.ets                 // 成就卡片信息
│     ├─ColumnInfo.ets               // 數(shù)據(jù)庫(kù)表結(jié)構(gòu)
│     ├─CommonConstantsInfo.ets      // 公共常量信息
│     ├─DayInfo.ets                  // 每日信息
│     ├─GlobalInfo.ets               // 全局信息
│     ├─HomeViewModel.ets            // 首頁(yè)相關(guān)模塊
│     ├─PublishReminderInfo.ets      // 發(fā)布提醒信息
│     ├─ReminderInfo.ets             // 提醒信息
│     ├─TaskInfo.ets                 // 任務(wù)信息
│     ├─TaskViewModel.ets            // 任務(wù)設(shè)置相關(guān)模塊
│     ├─WeekCalendarInfo.ets         // 日歷信息
│     └─WeekCalendarMethodInfo.ets   // 日歷方法信息
└─entry/src/main/resources           // 資源文件夾

`HarmonyOSOpenHarmony鴻蒙文檔籽料:mau123789v直接拿`

搜狗高速瀏覽器截圖20240326151450.png

應(yīng)用架構(gòu)分析

本應(yīng)用的基本架構(gòu)如下圖所示,數(shù)據(jù)庫(kù)為其他服務(wù)提供基礎(chǔ)的用戶數(shù)據(jù),主要業(yè)務(wù)包括:用戶可以查看和編輯自己的健康任務(wù)并進(jìn)行打卡、查看成就。UI層提供了承載上述業(yè)務(wù)的UI界面。

應(yīng)用主頁(yè)面

本節(jié)將介紹如何給應(yīng)用添加一個(gè)啟動(dòng)頁(yè),設(shè)計(jì)應(yīng)用的主界面,以及首頁(yè)的界面開(kāi)發(fā)和數(shù)據(jù)展示。

啟動(dòng)頁(yè)

首先我們需要給應(yīng)用添加一個(gè)啟動(dòng)頁(yè),啟動(dòng)頁(yè)里我們需要用到一個(gè)定時(shí)器來(lái)實(shí)現(xiàn)啟動(dòng)頁(yè)展示固定時(shí)間后跳轉(zhuǎn)應(yīng)用主頁(yè)的功能,效果圖如下:

打開(kāi)應(yīng)用時(shí)會(huì)進(jìn)入此頁(yè)面,具體實(shí)現(xiàn)邏輯是:

通過(guò)修改/entry/src/main/ets/entryability里的loadContent路徑可以改變應(yīng)用的入口文件,我們需要把入口文件改為我們寫(xiě)的SplashPage啟動(dòng)頁(yè)面。

// EntryAbility.ets
windowStage.loadContent('pages/SplashPage', (err, data) = > {    
  if (err.code) {...}    
  Logger.info('windowStage','Succeeded in loading the content. Data: ' + JSON.stringify(data));
});

在SplashPage啟動(dòng)頁(yè)的文件里通過(guò)首選項(xiàng)來(lái)實(shí)現(xiàn)是否需要彈“權(quán)限管理”的彈窗,如果需要彈窗的情況下,用戶點(diǎn)擊同意權(quán)限后通過(guò)首選項(xiàng)對(duì)用戶的操作做持久化保存。相關(guān)代碼如下:

// SplashPage.ets
import data_preferences from '@ohos.data.preferences';
onConfirm() {
  let preferences = data_preferences.getPreferences(this.context, H_STORE);
  preferences.then((res) = > {
    res.put(IS_PRIVACY, true).then(() = > {
      res.flush();
      Logger.info('SplashPage','isPrivacy is put success');
    }).catch((err: Error) = > {
      Logger.info('SplashPage','isPrivacy put failed. Cause:' + err);
    });
  })
  this.jumpAdPage();
}
exitApp() {
  this.context.terminateSelf();
}
jumpAdPage() {
  setTimeout(() = > {
    router.replaceUrl({ url: 'pages/AdvertisingPage' });
  }, Const.LAUNCHER_DELAY_TIME);
}
aboutToAppear() {
  let preferences = data_preferences.getPreferences(this.context, H_STORE);
  preferences.then((res) = > {
    res.get(IS_PRIVACY, false).then((isPrivate) = > {
      if (isPrivate === true) {
        this.jumpAdPage();
      } else {
        this.dialogController.open();
      }
    });
  });
}

APP功能入口

我們需要給APP添加底部菜單欄,用于切換不同的應(yīng)用模塊,由于各個(gè)模塊之間屬于完全獨(dú)立的情況,并且不需要每次切換都進(jìn)行界面的刷新,所以我們用到了Tabs,TabContent組件。

本應(yīng)用一共有首頁(yè)(HomeIndex),成就(AchievementIndex)和我的(MineIndex)三個(gè)模塊,分別對(duì)應(yīng)Tabs組件的三個(gè)子組件TabContent。

// MainPage.ets
TabContent() {
  HomeIndex({ homeStore: $homeStore, editedTaskInfo: $editedTaskInfo, editedTaskID: $editedTaskID })
    .borderWidth({ bottom: 1 })
    .borderColor($r('app.color.primaryBgColor'))
}
.tabBar(this.TabBuilder(TabId.HOME))
.align(Alignment.Start)
TabContent() {
  AchievementIndex()
}
.tabBar(this.TabBuilder(TabId.ACHIEVEMENT))
TabContent() {
  MineIndex()
    .borderWidth({ bottom: 1 })
    .borderColor($r('app.color.primaryBgColor'))
}
.tabBar(this.TabBuilder(TabId.MINE))

首頁(yè)

首頁(yè)包含了任務(wù)信息的所有入口,包含任務(wù)列表的展示,任務(wù)的編輯和新增,上下滾動(dòng)的過(guò)程中頂部導(dǎo)航欄的漸變,日期的切換以及隨著日期切換界面任務(wù)列表跟著同步的功能,效果圖如下:

%E9%A6%96%E9%A1%B5.gif
具體代碼實(shí)現(xiàn)我們將在下邊分模塊進(jìn)行說(shuō)明:

  1. 導(dǎo)航欄背景漸變
    Scroll滾動(dòng)的過(guò)程中,在它的onScroll方法里我們通過(guò)計(jì)算它Y軸的偏移量來(lái)改變當(dāng)前界面的@State修飾的naviAlpha變量值,進(jìn)而改變頂部標(biāo)題的背景色,代碼實(shí)現(xiàn)如下:
    // HomeComponent.ets
    // 視圖滾動(dòng)的過(guò)程中處理導(dǎo)航欄的透明度
    onScrollAction() {  
      this.yOffset = this.scroller.currentOffset().yOffset;  
      if (this.yOffset > Const.DEFAULT_56) {    
        this.naviAlpha = 1; 
      } else {    
        this.naviAlpha = this.yOffset / Const.DEFAULT_56;
      }
    }
    
  2. 日歷組件
    日歷組件主要用到的是一個(gè)橫向滑動(dòng)的Scroll組件。
// WeekCalendarComponent.ets
   build() {    
     Row() {      
       Column() {        
         Row() {...}             
         Scroll(this.scroller) {          
           Row() {            
             ForEach(this.homeStore.dateArr, (item: WeekDateModel, index?: number) = > {              
               Column() {                
                 Text(item.weekTitle)                  
                   .fontColor(sameDate(item.date, this.homeStore.showDate) ? $r('app.color.blueColor') : $r('app.color.titleColor'))                                 
                 Divider()
                   .color(sameDate(item.date, this.homeStore.showDate) ? $r('app.color.blueColor') : $r('app.color.white'))                
                 Image(this.getProgressImg(item))                               
               } 
               .onClick(() = > WeekCalendarMethods.calenderItemClickAction(item, index, this.homeStore))            
             })          
           }       
         }
         ...               
         .onScrollEdge((event) = > this.onScrollEdgeAction(event))      
       }
       ...       
     }
     ...    
   }

手動(dòng)滑動(dòng)頁(yè)面時(shí),我們通過(guò)在onScrollEnd方法里計(jì)算Scroll的偏移量來(lái)實(shí)現(xiàn)分頁(yè)的效果,同時(shí)Scroll有提供scrollPage()方法可供我們點(diǎn)擊左右按鈕的時(shí)候來(lái)進(jìn)行頁(yè)面切換。

// WeekCalendarComponent.ets
import display from '@ohos.display';
...
// scroll滾動(dòng)停止時(shí)通過(guò)判斷偏移量進(jìn)行分頁(yè)處理
onScrollEndAction() {
  if (this.isPageScroll === false) {
    let page = Math.round(this.scroller.currentOffset().xOffset / this.scrollWidth);
    page = (this.isLoadMore === true) ? page + 1 : page;
    if (this.scroller.currentOffset().xOffset % this.scrollWidth != 0 || this.isLoadMore === true) {
      let xOffset = page * this.scrollWidth;
      this.scroller.scrollTo({ xOffset, yOffset: 0 } as ScrollTo);
      this.isLoadMore = false;
    }
    this.currentPage = this.homeStore.dateArr.length / Const.WEEK_DAY_NUM - page - 1;
    Logger.info('HomeIndex', 'onScrollEnd: page ' + page + ', listLength ' + this.homeStore.dateArr.length);
    let dayModel: WeekDateModel = this.homeStore.dateArr[Const.WEEK_DAY_NUM * page+this.homeStore.selectedDay];
    Logger.info('HomeIndex', 'currentItem: ' + JSON.stringify(dayModel) + ', selectedDay  ' + this.homeStore.selectedDay);
    this.homeStore!.setSelectedShowDate(dayModel!.date!.getTime());
  }
  this.isPageScroll = false;
}

我們?cè)谛枰赟croll滑動(dòng)到左邊邊緣的時(shí)候去請(qǐng)求更多的歷史數(shù)據(jù)以便Scroll能一直滑動(dòng),通過(guò)Scroll的onScrollEdge方法我們可以判斷它是否已滑到邊緣位置。

// WeekCalendarComponent.ets
onScrollEdgeAction(side: Edge) {
  if (side === Edge.Top && this.isPageScroll === false) {
    Logger.info('HomeIndex', 'onScrollEdge: currentPage ' + this.currentPage);
    if ((this.currentPage + 2) * Const.WEEK_DAY_NUM >= this.homeStore.dateArr.length) {
      Logger.info('HomeIndex', 'onScrollEdge: load more data');
      let date: Date = new Date(this.homeStore.showDate);
      date.setDate(date.getDate() - Const.WEEK_DAY_NUM);
      this.homeStore.getPreWeekData(date, () = > {});
      this.isLoadMore = true;
    }
  }
}

homeStore主要是請(qǐng)求數(shù)據(jù)庫(kù)的數(shù)據(jù)并對(duì)數(shù)據(jù)進(jìn)行處理進(jìn)而渲染到界面上。

// HomeViewModel.ets
public getPreWeekData(date: Date, callback: Function) {
  let weekCalendarInfo: WeekCalendarInfo = getPreviousWeek(date);
    
  // 請(qǐng)求數(shù)據(jù)庫(kù)數(shù)據(jù)
  DayInfoApi.queryList(weekCalendarInfo.strArr, (res: DayInfo[]) = > {

    // 數(shù)據(jù)處理
    ...  
    this.dateArr = weekCalendarInfo.arr.concat(...this.dateArr);
  })
}

同時(shí)我們還需要知道怎么根據(jù)當(dāng)天的日期計(jì)算出本周內(nèi)的所有日期數(shù)據(jù)。

// WeekCalendarModel.ets
export function getPreviousWeek(showDate: Date): WeekCalendarInfo {
  Logger.debug('WeekCalendarModel', 'get week date by date: ' + showDate.toDateString());
  let weekCalendarInfo: WeekCalendarInfo = new WeekCalendarInfo();
  let arr: Array< WeekDateModel > = [];
  let strArr: Array< string > = [];
  let currentDay = showDate.getDay() - 1;
  // 由于date的getDay()方法返回的是0-6代表周日到周六,我們界面上展示的周一-周日為一周,所以這里要將getDay()數(shù)據(jù)偏移一天
  let currentDay = showDate.getDay() - 1;
  if (showDate.getDay() === 0) {
    currentDay = 6;
  }
  // 將日期設(shè)置為當(dāng)前周第一天的數(shù)據(jù)(周一)
  showDate.setDate(showDate.getDate() - currentDay);
  for (let index = WEEK_DAY_NUM; index > 0; index--) {
    let tempDate = new Date(showDate);
    tempDate.setDate(showDate.getDate() - index);
    let dateStr = dateToStr(tempDate);
    strArr.push(dateStr);
    arr.push(new WeekDateModel(WEEK_TITLES[tempDate.getDay()], dateStr, tempDate));
  }
  Logger.debug('WeekCalendarModel', JSON.stringify(arr));
  weekCalendarInfo.arr = arr;
  weekCalendarInfo.strArr = strArr;
  return weekCalendarInfo;
}
  1. 懸浮按鈕
    由于首頁(yè)右下角有一個(gè)懸浮按鈕,所以首頁(yè)整體我們用了一個(gè)Stack組件,將右下角的懸浮按鈕和頂部的title放在滾動(dòng)組件層的上邊。

    // HomeComponent.ets
    build() {  
      Stack() {    
        Scroll(this.scroller) {      
          Column() {     
            ...   // 上部界面組件
            Column() {          
              ForEach(this.homeStore.getTaskListOfDay(), (item: TaskInfo) = > {            
                TaskCard({
                  taskInfoStr: JSON.stringify(item),
                  clickAction: (isClick: boolean) = > this.taskItemAction(item, isClick)
                })
                ...
              }, (item: TaskInfo) = > JSON.stringify(item))} 
          }   
        }
      }
      .onScroll(() = > {
        this.onScrollAction()
      })
      // 懸浮按鈕
      AddBtn({ clickAction: () = > {
        this.editTaskAction()
      } }) 
      // 頂部title 
      Row() {
        Text($r('app.string.EntryAbility_label'))
          .titleTextStyle()
          .fontSize($r('app.float.default_24'))
          .padding({ left: Const.THOUSANDTH_66 })
      }
      .width(Const.THOUSANDTH_1000)
      .height(Const.DEFAULT_56)
      .position({ x: 0, y: 0 })
      .backgroundColor(`rgba(${WHITE_COLOR_0X},${WHITE_COLOR_0X},${WHITE_COLOR_0X},${this.naviAlpha})`)
      CustomDialogView()
    } 
    .allSize() 
    .backgroundColor($r('app.color.primaryBgColor'))
    
  2. 界面跳轉(zhuǎn)及傳參
    首頁(yè)任務(wù)列表長(zhǎng)按時(shí)需要跳轉(zhuǎn)到對(duì)應(yīng)的任務(wù)編輯界面,同時(shí)點(diǎn)擊懸浮按鈕時(shí)需要跳轉(zhuǎn)到任務(wù)列表頁(yè)面。
    頁(yè)面跳轉(zhuǎn)需要在頭部引入router。

    // HomeComponent.ets
    import router from '@ohos.router';
    

    任務(wù)item的點(diǎn)擊事件代碼如下

    // HomeComponent.ets
    taskItemAction(item: TaskInfo, isClick: boolean): void {
      if (!this.homeStore.checkCurrentDay()) {
        return;
      }
      if (isClick) {  
        // 點(diǎn)擊任務(wù)打卡    
        let callback: CustomDialogCallback = { confirmCallback: (taskTemp: TaskInfo) = > {
          this.onConfirm(taskTemp)
        }, cancelCallback: () = > {
        } };
        this.broadCast.emit(BroadCastType.SHOW_TASK_DETAIL_DIALOG, [item, callback]); 
      } else {   
        // 長(zhǎng)按編輯任務(wù)    
        let editTaskStr: string = JSON.stringify(TaskMapById[item.taskID - 1]);
        let editTask: ITaskItem = JSON.parse(editTaskStr);
        ...
        router.pushUrl({ url: 'pages/TaskEditPage', params: { params: JSON.stringify(editTask) } });
      }
    }
    

任務(wù)創(chuàng)建與編輯

本節(jié)將介紹如何創(chuàng)建和編輯健康生活任務(wù)。

功能概述

用戶點(diǎn)擊懸浮按鈕進(jìn)入任務(wù)列表頁(yè),點(diǎn)擊任務(wù)列表可進(jìn)入對(duì)應(yīng)任務(wù)編輯的頁(yè)面中,對(duì)任務(wù)進(jìn)行詳細(xì)的設(shè)置,之后點(diǎn)擊完成按鈕編輯任務(wù)后將返回首頁(yè)。實(shí)現(xiàn)效果如下圖:

任務(wù)列表與編輯任務(wù)

這里主要為大家介紹添加任務(wù)列表頁(yè)的實(shí)現(xiàn)、任務(wù)編輯的實(shí)現(xiàn)、以及具體彈窗設(shè)置和編輯完成功能的邏輯實(shí)現(xiàn)。

任務(wù)列表頁(yè)

任務(wù)列表頁(yè)由包括上部分的標(biāo)題、返回按鈕以及正中間的任務(wù)列表組成。實(shí)現(xiàn)效果如圖:

使用Navigation以及List組件構(gòu)成元素,F(xiàn)orEach遍歷生成具體列表。這里是Navigation構(gòu)成頁(yè)面導(dǎo)航:

// TaskListPage.ets
Navigation() {
  Column() {
    // 頁(yè)面中間的列表
    TaskList() 
  }
  .width(Const.THOUSANDTH_1000)
  .justifyContent(FlexAlign.Center)
}
.size({ width: Const.THOUSANDTH_1000, height: Const.THOUSANDTH_1000 })
.title(Const.ADD_TASK_TITLE)
.titleMode(NavigationTitleMode.Mini)

列表右側(cè)有一個(gè)判斷是否開(kāi)啟的文字標(biāo)識(shí),點(diǎn)擊某個(gè)列表需要跳轉(zhuǎn)到對(duì)應(yīng)的任務(wù)編輯頁(yè)里。具體的列表實(shí)現(xiàn)如下:

// TaskListComponent.ets

@Component
export default struct TaskList {
  ...
  build() {
    List({ space: Const.LIST_ITEM_SPACE }) {
      ForEach(this.taskList, (item: ITaskItem) = > {
        ListItem() {
          Row() {
            Row() {
              Image(item?.icon)
                ...
              Text(item?.taskName).fontSize(Const.DEFAULT_20).fontColor($r('app.color.titleColor'))
            }.width(Const.THOUSANDTH_500)

            Blank()
              ...
			// 狀態(tài)改變
            if (item?.isOpen) {
              Text($r('app.string.already_open'))
                ...
            }
            Image($r('app.media.ic_right_grey'))
              ...
          }
          ...
        }
        ...
        .onClick(() = > {
          router.pushUrl({
            url: 'pages/TaskEditPage',
            params: {
              params: formatParams(item),
            }
          })
        })
        ...
      }, (item: ITaskItem) = > JSON.stringify(item))
    }
  ...
  }
}

任務(wù)編輯頁(yè)

任務(wù)編輯頁(yè)由上方的“編輯任務(wù)”標(biāo)題以及返回按鈕,主體內(nèi)容的List配置項(xiàng)和下方的完成按鈕組成,實(shí)現(xiàn)效果如圖:

由于每一個(gè)配置項(xiàng)功能不相同,且邏輯復(fù)雜,故將其拆分為五個(gè)獨(dú)立的組件。

這是任務(wù)編輯頁(yè)面,由Navigation和一個(gè)自定義組件TaskDetail構(gòu)成:

// TaskEditPage.ets
Navigation() {
  Column() {
    TaskDetail()
  }
  .width(Const.THOUSANDTH_1000)
  .height(Const.THOUSANDTH_1000)
}
.size({ width: Const.THOUSANDTH_1000, height: Const.THOUSANDTH_1000 })
.title(Const.EDIT_TASK_TITLE)
.titleMode(NavigationTitleMode.Mini)

自定義組件由List以及其子組件ListItem構(gòu)成:

// TaskDetailComponent.ets
List({ space: Const.LIST_ITEM_SPACE }) {
  ListItem() {
    TaskChooseItem()
  }
  ...
  ListItem() {
    TargetSetItem()
  }
  ...
  ListItem() {
    OpenRemindItem()
  }
  ...
  ListItem() {
    RemindTimeItem()
  }
  ...
  ListItem() {
    FrequencyItem()
  }
  ...
}
.width(Const.THOUSANDTH_940)

其中做了禁用判斷,需要任務(wù)打開(kāi)才可以點(diǎn)擊編輯:

// TaskDetailComponent.ets
.enabled(
  this.settingParams?.isOpen
)

一些特殊情況的禁用,如每日微笑、每日刷牙的目標(biāo)設(shè)置不可編輯:

// TaskDetailComponent.ets
.enabled(
  this.settingParams?.isOpen
    && this.settingParams?.taskID !== taskType.smile
    && this.settingParams?.taskID !== taskType.brushTeeth
)

提醒時(shí)間在開(kāi)啟提醒打開(kāi)之后才可以編輯:

// TaskDetailComponent.ets
.enabled(this.settingParams?.isOpen && this.settingParams?.isAlarm)

設(shè)置完成之后,點(diǎn)擊完成按鈕,會(huì)向數(shù)據(jù)庫(kù)更新現(xiàn)在進(jìn)行改變的狀態(tài)信息,并執(zhí)行之后的邏輯判斷:

// TaskDetailComponent.ets
addTask(taskInfo, context).then((res: number) = > {
  GlobalContext.getContext().setObject('taskListChange', true);
  // 成功的狀態(tài),成功后跳轉(zhuǎn)首頁(yè)
  router.back({
    url: 'pages/MainPage', 
    params: {
      editTask: this.backIndexParams(),
    }
  })
  Logger.info('addTaskFinished', JSON.stringify(res));
}).catch((error: Error) = > {
  // 失敗的狀態(tài),失敗后彈出提示,并打印錯(cuò)誤日志
  prompt.showToast({
    message: Const.SETTING_FINISH_FAILED_MESSAGE
  })
  Logger.error('addTaskFailed', JSON.stringify(error));
})

任務(wù)編輯彈窗

彈窗由封裝的自定義組件CustomDialogView注冊(cè)事件,并在點(diǎn)擊對(duì)應(yīng)的編輯項(xiàng)時(shí)進(jìn)行觸發(fā),從而打開(kāi)彈窗。

CustomDialogView引入實(shí)例并注冊(cè)事件:

// TaskDialogView.ets
targetSettingDialog: CustomDialogController = new CustomDialogController({ 
  builder: TargetSettingDialog(),
  autoCancel: true,
  alignment: DialogAlignment.Bottom,
  offset: { dx: Const.ZERO, dy: Const.MINUS_20 }
});
...

// 注冊(cè)事件
this.broadCast.on(BroadCastType.SHOW_TARGET_SETTING_DIALOG, () = > {
  this.targetSettingDialog.open();
})

點(diǎn)擊對(duì)應(yīng)的編輯項(xiàng)進(jìn)行觸發(fā):

// TaskDetailComponent.ets
.onClick(() = > {
  this.broadCast.emit(
    BroadCastType.SHOW_TARGET_SETTING_DIALOG);
})

自定義彈窗的實(shí)現(xiàn):

任務(wù)目標(biāo)設(shè)置的彈窗較為特殊,故單獨(dú)拿出來(lái)說(shuō)明。

因?yàn)槿蝿?wù)目標(biāo)設(shè)置有三種類型:

  • 早睡早起的時(shí)間
  • 喝水的量度
  • 吃蘋(píng)果的個(gè)數(shù)

如下圖所示:

故根據(jù)任務(wù)的ID進(jìn)行區(qū)分,將同一彈窗復(fù)用:

// TaskSettingDialog.ets
if ([taskType.getup, taskType.sleepEarly].indexOf(this.settingParams?.taskID) > Const.HAS_NO_INDEX) {
  TimePicker({
    selected: new Date(`${new Date().toDateString()} 8:00:00`),
  })
    .height(Const.THOUSANDTH_800)
    .useMilitaryTime(true)
    .onChange((value: TimePickerResult) = > {
      this.currentTime = formatTime(value);
    })
} else {
  TextPicker({ range: this.settingParams?.taskID === taskType.drinkWater ? this.drinkRange : this.appleRange })
    .width(Const.THOUSANDTH_900,)
    .height(Const.THOUSANDTH_800,)
    .onChange((value) = > {
      this.currentValue = value?.split(' ')[0];
    })
}

彈窗確認(rèn)的時(shí)候?qū)⑿薷暮玫闹蒂x予該項(xiàng)設(shè)置,如不符合規(guī)則,將彈出提示:

// TaskSettingDialog.ets
// 校驗(yàn)規(guī)則
compareTime(startTime: string, endTime: string) {
  if (returnTimeStamp(this.currentTime) < returnTimeStamp(startTime) ||
    returnTimeStamp(this.currentTime) > returnTimeStamp(endTime)) {
    prompt.showToast({
      message: Const.CHOOSE_TIME_OUT_RANGE
    })
    return false;
  }
  return true;
}
// 設(shè)置修改項(xiàng)
setTargetValue() {
  if (this.settingParams?.taskID === taskType.getup) {
    if (!this.compareTime(Const.GET_UP_EARLY_TIME, Const.GET_UP_LATE_TIME)) {
      return;
    }
    this.settingParams.targetValue = this.currentTime;
    return;
  }
  if (this.settingParams?.taskID === taskType.sleepEarly) {
    if (!this.compareTime(Const.SLEEP_EARLY_TIME, Const.SLEEP_LATE_TIME)) {
      return;
    }
    this.settingParams.targetValue = this.currentTime;
    return;
  }
  this.settingParams.targetValue = this.currentValue;
}

其余彈窗實(shí)現(xiàn)基本類似,這里不再贅述。

后臺(tái)代理提醒

健康生活A(yù)pp中提供了任務(wù)提醒功能,我們用系統(tǒng)提供的后臺(tái)代理提醒reminderAgent接口完成相關(guān)的開(kāi)發(fā)。

說(shuō)明: 后臺(tái)代理提醒接口需要在module.json5中申請(qǐng)ohos.permission.PUBLISH_AGENT_REMINDER權(quán)限,代碼如下:

// module.json5
"requestPermissions": [
  {
    "name": "ohos.permission.PUBLISH_AGENT_REMINDER"
  }
]

后臺(tái)代理提醒entrysrcmainetsserviceReminderAgent.ts文件中提供了發(fā)布提醒任務(wù)、查詢提醒任務(wù)、刪除提醒任務(wù)三個(gè)接口供任務(wù)編輯頁(yè)面調(diào)用,跟隨任務(wù)提醒的開(kāi)關(guān)增加、更改、刪除相關(guān)后臺(tái)代理提醒,代碼如下:開(kāi)發(fā)前請(qǐng)熟悉鴻蒙開(kāi)發(fā)指導(dǎo)文檔:[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md]

// ReminderAgent.ets
import reminderAgent from '@ohos.reminderAgentManager';
import notification from '@ohos.notificationManager';
import preferences from '@ohos.data.preferences';
import Logger from '../common/utils/Logger';
import { CommonConstants as Const } from '../common/constants/CommonConstants';
import ReminderInfo from '../viewmodel/ReminderInfo';
import PublishReminderInfo from '../viewmodel/PublishReminderInfo';

// 發(fā)布提醒
function publishReminder(params: PublishReminderInfo, context: Context) {
  if (!params) {
    Logger.error(Const.REMINDER_AGENT_TAG, 'publishReminder params is empty');
    return;
  }
  let notifyId: string = params.notificationId.toString();
  hasPreferencesValue(context, notifyId, (preferences: preferences.Preferences, hasValue: boolean) = > {
    if (hasValue) {
      preferences.get(notifyId, -1, (error: Error, value: preferences.ValueType) = > {
        if (typeof value !== 'number') {
          return;
        }
        if (value >= 0) {
          reminderAgent.cancelReminder(value).then(() = > {
            processReminderData(params, preferences, notifyId);
          }).catch((err: Error) = > {
            Logger.error(Const.REMINDER_AGENT_TAG, `cancelReminder err: ${err}`);
          });
        } else {
          Logger.error(Const.REMINDER_AGENT_TAG, 'preferences get value error ' + JSON.stringify(error));
        }
      });
    } else {
      processReminderData(params, preferences, notifyId);
    }
  });
}

// 取消提醒
function cancelReminder(reminderId: number, context: Context) {
  if (!reminderId) {
    Logger.error(Const.REMINDER_AGENT_TAG, 'cancelReminder reminderId is empty');
    return;
  }
  let reminder: string = reminderId.toString();
  hasPreferencesValue(context, reminder, (preferences: preferences.Preferences, hasValue: boolean) = > {
    if (!hasValue) {
      Logger.error(Const.REMINDER_AGENT_TAG, 'cancelReminder preferences value is empty');
      return;
    }
    getPreferencesValue(preferences, reminder);
  });
}

// 可通知ID
function hasNotificationId(params: number) {
  if (!params) {
    Logger.error(Const.REMINDER_AGENT_TAG, 'hasNotificationId params is undefined');
    return;
  }
  return reminderAgent.getValidReminders().then((reminders) = > {
    if (!reminders.length) {
      return false;
    }
    let notificationIdList: Array< number > = [];
    for (let i = 0; i < reminders.length; i++) {
      let notificationId = reminders[i].notificationId;
      if (notificationId) {
        notificationIdList.push(notificationId);
      }
    }
    const flag = notificationIdList.indexOf(params);
    return flag === -1 ? false : true;
  });
}

function hasPreferencesValue(context: Context, hasKey: string, callback: Function) {
  let preferencesPromise = preferences.getPreferences(context, Const.H_STORE);
  preferencesPromise.then((preferences: preferences.Preferences) = > {
    preferences.has(hasKey).then((hasValue: boolean) = > {
      callback(preferences, hasValue);
    });
  });
}

// 進(jìn)程提醒數(shù)據(jù)
function processReminderData(params: PublishReminderInfo, preferences: preferences.Preferences, notifyId: string) {
  let timer = fetchData(params);
  reminderAgent.publishReminder(timer).then((reminderId: number) = > {
    putPreferencesValue(preferences, notifyId, reminderId);
  }).catch((err: Error) = > {
    Logger.error(Const.REMINDER_AGENT_TAG, `publishReminder err: ${err}`);
  });
}

// 獲取數(shù)據(jù)
function fetchData(params: PublishReminderInfo): reminderAgent.ReminderRequestAlarm {
  return {
    reminderType: reminderAgent.ReminderType.REMINDER_TYPE_ALARM,
    hour: params.hour || 0,
    minute: params.minute || 0,
    daysOfWeek: params.daysOfWeek || [],
    wantAgent: {
      pkgName: Const.PACKAGE_NAME,
      abilityName: Const.ENTRY_ABILITY
    },
    title: params.title || '',
    content: params.content || '',
    notificationId: params.notificationId || -1,
    slotType: notification.SlotType.SOCIAL_COMMUNICATION
  }
}

function putPreferencesValue(preferences: preferences.Preferences, putKey: string, putValue: number) {
  preferences.put(putKey, putValue).then(() = > {
    preferences.flush();
  }).catch((error: Error) = > {
    Logger.error(Const.REMINDER_AGENT_TAG, 'preferences put value error ' + JSON.stringify(error));
  });
}

function getPreferencesValue(preferences: preferences.Preferences, getKey: string) {
  preferences.get(getKey, -1).then((value: preferences.ValueType) = > {
    if (typeof value !== 'number') {
      return;
    }
    if (value >= 0) {
      reminderAgent.cancelReminder(value).then(() = > {
        Logger.info(Const.REMINDER_AGENT_TAG, 'cancelReminder promise success');
      }).catch((err: Error) = > {
        Logger.error(Const.REMINDER_AGENT_TAG, `cancelReminder err: ${err}`);
      });
    }
  }).catch((error: Error) = > {
    Logger.error(Const.REMINDER_AGENT_TAG, 'preferences get value error ' + JSON.stringify(error));
  });
}

const reminder = {
  publishReminder: publishReminder,
  cancelReminder: cancelReminder,
  hasNotificationId: hasNotificationId
} as ReminderInfo

export default reminder;

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 鴻蒙
    +關(guān)注

    關(guān)注

    60

    文章

    2617

    瀏覽量

    44022
  • OpenHarmony
    +關(guān)注

    關(guān)注

    29

    文章

    3851

    瀏覽量

    18566
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    3562-物聯(lián)網(wǎng)模塊開(kāi)發(fā)案

    3562-物聯(lián)網(wǎng)模塊開(kāi)發(fā)案例 本文檔主要介紹基于 TL3562-EVM 評(píng)估板的物聯(lián)網(wǎng)模塊開(kāi)發(fā)案例,適用開(kāi)發(fā)環(huán)境: Windows 開(kāi)發(fā)環(huán)境:Windows 7 64bit、Wind
    的頭像 發(fā)表于 03-05 10:59 ?1422次閱讀
    3562-物聯(lián)網(wǎng)模塊<b class='flag-5'>開(kāi)發(fā)案</b>例

    HarmonyOS開(kāi)發(fā)案例:【購(gòu)物車app

    OpenHarmony ArkUI框架提供了豐富的動(dòng)畫(huà)組件和接口,開(kāi)發(fā)者可以根據(jù)實(shí)際場(chǎng)景和開(kāi)發(fā)需求,選用豐富的動(dòng)畫(huà)組件和接口來(lái)實(shí)現(xiàn)不同的動(dòng)畫(huà)效果。
    的頭像 發(fā)表于 05-14 18:19 ?1471次閱讀
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>開(kāi)發(fā)案</b>例:【購(gòu)物車<b class='flag-5'>app</b>】

    HarmonyOS開(kāi)發(fā)案例:【生活健康app之實(shí)現(xiàn)打卡功能】(2)

    首頁(yè)會(huì)展示當(dāng)前用戶已經(jīng)開(kāi)啟的任務(wù)列表,每條任務(wù)會(huì)顯示對(duì)應(yīng)的任務(wù)名稱以及任務(wù)目標(biāo)、當(dāng)前任務(wù)完成情況。用戶只可對(duì)當(dāng)天任務(wù)進(jìn)行打卡操作,用戶可以根據(jù)需要對(duì)任務(wù)列表中相應(yīng)的任務(wù)進(jìn)行點(diǎn)擊打卡。如果任務(wù)列表中的每個(gè)任務(wù)都在當(dāng)天完成則為連續(xù)打卡一天,連續(xù)打卡多天會(huì)獲得成就徽章。
    的頭像 發(fā)表于 05-10 15:29 ?1107次閱讀
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>開(kāi)發(fā)案</b>例:【<b class='flag-5'>生活</b><b class='flag-5'>健康</b><b class='flag-5'>app</b>之實(shí)現(xiàn)打卡功能】(2)

    HarmonyOS IoT 硬件開(kāi)發(fā)案例分享

    ``許思維老師HiSpark Wi-Fi IoT 開(kāi)發(fā)案例分享:案例一:AHT20溫濕度傳感器開(kāi)發(fā)、調(diào)試;案例二:oled屏驅(qū)動(dòng)庫(kù)移植,調(diào)試;案例三:用OLED屏播放視頻,Wi-Fi 和 TCP/IP 綜合應(yīng)用。 ``
    發(fā)表于 10-27 17:30

    harmonyOS開(kāi)發(fā)APP如何訪問(wèn)Webservice?

    我接到一個(gè)項(xiàng)目,需要用到HarmonyOS開(kāi)發(fā)APP做為移動(dòng)手機(jī)查詢和收到報(bào)警數(shù)據(jù),具體是這樣的,在C/S加B/S的系統(tǒng)框架下我們有數(shù)據(jù)庫(kù)服務(wù)器和Web服務(wù)器,有widows桌面應(yīng)用和Web瀏覽器
    發(fā)表于 03-28 10:14

    鴻蒙智聯(lián)生態(tài)產(chǎn)品《接入智慧生活App開(kāi)發(fā)指導(dǎo)》(官方更新版)

    HarmonyOS Connect生態(tài)產(chǎn)品應(yīng)用開(kāi)發(fā)過(guò)程中,很多開(kāi)發(fā)者對(duì)于如何接入智慧生活App還存在一些疑問(wèn),如:如何選擇合適的
    發(fā)表于 04-26 15:00

    HarmonyOS APP打包運(yùn)行和調(diào)試應(yīng)用開(kāi)發(fā)步驟

    在進(jìn)行HarmonyOS應(yīng)用開(kāi)發(fā)前,您應(yīng)該掌握HarmonyOS應(yīng)用的邏輯結(jié)構(gòu)。HarmonyOS應(yīng)用發(fā)布形態(tài)為APP Pack(Appli
    發(fā)表于 05-24 14:27

    4天帶你上手HarmonyOS ArkUI開(kāi)發(fā)——《HarmonyOS ArkUI入門(mén)訓(xùn)練營(yíng)之健康生活實(shí)戰(zhàn)》

    HarmonyOS ArkUI入門(mén)訓(xùn)練營(yíng)之健康飲食應(yīng)用》是面向入門(mén)開(kāi)發(fā)者打造的實(shí)戰(zhàn)課程系列。特邀華為終端BG高級(jí)開(kāi)發(fā)工程師作為本次訓(xùn)練營(yíng)講師,以
    發(fā)表于 01-05 11:49

    App開(kāi)發(fā)案例教程PDF電子書(shū)免費(fèi)下載

    App開(kāi)發(fā)案例教程》通過(guò)一個(gè)實(shí)例,介紹App設(shè)計(jì)、開(kāi)發(fā)直至上線的全過(guò)程,引導(dǎo)讀者在較短時(shí)間內(nèi)熟悉一個(gè)較大規(guī)模的App應(yīng)用系統(tǒng)的
    發(fā)表于 08-05 08:00 ?38次下載
    <b class='flag-5'>App</b><b class='flag-5'>開(kāi)發(fā)案</b>例教程PDF電子書(shū)免費(fèi)下載

    華為開(kāi)發(fā)者分論壇HarmonyOS學(xué)生公開(kāi)課-OpenHarmony Codelabs開(kāi)發(fā)案

    2021華為開(kāi)發(fā)者分論壇HarmonyOS學(xué)生公開(kāi)課-OpenHarmony Codelabs開(kāi)發(fā)案
    的頭像 發(fā)表于 10-24 11:25 ?2165次閱讀
    華為<b class='flag-5'>開(kāi)發(fā)</b>者分論壇<b class='flag-5'>HarmonyOS</b>學(xué)生公開(kāi)課-OpenHarmony Codelabs<b class='flag-5'>開(kāi)發(fā)案</b>例

    HarmonyOS家庭寵物健康監(jiān)測(cè)系統(tǒng)開(kāi)發(fā)方案

    本期我們給大家?guī)?lái)的是家庭寵物健康監(jiān)測(cè)系統(tǒng)開(kāi)發(fā)者楊光的分享,希望能給你的HarmonyOS開(kāi)發(fā)之旅帶來(lái)啟發(fā)~
    的頭像 發(fā)表于 03-03 10:24 ?3086次閱讀

    如何接入智慧生活App

     在HarmonyOS Connect生態(tài)產(chǎn)品應(yīng)用開(kāi)發(fā)過(guò)程中,很多開(kāi)發(fā)者對(duì)于如何接入智慧生活App還存在一些疑問(wèn),如:如何選擇合適的
    的頭像 發(fā)表于 04-25 10:42 ?4691次閱讀
    如何接入智慧<b class='flag-5'>生活</b><b class='flag-5'>App</b>

    淘寶與華為合作將基于HarmonyOS NEXT啟動(dòng)鴻蒙原生應(yīng)用開(kāi)發(fā)

    1月25日,淘寶與華為舉辦鴻蒙合作簽約儀式,宣布將基于HarmonyOS NEXT啟動(dòng)鴻蒙原生應(yīng)用開(kāi)發(fā)
    的頭像 發(fā)表于 01-26 16:14 ?1489次閱讀

    HarmonyOS開(kāi)發(fā)案例:【app初始啟動(dòng)

    基于自定義彈框、首選項(xiàng)和頁(yè)面路由實(shí)現(xiàn)一個(gè)模擬應(yīng)用首次啟動(dòng)的案例。
    的頭像 發(fā)表于 04-18 22:04 ?790次閱讀
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>開(kāi)發(fā)案</b>例:【<b class='flag-5'>app</b>初始<b class='flag-5'>啟動(dòng)</b>】

    HarmonyOS開(kāi)發(fā)案例:【生活健康app之獲取成就】(3)

    成就頁(yè)面展示用戶可以獲取的所有勛章,當(dāng)用戶滿足一定的條件時(shí),將點(diǎn)亮本頁(yè)面對(duì)應(yīng)的勛章,沒(méi)有得到的成就勛章處于熄滅狀態(tài)。共有六種勛章,當(dāng)用戶連續(xù)完成任務(wù)打卡3天、7天、30天、50天、73天、99天時(shí),可以獲得對(duì)應(yīng)的“連續(xù)xx天達(dá)成”勛章。
    的頭像 發(fā)表于 05-10 17:18 ?532次閱讀
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>開(kāi)發(fā)案</b>例:【<b class='flag-5'>生活</b><b class='flag-5'>健康</b><b class='flag-5'>app</b>之獲取成就】(3)