鴻蒙開發(fā)實(shí)戰(zhàn)案例:桌面卡片實(shí)現(xiàn)案例

介紹

桌面卡片是比較常見的功能,本案例詳細(xì)列舉了卡片開發(fā)的大部分功能,如使用postCardAction接口快速拉起卡片提供方應(yīng)用的指定UIAbility,通過message事件刷新卡片內(nèi)容等,為開發(fā)者提供了卡片功能的展示。

效果圖預(yù)覽

使用說明

  1. 長(zhǎng)按應(yīng)用,添加卡片到桌面。
  2. 卡片內(nèi)可滑動(dòng)選擇案例,點(diǎn)擊可進(jìn)入案例詳情。
  3. 部分案例無詳情頁時(shí),點(diǎn)擊跳轉(zhuǎn)到首頁瀑布流。

實(shí)現(xiàn)思路

  1. 新建卡片
  2. 配置formconfig
  3. 編寫卡片UI代碼
  4. 觸發(fā)刷新事件
  5. 觸發(fā)點(diǎn)擊事件

實(shí)現(xiàn)步驟

本例涉及的關(guān)鍵特性和實(shí)現(xiàn)方案如下:

  1. 新建卡片。右鍵點(diǎn)擊entry目錄,選擇新建->Service Widget->Dynamic Widget,其中Dynamic Widget為動(dòng)態(tài)卡片,Static Widget為靜態(tài)卡片。此時(shí)會(huì)生成幾個(gè)文件:配置文件form_config.json;卡片AbilityEntryFormAbility.ets;卡片組件WidgetCard.ets。

  2. 新建卡片后,根據(jù)需要(如卡片大小,刷新時(shí)間,動(dòng)態(tài)靜態(tài)卡片設(shè)置)配置form_config.json。

{
  "forms": [
    {
      "name": "widget", // 卡片的名稱。
      "displayName": "$string:widget_display_name", // 卡片的顯示名稱。
      "description": "$string:widget_desc", // 卡片的描述。 
      "src": "./ets/widget/pages/WidgetCard.ets", // 卡片對(duì)應(yīng)的UI代碼的完整路徑。
      "uiSyntax": "arkts", // 卡片的類型
      "window": { // 用于定義與顯示窗口相關(guān)的配置。
        "designWidth": 720,
        "autoDesignWidth": true
      },
      "colorMode": "auto", // 卡片的主題樣式。
      "isDynamic": true, // 卡片是否為動(dòng)態(tài)卡片。
      "isDefault": true, // 卡片是否為默認(rèn)卡片。
      "updateEnabled": true, // 卡片是否支持周期性刷新。
      "scheduledUpdateTime": "10:30", // 卡片的定點(diǎn)刷新的時(shí)刻。
      "updateDuration": 1, // 卡片定時(shí)刷新的更新周期,單位為30分鐘,取值為自然數(shù)。
      "defaultDimension": "6*4", // 卡片的默認(rèn)外觀規(guī)格。
      "supportDimensions": [ // 卡片支持的外觀規(guī)格,取值范圍。
        "6*4"
      ]
    }
  ]
}
  1. 編寫卡片UI代碼。在主文件WidgetCard.ets中添加UI組件,需要注意的是:ArkTS卡片存在較多約束(如不支持導(dǎo)入共享包),較多邏輯不可在卡片中使用,在使用時(shí)需要根據(jù)文檔進(jìn)行操作。

  2. 編寫跳轉(zhuǎn)事件:當(dāng)應(yīng)用未被拉起時(shí),點(diǎn)擊某個(gè)卡片時(shí)跳轉(zhuǎn)到具體的案例頁面。在EntryAbility.ets中補(bǔ)充邏輯:onCreate生命周期內(nèi)獲取want.parameters.params判斷卡片內(nèi)容的跳轉(zhuǎn)。

// EntryAbility.ets
export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // ...
    // 桌面卡片判斷跳轉(zhuǎn)內(nèi)容
    if (want?.parameters?.params) {
      // want.parameters.params 對(duì)應(yīng) postCardAction() 中 params 內(nèi)容
      let params: Record<string, Object> = JSON.parse(want.parameters.params as string);
      this.selectPage = params.targetPage as string;
    }
    // ...
  }
}
  1. 編寫跳轉(zhuǎn)事件:當(dāng)應(yīng)用在后臺(tái)時(shí),點(diǎn)擊某個(gè)卡片時(shí)跳轉(zhuǎn)到具體的案例頁面??蓮膐nNewWant生命周期獲取want.parameters.params判斷卡片內(nèi)容的跳轉(zhuǎn)。
