HarmonyOS鸿蒙Next中卡片添加到桌面编辑

HarmonyOS鸿蒙Next中卡片添加到桌面编辑 看到一个待办卡片长按可以编辑卡片设置,是怎么实现的?比如修改卡片背景、文字大小。

3 回复

可以参考桌面提供统一的卡片编辑页,卡片提供方使用卡片框架提供的FormEditExtensionAbility开发卡片编辑功能。

示例如下:

提供了卡片一级编辑页面和二级卡片编辑功能,在一级编辑页面实现修改卡片字体大小的功能。二级编辑可根据实际业务需要保留或删除。

1.在工程的entry模块中,在src/main/ets目录下新建entryformeditability文件夹。

2.在entryformeditability文件夹下,创建EntryFormEditAbility.ets文件。在EntryFormEditAbility文件中,实现startSecondPage方法,在onSessionCreate回调方法中,加载一级卡片编辑页,并将startSecondPage方法的实现传递给一级卡片编辑页。

// src/main/ets/entryformeditability/EntryFormEditAbility.ets

import { FormEditExtensionAbility } from '@kit.FormKit';
import { UIExtensionContentSession, Want } from '@kit.AbilityKit';
import { ExtensionEvent } from '../pages/model/ExtensionEvent';

const TAG: string = 'FormEditDemo[EntryFormEditAbility] -->';

export default class EntryFormEditAbility extends FormEditExtensionAbility {
  onCreate() {
    console.info(`${TAG} onCreate`);
  }

  onForeground(): void {
    console.info(`${TAG} EntryFormEditAbility onForeground.....`);
  }

  onBackground(): void {
    console.info(`${TAG} EntryFormEditAbility onBackground......`);
  }

  onDestroy(): void {
    console.info(`${TAG} EntryFormEditAbility onDestroy......`);
  }

  onSessionCreate(want: Want, session: UIExtensionContentSession) {
    console.info(`${TAG} onSessionCreate start..... ${want.bundleName}`);
    // 获取被编辑卡片的卡片ID和预览卡片的卡片ID,通过storage同步到一级编辑页中
    const formId: string | undefined = want.parameters?.cardId as string;
    const previewFormId: string | undefined = want.parameters?.previewCardId as string;


    let storage: LocalStorage = new LocalStorage();
    let extensionEvent: ExtensionEvent = new ExtensionEvent();
    extensionEvent.setStartSecondPage(() => this.startSecondPage());
    storage.setOrCreate('extensionEvent', extensionEvent);

    // 保存
    if (formId) {
      console.info(`${TAG} form id is ${formId}`);
      storage.setOrCreate('formId', formId);
    }
    if (previewFormId) {
      console.info(`${TAG} preview form id is ${previewFormId}`);
      storage.setOrCreate('previewFormId', previewFormId);
    }

    try {
      session.loadContent('pages/Extension', storage);
    } catch (e) {
      console.error(`${TAG} EntryFormEditAbility loadContent err, want: ${JSON.stringify(e)}`);
    }
  }

  onSessionDestroy() {
    console.info(`${TAG} onSessionDestroy`);
  }

  private startSecondPage(): void {
    const bundleName: string = this.context.extensionAbilityInfo.bundleName;
    const secPageAbilityName: string = 'FormEditSecPageAbility';
    console.info(`${TAG} startSecondPage. bundleName: ${bundleName}, secPageAbilityName: ${secPageAbilityName}.`);
    try {
      this.context.startSecondPage({
        bundleName: bundleName,
        parameters: {
          'secPageAbilityName': secPageAbilityName
        }
      });
    } catch (err) {
      console.error(`${TAG} startSecondPage failed: ${err}`);
    }
  }
};

3.在entryformeditability文件夹下,创建FormEditSecPageAbility.ets文件。在onSessionCreate回调方法中,加载二级卡片编辑页。

// entry/src/main/ets/entryformeditability/FormEditSecPageAbility.ets
import { FormEditExtensionAbility } from '@kit.FormKit';
import { UIExtensionContentSession, Want } from '@kit.AbilityKit';
import { ExtensionEvent } from '../pages/model/ExtensionEvent';

const TAG: string = 'FormEditDemo[FormEditSecPageAbility] -->';

export default class FormEditSecPageAbility extends FormEditExtensionAbility {
  onCreate() {
    console.info(TAG, `Ability onCreate`);
  }

  onForeground(): void {
    console.info(TAG, `Ability onForeground`);
  }

  onBackground(): void {
    console.info(TAG, `Ability onBackground`);
  }

  onDestroy(): void {
    console.info(TAG, `Ability onDestroy`);
  }

