鸿蒙卡片开发保姆级教程

卡片

1. 卡片概念

  1. 什么是卡片? 卡片用来显示或者提示一些基本信息或者进行一些基本操作。注意不能做重逻辑,所有重要逻辑全部交给应用
  2. 如果是元服务如何唤醒? 因为元服务不提供桌面应用图标,我们可以通过用户手动的方式在桌面上添加一张卡片,通过点击卡片来唤起元服务。

2. 创建卡片

  1. 在编辑器中创建

  2. 选择动态卡片




    卡片的特点
    1.卡片只能承载少量的内容和交互
    2. 卡片可以充当元服务icon作为入口,默认提供一张服务卡片作为入口
    3. 普通应用也可以添加服务卡片,但默认没有添加卡片

    元服务和普通应用的区别

  3. 添加卡片

3. ArkTS卡片实现原理

4. ArkTS卡片渲染服务运行原理

5. 卡片的服务通信

5.1 卡片-------> 应用
  • 使用postCardAction方法
  1. 在卡片pages中书写代码
typescript 复制代码
// 卡片的应用
@Entry
@Component
struct WidgetCard {
  @State count: number = 10;
  build() {
    Column() {
      Row({ space: 20 }) {
        Button('++')
          .onClick(() => {
            this.count++;

            postCardAction(this, {
              action: 'call',
              abilityName: 'EntryAbility',
              params: {
                method: 'updateFormCount',
                num: this.count
              }
            })
          })

        Text(this.count.toString())
          .fontSize(18)

        Button('--')
          .onClick(() => {
            if (this.count > 0) {
              this.count--;
              postCardAction(this,{
                action:'call',
                abilityName: 'EntryAbility',
                params:{
                  method:'updateFormCount',
                  num:this.count
                }
              })
            }
          })
      }
    }
    .width('100%')
    .height('100%')
    .onClick(() => {
    	// 点击唤醒应用
      postCardAction(this, {
        action: 'router',
        abilityName: 'EntryAbility'
      })
    })
  }
}
  1. module.json5添加-保持应用在后台权限
typescript 复制代码
 "requestPermissions": [{
      "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"
    }],
  1. 应用的entryability中进行接收
typescript 复制代码
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { rpc } from '@kit.IPCKit';
import { JSON } from '@kit.ArkTS';
import { preferences } from '@kit.ArkData';
import { formBindingData, formProvider } from '@kit.FormKit';

const DOMAIN = 0x0000;

//必须是rpc.Parcelable类型
class Params implements rpc.Parcelable {
  marshalling(dataOut: rpc.MessageSequence): boolean {
    return true;
  }

  unmarshalling(dataIn: rpc.MessageSequence): boolean {
    return true;
  }
}

class CardParams {
  count: number = 0
  formId:string = ""
}

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
    this.callee.on("updateFormCount", (data) => {
      const res = JSON.parse(data.readString()) as CardParams;
      AppStorage.setOrCreate('count', res.count);


	//必须返回一个rpc.Parcelable类型
      return new Params();
    })
  }

  onDestroy(): void {
  	//销毁时解除监听
    this.callee.off("updateFormCount")
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
      }
      hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
    });
  }

  onWindowStageDestroy(): void {
    // Main window is destroyed, release UI related resources
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
    // Ability has brought to foreground
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
  }

  onBackground(): void {
    // Ability has back to background
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
  }
}
  1. 主页Index.ets
typescript 复制代码
import { preferences } from '@kit.ArkData'
import { formBindingData, formProvider } from '@kit.FormKit'

@Component
@Entry
struct Index {
  @StorageLink('count')
  count:number = 0
  build() {
    Column(){
      Text('课程太多了')
        .fontSize(18)
        .fontColor(Color.Orange)
        .fontWeight(700)
      Row({ space: 20 }) {
        Button('++')
          .onClick(() => {
            this.count++;
          })

        Text(this.count.toString())
          .fontSize(18)

        Button('--')
          .onClick(() => {
            if (this.count > 0) {
              this.count--;
            }
          })
      }
    }
    .backgroundColor(Color.Pink)
    .width('100%')
    .height('100%')
  }
}

总结:如图示

5.2 应用----------->卡片
  1. 卡片的ability的entryformability的onAddForm方法中添加
typescript 复制代码
onAddForm(want: Want) {
    // Called to return a FormBindingData object.
    return formBindingData.createFormBindingData({
      formId: want.parameters!["ohos.extra.param.key.form_identity"] as string
    });
  }
  1. 卡片:WidgetCard.ets 监听formId ,当formId发生变化时,发送至应用