// EntryAbility.ets
export default class EntryAbility extends UIAbility {
  // 如果UIAbility已在后臺(tái)運(yùn)行,在收到Router事件后會(huì)觸發(fā)onNewWant生命周期回調(diào)
  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    if (want?.parameters?.params) {
      // want.parameters.params 對(duì)應(yīng) postCardAction() 中 params 內(nèi)容
      let params: Record<string, Object> = JSON.parse(want.parameters.params as string);
      this.selectPage = params.targetPage as string;
    } else {
      this.selectPage = '';
    }
    if (this.currentWindowStage !== null) {
      //  存在窗口時(shí)點(diǎn)擊卡片后進(jìn)行頁面跳轉(zhuǎn)
      if (this.selectPage) {
        waterFlowData.forEach((item: SceneModuleInfo) => {
          let index = item.appUri.indexOf(this.selectPage);
          if (index > -1) {
            if (DynamicsRouter.appRouterStack.slice(-1)[0].name !== item.appUri) {
              DynamicsRouter.clear();
              DynamicsRouter.pushUri(item.appUri);
            }
            return;
          }
        })
        this.selectPage = '';
      }
    }
  }
}
  1. 具體跳轉(zhuǎn)邏輯編寫。在onWindowStageCreate生命周期內(nèi)進(jìn)行具體的跳轉(zhuǎn)邏輯。
// EntryAbility.ets
export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    // 判斷是否存在窗口可進(jìn)行頁面跳轉(zhuǎn)
    if (this.currentWindowStage === null) {
       this.currentWindowStage = windowStage;
    }
    //  點(diǎn)擊卡片后進(jìn)行頁面跳轉(zhuǎn)
    if (this.selectPage) {
      this.storage.setOrCreate('formNavigationRouter', this.selectPage);
      windowStage.loadContent('pages/EntryView', this.storage, (err, data) => {
        if (err.code) {
          logger.error(DOMAIN_NUMBER.toString(), TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
          return;
        }
        logger.info(DOMAIN_NUMBER.toString(), TAG, 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
      });
    }
    // ...
   }
}
  1. 編寫跳轉(zhuǎn)事件:在EntryAbility.ets編寫事件后,同時(shí)在接收案例參數(shù)的EntryView.ets內(nèi)處理頁面跳轉(zhuǎn)邏輯。通過和DynamicsRouter內(nèi)的數(shù)據(jù)對(duì)比,判斷通過storage傳入的頁面是哪一個(gè),然后執(zhí)行pushUri跳轉(zhuǎn)。
@Entry(storage)
@Component
struct EntryView {
  onPageShow(): void {
  // 從卡片進(jìn)入頁面時(shí)判斷具體跳轉(zhuǎn)頁面
  if (this.formRouter) {
    waterFlowData.forEach((item: SceneModuleInfo) => {
      let index = item.appUri.indexOf(this.formRouter);
      if (index > -1) {
        if (DynamicsRouter.appRouterStack.slice(-1)[0].name !== item.appUri) {
          DynamicsRouter.clear();
          DynamicsRouter.pushUri(item.appUri);
        }
        return;
      }
    })
    this.formRouter = '';
  }
}
}
  1. 判斷卡片大?。韩@取卡片詳情,根據(jù)寬高比獲取卡片的規(guī)格,不同規(guī)格顯示內(nèi)容不同。在EntryFormAbility.ets中補(bǔ)充點(diǎn)擊卡片進(jìn)入時(shí)查找對(duì)應(yīng)案例的邏輯。在onAddForm生命周期內(nèi)做卡片生成時(shí),createFormBindingData方法傳遞屬性。
// EntryFormAbility.ets
export default class EntryFormAbility extends FormExtensionAbility {
   // 卡片對(duì)象集合
  onAddForm(want: Want): formBindingData.FormBindingData {
    let isLongCard: boolean = true;
    if ((want.parameters?.[formInfo.FormParam.WIDTH_KEY] as number) /
      (want.parameters?.[formInfo.FormParam.HEIGHT_KEY] as number) > 0.666) {
      isLongCard = false;
    }
    // 使用方創(chuàng)建卡片時(shí)觸發(fā),提供方需要返回卡片數(shù)據(jù)綁定類
    let obj: Record<string, string | boolean> = {
      'title': 'titleOnAddForm',
      'isLongCard': isLongCard
    };
    let formData: formBindingData.FormBindingData = formBindingData.createFormBindingData(obj);
    return formData;
  }
}
  1. 編寫刷新事件:當(dāng)定時(shí)更新或定點(diǎn)更新觸發(fā)時(shí),需要更新卡片內(nèi)容。onUpdateForm生命周期發(fā)生在定時(shí)更新/定點(diǎn)更新/卡片使用方主動(dòng)請(qǐng)求更新時(shí),在方法內(nèi)增加獲取案例數(shù)據(jù)的功能。