  onSessionCreate(want: Want, session: UIExtensionContentSession) {
    console.info(`${TAG} onSessionCreate start..... ${want.bundleName}`);
    let storage: LocalStorage = new LocalStorage();
    let extensionEvent: ExtensionEvent = new ExtensionEvent();
    storage.setOrCreate('extensionEvent', extensionEvent);
    storage.setOrCreate('session', session);

    try {
      session.loadContent('pages/SecondPage', storage);
      console.info(TAG, `loadContent first edit page success`);
    } catch (e) {
      console.error(TAG, `EntryFormEditAbility loadContent err, want: ${e?.message}`);
    }
  }

  onSessionDestroy() {
    console.info(TAG, `onSessionDestroy`);
  }
};

4.在src/main/ets/pages目录下新增Extension文件,用于展示卡片一级编辑页。

// src/main/ets/pages/Extension.ets

import { ExtensionEvent } from './model/ExtensionEvent';
import { formBindingData, formProvider } from '@kit.FormKit';

const TAG: string = 'FormEditDemo[Extension] -->';

@Entry
@Component
struct Extension {
  localStorage = this.getUIContext().getSharedLocalStorage();
  private extensionEvent: ExtensionEvent | undefined = this.localStorage?.get<ExtensionEvent>('extensionEvent');
  // 获取编辑卡片的卡片ID和预览卡片的卡片ID
  private formId: string = this.localStorage?.get('formId') as string;
  private previewFormId: string = this.localStorage?.get('previewFormId') as string;
  fontSize: number = 0;

  updateForm(fontSize: number) {
    if (!this.formId && !this.previewFormId) {
      return;
    }
    let param: Record<string, number> = {
      'fontSize': fontSize
    };

    let obj: formBindingData.FormBindingData = formBindingData.createFormBindingData(param);
    try {
      // 刷新被编辑卡片的信息
      formProvider.updateForm(this.formId, obj, (error: BusinessError) => {
        if (error) {
          console.error(TAG, `callback error, code: ${error.code}, message: ${error.message})`);
          return;
        }
        console.info(TAG, `formProvider updateForm success`);
      });
    } catch (error) {
      console.error(TAG, `catch error, Code: ${error.code}, Message: ${error.message}`);
    }
    if (!this.previewFormId) {
      console.error(TAG, 'previewFormId is empty');
      return;
    }
    try {
      // 刷新预览卡片的信息
      formProvider.updateForm(this.previewFormId, obj, (error: BusinessError) => {
        if (error) {
          console.error(TAG, `callback error, code: ${error.code}, message: ${error.message})`);
          return;
        }
        console.info(TAG, `formProvider updateForm success`);
      });
    } catch (error) {
      console.error(TAG, `catch error, Code: ${error.code}, Message: ${error.message}`);
    }
  }

  build() {
    Row() {
      Column({ space: 10 }) {
        Row() {
          Text('修改字体大小:');
          TextInput()
            .type(InputType.Number)
            .layoutWeight(1)
            .onChange((value) => {
              this.fontSize = Number(value);
            });
        }.width('100%');

        Button('更新字体大小')
          .width('80%')
          .type(ButtonType.Capsule)
          .margin({
            top: 20
          })
          .onClick(() => {
            this.updateForm(this.fontSize);
          });

        Button('跳转二级编辑页')
          .width('80%')
          .type(ButtonType.Capsule)
          .margin({
            top: 20
          })
          .onClick(() => {
            console.info(`${TAG} Button onClick, ${this.extensionEvent}`);
            this.extensionEvent?.startFormEditSecondPage();
          });
      };
    }
    .justifyContent(FlexAlign.Center)
    .width('100%');
  }
}

5.在src/main/ets/pages目录下新增SecondPage文件,用于展示卡片二级编辑页。

// src/main/ets/pages/Extension.ets

const TAG: string = 'FormEditDemo[Extension] -->';

@Entry
@Component
struct SecondPage {
  message: string = '这里是二级编辑页面 UIExtension Provider';

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .textAlign(TextAlign.Center);
        Button('这里是二级编辑页面')
          .width('80%')
          .type(ButtonType.Capsule)
          .margin({
            top: 20
          })
          .onClick(() => {
            console.info(`${TAG} Button onClick`);
          });
      };
    }
    .justifyContent(FlexAlign.Center)
    .width('100%');
  }
}

6.新建src/main/ets/pages/model目录,新增ExtensionEvent文件,使用startFormEditSecondPage方法调用startSecondPage方法。

// src/main/ets/pages/model/ExtensionEvent.ets

const TAG: string = 'FormEditDemo[ExtensionEvent] -->';

export class ExtensionEvent {
  private startSecondPage: () => void = () => {
    console.info(`${TAG} startSecondPage is empty!`);
  };

  public setStartSecondPage(startSecondPage: () => void) {
    console.info(`${TAG} setStartSecondPage`);
    this.startSecondPage = startSecondPage;
  }