typescript 复制代码
 @LocalStorageProp("formId")
  @Watch("updateFormId")
  formId: string = ""

  updateFormId () {
    postCardAction(this, {
      action: 'call',
      abilityName: 'EntryAbility', // 只能跳转到当前应用下的UIAbility
      params: {
        method: 'updateFormId',
        formId: this.formId
      }
    })
  }
  1. 在ability中通过callee监听方法,将formId存入持久化
typescript 复制代码
 this.callee.on("updateFormId", (data) => {
      const res = JSON.parse(data.readString()) as CardParams
      const store = preferences.getPreferencesSync(this.context, {
        name: 'formIdList'
      })
      const list = JSON.parse(store.getSync("formIdList", "[]") as string) as string[]
      if(!list.includes(res.formId)) {
        list.push(res.formId)
      }
      store.putSync("formIdList", JSON.stringify(list))
      store.flush()
      formProvider.updateForm(res.formId, formBindingData.createFormBindingData({
        num: AppStorage.get("num")
      }))
      return new Params()
    })
  1. 卸载时解除
typescript 复制代码
  onDestroy(): void {
    this.callee.off("updateNum")
    this.callee.off("updateFormId")
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
  }
  1. Index.ets
typescript 复制代码
@StorageLink("num")
  @Watch("pushCard")
  num: number = 0

  pushCard() {
    const store = preferences.getPreferencesSync(getContext(), {
      name: 'formIdList'
    })
    const formIdList = JSON.parse(store.getSync("formIdList", "[]") as string) as string[]
    if (formIdList && formIdList.length) {
      formIdList.forEach((formId) => {
        formProvider.updateForm(formId, formBindingData.createFormBindingData({
          num: this.num
        }))
      })

    }
  }
  1. 卡片:从推送的数据中从新获取
typescript 复制代码
@Entry
@Component
struct WidgetCard {
  //修改成@LocalStorageProp
  @LocalStorageProp("count")  
  count: number = 0;
  @LocalStorageProp("formId")
  @Watch("updateFormId")
  formId:string = ""
  //应用===》卡片  需要把formId给到应用
  updateFormId(){
    postCardAction(this,{
      action:'call',
      abilityName: 'EntryAbility',
        params:{
          method:'updateFormId',
          formId:this.formId
        }
    })
  }

  build() {
    Column() {
      Row({ space: 20 }) {
        Button('++')
          .onClick(() => {
            this.count++;

            postCardAction(this, {
              action: 'call',
              abilityName: 'EntryAbility',
              params: {
                method: 'updateFormCount',
                count: this.count
              }
            })
          })

        Text(this.count.toString())
          .fontSize(18)
        Button('--')
          .onClick(() => {
            if (this.count > 0) {
              this.count--;
              postCardAction(this,{
                action:'call',
                abilityName: 'EntryAbility',
                params:{
                  method:'updateFormCount',
                  count:this.count
                }
              })
            }
          })
      }
    }
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .width('100%')
    .height('100%')
    .onClick(() => {
      postCardAction(this, {
        action: 'router',
        abilityName: 'EntryAbility'
      })
    })
  }
}

总结:如图示:

相关推荐
食品一少年3 小时前
【Day7-10】开源鸿蒙组件封装实战(3)仿知乎日报的首页轮播图实现
华为·开源·harmonyos
HONG````4 小时前
鸿蒙应用HTTP网络请求实战指南:从基础到进阶优化
网络·http·harmonyos
赵财猫._.4 小时前
HarmonyOS内存优化实战:泄漏检测、大对象管理与垃圾回收策略
华为·wpf·harmonyos
风浅月明4 小时前
[Harmony]跳转应用商店进行版本更新
harmonyos·版本更新
欧学明4 小时前
希影RS80 Ultra 鸿蒙巨幕 4K投影仪:2㎡阳台的多元光影体验
harmonyos·希影 rs80 ultra
马剑威(威哥爱编程)4 小时前
【鸿蒙开发实战篇】鸿蒙跨设备的碰一碰文件分享
华为·harmonyos
赵财猫._.4 小时前
鸿蒙超级终端体验:无缝流转的底层实现与用户体验优化
wpf·harmonyos·ux
A懿轩A4 小时前
【2025版 OpenHarmony】GitCode 口袋工具 v1.0.3:Flutter + HarmonyOS 深色模式全面启用
flutter·harmonyos·openharmony·gitcode·开源鸿蒙
YJlio4 小时前
[鸿蒙2025领航者闯关] 基于鸿蒙 6 的「隐私感知跨设备办公助手」实战:星盾安全 + AI防窥 + 方舟引擎优化全流程复盘
人工智能·安全·harmonyos
御承扬4 小时前
鸿蒙原生系列之监听布局和送显事件
harmonyos·鸿蒙ndk ui