// EntryFormAbility.ets
export default class EntryFormAbility extends FormExtensionAbility {
   // 網(wǎng)絡(luò)獲取README數(shù)據(jù)并利用formProvider.updateForm更新到卡片
   async getData(formId: string) {
      let detail: CASES[] = [];
      let httpRequest = http.createHttp();
      let webData: http.HttpResponse = await httpRequest.request(URL);
      if (webData?.responseCode == http.ResponseCode.OK) {
         try {
            detail = this.formatData(webData.result.toString());
            hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onFormEvent' + 'webData.result:' + webData.result);

            class FormDataClass {
               detail: CASES[] = detail;
            }

            let formData = new FormDataClass();
            let formInfo = formBindingData.createFormBindingData(formData);
            await formProvider.updateForm(formId, formInfo);
            hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'FormAbility updateForm success.');
         } catch (error) {
            hilog.error(DOMAIN_NUMBER, TAG, `FormAbility updateForm failed: ${JSON.stringify(error)}`);
         }
      } else {
         hilog.error(DOMAIN_NUMBER, TAG, `ArkTSCard download task failed`);
         let param: Record<string, string> = {
            'text': '刷新失敗'
         };
         let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(param);
         formProvider.updateForm(formId, formInfo);
      }
      httpRequest.destroy();
   }

   async onUpdateForm(formId: string): Promise<void> {
      // 若卡片支持定時(shí)更新/定點(diǎn)更新/卡片使用方主動(dòng)請(qǐng)求更新功能,則提供方需要重寫該方法以支持?jǐn)?shù)據(jù)更新
      hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onUpdateForm');
      this.getData(formId);
   }
}
  1. 編寫刷新事件:手動(dòng)刷新內(nèi)容時(shí),需要更新卡片內(nèi)容。onFormEvent生命周期發(fā)生在卡片主動(dòng)通過postCardAction接口觸發(fā)message事件。
// EntryFormAbility.ets
export default class EntryFormAbility extends FormExtensionAbility {
   // 網(wǎng)絡(luò)獲取README數(shù)據(jù)并利用formProvider.updateForm更新到卡片
   async getData(formId: string) {
      let detail: CASES[] = [];
      let httpRequest = http.createHttp();
      let webData: http.HttpResponse = await httpRequest.request(URL);
      if (webData?.responseCode == http.ResponseCode.OK) {
         try {
            detail = this.formatData(webData.result.toString());
            hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onFormEvent' + 'webData.result:' + webData.result);

            class FormDataClass {
               detail: CASES[] = detail;
            }

            let formData = new FormDataClass();
            let formInfo = formBindingData.createFormBindingData(formData);
            await formProvider.updateForm(formId, formInfo);
            hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'FormAbility updateForm success.');
         } catch (error) {
            hilog.error(DOMAIN_NUMBER, TAG, `FormAbility updateForm failed: ${JSON.stringify(error)}`);
         }
      } else {
         hilog.error(DOMAIN_NUMBER, TAG, `ArkTSCard download task failed`);
         let param: Record<string, string> = {
            'text': '刷新失敗'
         };
         let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(param);
         formProvider.updateForm(formId, formInfo);
      }
      httpRequest.destroy();
   }

   async onFormEvent(formId: string, message: string): Promise<void> {
      this.getData(formId);
   }
}
  1. 編寫刷新事件:參數(shù)傳到卡片組件內(nèi),組件接收參數(shù)。處理WidgetCard.ets卡片內(nèi)邏輯??ㄆ撁嬷惺褂肔ocalStorageProp裝飾需要刷新的卡片數(shù)據(jù)。
let casesCardInfo = new LocalStorage();
@Entry(casesCardInfo)
@Component
struct Widget_DynamicCard {
  @LocalStorageProp('detail') detail: CASES[] = []; // 卡片對(duì)象集合
  private swiperController: SwiperController = new SwiperController();
  @LocalStorageProp('isLongCard') isLongCard: boolean = false;

  build() {
    // ...
  }
}

寫在最后

  • 如果你覺得這篇內(nèi)容對(duì)你還蠻有幫助,我想邀請(qǐng)你幫我三個(gè)小忙:
  • 點(diǎn)贊,轉(zhuǎn)發(fā),有你們的 『點(diǎn)贊和評(píng)論』,才是我創(chuàng)造的動(dòng)力。
  • 關(guān)注小編,同時(shí)可以期待后續(xù)文章ing??,不定期分享原創(chuàng)知識(shí)。
  • 想要獲取更多完整鴻蒙最新學(xué)習(xí)知識(shí)點(diǎn),請(qǐng)移步前往小編:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容