  public startFormEditSecondPage(): void {
    console.info(`${TAG} startFormEditSecondPage`);
    this.startSecondPage();
  }
}

7.在应用的module.json5配置文件中添加卡片编辑配置信息。关注extensionAbilities部分。

{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:layered_image",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "ohos.want.action.home"
            ]
          }
        ]
      }
    ],
    "extensionAbilities": [
      {
        "name": "EntryBackupAbility",
        "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
        "type": "backup",
        "exported": false,
        "metadata": [
          {
            "name": "ohos.extension.backup",
            "resource": "$profile:backup_config"
          }
        ],
      },
      {
        "name": "EntryFormAbility",
        "srcEntry": "./ets/entryformability/EntryFormAbility.ets",
        "label": "$string:EntryFormAbility_label",
        "description": "$string:EntryFormAbility_desc",
        "type": "form",
        "metadata": [
          {
            "name": "ohos.extension.form",
            "resource": "$profile:form_config"
          }
        ]
      },
      {
        // 一级编辑页
        "name": "EntryFormEditAbility",
        "srcEntry": "./ets/entryformeditability/EntryFormEditAbility.ets",
        "type": "formEdit"
      },
      {
        // 二级编辑页
        "name": "FormEditSecPageAbility",
        "srcEntry": "./ets/entryformeditability/FormEditSecPageAbility.ets",
        "type": "formEdit"
      }
    ]
  }
}

8.在卡片的form_config.json配置文件中添加formConfigAbility配置项信息。

{
  "forms": [
    {
      "name": "widget",
      "displayName": "$string:widget_display_name",
      "description": "$string:widget_desc",
      "src": "./ets/widget/pages/WidgetCard.ets",
      "uiSyntax": "arkts",
      "window": {
        "designWidth": 720,
        "autoDesignWidth": true
      },
      "colorMode": "auto",
      "formConfigAbility": "ability://EntryFormEditAbility",
      "isDynamic": true,
      "isDefault": true,
      "updateEnabled": false,
      "scheduledUpdateTime": "10:30",
      "updateDuration": 1,
      "defaultDimension": "2*2",
      "supportDimensions": [
        "2*2"
      ]
    }
  ]
}

9.在开发视图的resource/base/profile/main_pages.json文件中,注册一级及二级编辑页面文件。

{
  "src": [
    "pages/Index",
    "pages/Extension",
    "pages/SecondPage"
  ]
}
  1. 在卡片页面接收字体大小,并修改渲染。
// src/main/ets/widget/pages/WidgetCard.ets

@Entry
@Component
struct WidgetCard {
  @LocalStorageProp('fontSize') fontSize: number = 12;

  build() {
    Row() {
      Column() {
        Text('嘻嘻哈哈')
          .fontSize(this.fontSize)
          .fontWeight(FontWeight.Medium)
          .fontColor($r('sys.color.font'));
      };
    }
    .padding(16)
    .width('100%')
    .height('100%');

  }
}

更多关于HarmonyOS鸿蒙Next中卡片添加到桌面编辑的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,卡片添加到桌面编辑主要通过长按应用图标触发。系统会显示可用的服务卡片,用户可选择卡片样式和尺寸,然后将其拖拽至桌面任意位置。桌面进入编辑模式后,可对已添加的卡片进行移动、移除或更换样式。该功能依赖应用本身提供卡片服务。

在HarmonyOS Next中,实现卡片长按编辑功能(如修改背景、文字大小)主要依赖于ArkTS UI框架和FormExtensionAbility。以下是核心实现步骤:

  1. 卡片配置:在resources/base/profile/form_config.json中声明卡片支持updateEnabled能力,允许动态更新。

  2. 数据交互:通过FormProviderupdateForm方法,在卡片与宿主(如桌面)间传递更新数据。长按编辑时,宿主可触发更新请求。

  3. UI动态更新:在卡片的ArkUI组件中,使用状态变量(如@State)绑定样式属性(背景色、字体大小)。当收到更新数据时,修改状态变量即可实时刷新卡片样式。

  4. 编辑界面:通常由宿主应用提供编辑弹窗或页面,用户修改配置后,将新配置(如backgroundColor: '#FFEFDB', fontSize: 18)通过updateForm推送给卡片。

关键代码示例(卡片组件内)

@State bgColor: string = '#FFFFFF'
@State textSize: number = 16

build() {
  Column() {
    Text('待办事项')
      .fontSize(this.textSize)
  }
  .width('100%')
  .height('100%')
  .backgroundColor(this.bgColor)
}

当宿主调用更新时,卡片通过onUpdateForm回调接收新配置并更新状态变量,UI自动重绘。

此机制实现了卡片样式的动态化,无需重新创建卡片即可响应编辑操作。

回到顶部