提交 a650f86a 编写于 作者: H HelloCrease

Merge branch 'OpenHarmony-3.2-Release' of https://gitee.com/HelloCrease/docs...

Merge branch 'OpenHarmony-3.2-Release' of https://gitee.com/HelloCrease/docs into OpenHarmony-3.2-Release
...@@ -22,6 +22,31 @@ ...@@ -22,6 +22,31 @@
- [EnterpriseAdminExtensionAbility](enterprise-extensionAbility.md) - [EnterpriseAdminExtensionAbility](enterprise-extensionAbility.md)
- [InputMethodExtensionAbility](inputmethodextentionability.md) - [InputMethodExtensionAbility](inputmethodextentionability.md)
- [WindowExtensionAbility](windowextensionability.md) - [WindowExtensionAbility](windowextensionability.md)
- Service Widget Development in Stage Model
- [Service Widget Overview](service-widget-overview.md)
- Developing an ArkTS Widget
- [ArkTS Widget Working Principles](arkts-ui-widget-working-principles.md)
- [ArkTS Widget Related Modules](arkts-ui-widget-modules.md)
- ArkTS Widget Development
- [Creating an ArkTS Widget](arkts-ui-widget-creation.md)
- [Configuring Widget Configuration Files](arkts-ui-widget-configuration.md)
- [Widget Lifecycle Management](arkts-ui-widget-lifecycle.md)
- Widget Page Development
- [Widget Page Capability Overview](arkts-ui-widget-page-overview.md)
- [Using Animations in the Widget](arkts-ui-widget-page-animation.md)
- [Applying Custom Drawing in the Widget](arkts-ui-widget-page-custom-drawing.md)
- Widget Event Development
- [Widget Event Capability Overview](arkts-ui-widget-event-overview.md)
- [Updating Widget Content Through FormExtensionAbility](arkts-ui-widget-event-formextensionability.md)
- [Updating Widget Content Through UIAbility](arkts-ui-widget-event-uiability.md)
- [Redirecting to a Specified Page Through the Router Event](arkts-ui-widget-event-router.md)
- Widget Data Interaction
- [Widget Data Interaction Overview](arkts-ui-widget-interaction-overview.md)
- [Configuring a Widget to Update Periodically](arkts-ui-widget-update-by-time.md)
- [Updating Local and Online Images in the Widget](arkts-ui-widget-image-update.md)
- [Updating Widget Content by State](arkts-ui-widget-update-by-status.md)
- [Updating Widget Content by Widget Host (for System Applications Only)](arkts-ui-widget-content-update.md)
- [Developing a JS Widget](js-ui-widget-development.md)
- [AbilityStage Component Container](abilitystage.md) - [AbilityStage Component Container](abilitystage.md)
- [Context](application-context-stage.md) - [Context](application-context-stage.md)
- Want - Want
......
# Configuring Widget Configuration Files
Widget-related configuration includes **FormExtensionAbility** configuration and widget configuration.
1. Configure FormExtensionAbility information under **extensionAbilities** in the [module.json5 file](../quick-start/module-configuration-file.md). For a FormExtensionAbility, you must specify **metadata**. Specifically, set **name** to **ohos.extension.form** (fixed), and set **resource** to the index of the widget configuration information.
Example configuration:
```json
{
"module": {
...
"extensionAbilities": [
{
"name": "EntryFormAbility",
"srcEntry": "./ets/entryformability/EntryFormAbility.ts",
"label": "$string:EntryFormAbility_label",
"description": "$string:EntryFormAbility_desc",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
]
}
}
```
2. Configure the widget configuration information. In the **metadata** configuration item of FormExtensionAbility, you can specify the resource index of specific configuration information of the widget. For example, if resource is set to **$profile:form_config**, **form_config.json** in the **resources/base/profile/** directory of the development view is used as the profile configuration file of the widget. The following table describes the internal field structure.
**Table 1** form_config.json file
| Field| Description| Data Type| Default Value Allowed|
| -------- | -------- | -------- | -------- |
| name | Class name of the widget. The value is a string with a maximum of 127 bytes.| String| No|
| description | Description of the widget. The value can be a string or a resource index to descriptions in multiple languages. The value is a string with a maximum of 255 bytes.| String| Yes (initial value: left empty)|
| src | Full path of the UI code corresponding to the widget. For an ArkTS widget, the full path must contain the widget file name extension, for example, **./ets/widget/pages/WidgetCard.ets**. For a JS widget, the full path does not need to contain the widget file name extension, for example, **./js/widget/pages/WidgetCard**.| String| No|
| uiSyntax | Type of the widget.<br>- **arkts**: ArkTS widget<br>- **hml**: JS widget| String| Yes (initial value: **hml**)|
| window | Window-related configurations.| Object| Yes|
| isDefault | Whether the widget is a default one. Each UIAbility has only one default widget.<br>- **true**: The widget is the default one.<br>- **false**: The widget is not the default one.| Boolean| No|
| colorMode | Color mode of the widget.<br>- **auto**: auto-adaptive color mode<br>- **dark**: dark color mode<br>- **light**: light color mode| String| Yes (initial value: **auto**)|
| supportDimensions | Grid styles supported by the widget.<br>- **1 * 2**: indicates a grid with one row and two columns.<br>- **2 * 2**: indicates a grid with two rows and two columns.<br>- **2 * 4**: indicates a grid with two rows and four columns.<br>- **4 * 4**: indicates a grid with four rows and four columns.| String array| No|
| defaultDimension | Default grid style of the widget. The value must be available in the **supportDimensions** array of the widget.| String| No|
| updateEnabled | Whether the widget can be updated periodically.<br>- **true**: The widget can be updated at a specified interval (**updateDuration**) or at the scheduled time (**scheduledUpdateTime**). **updateDuration** takes precedence over **scheduledUpdateTime**.<br>- **false**: The widget cannot be updated periodically.| Boolean| No|
| scheduledUpdateTime | Scheduled time to update the widget. The value is in 24-hour format and accurate to minute.<br>**NOTE**<br>**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.| String| Yes (initial value: The widget cannot be updated periodically.)|
| updateDuration | Interval to update the widget. The value is a natural number, in the unit of 30 minutes.<br>If the value is **0**, this field does not take effect.<br>If the value is a positive integer *N*, the interval is calculated by multiplying *N* and 30 minutes.<br>**NOTE**<br>**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.| Number| Yes (initial value: **0**)|
| formConfigAbility | Link to a specific page of the application. The value is a URI.| String| Yes (initial value: left empty)|
| formVisibleNotify | Whether the widget is allowed to use the widget visibility notification.| String| Yes (initial value: left empty)|
| metadata | Metadata of the widget. This field contains the array of the **customizeData** field.| Object| Yes (initial value: left empty)|
Example configuration:
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": [
"2*2"
]
}
]
}
```
# Updating Widget Content by Widget Host (for System Applications Only)
Widgets that are updated periodically are subject to the scheduled time or interval settings. To offer more flexible updates, the widget host can provide a button to proactively trigger a widget update. Specifically, the widget host calls the [requestForm](../reference/apis/js-apis-app-form-formHost.md#requestform) API to request a widget update. The system then calls the [onUpdateForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onupdateform) lifecycle callback in the FormExtensionAbility of the widget provider. In the callback, the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) API can be used to update the widget content. For details about the **onUpdateForm** lifecycle callback, see [Updating Widget Content Through FormExtensionAbility](arkts-ui-widget-event-formextensionability.md).
```ts
import formHost from '@ohos.app.form.formHost';
@Entry()
@Component
struct WidgetCard {
formId = ...; // Widget ID
build() {
Button (`Update Widget`)
.type(ButtonType.Capsule)
.width('50%')
.height(50)
.onClick(() => {
console.info('FormAbility update form click');
// formId is the ID of the widget to be updated.
formHost.requestForm(this.formId.toString()).then(() => {
console.info('Succeeded in requestForming.');
});
})
...
}
}
```
# Creating an ArkTS Widget
To create an ArkTS widget in an existing application project, perform the following steps:
1. Create a widget.
![WidgetProjectCreate1](figures/WidgetProjectCreate1.png)
2. Select a widget template based on the actual service scenario.
![WidgetProjectCreate2](figures/WidgetProjectCreate2.png)
3. Set **Language** to **ArkTS** and click **Finish**.
![WidgetProjectCreate3](figures/WidgetProjectCreate3.png)
After an ArkTS widget is created, the following widget-related files are added to the project directory: **EntryFormAbility.ts** (widget lifecycle management file), **WidgetCard.ets** (widget page file), and **form_config.json** (widget configuration file).
![WidgetProjectView](figures/WidgetProjectView.png)
# Updating Widget Content Through FormExtensionAbility
On the widget page, the **postCardAction** API can be used to trigger a message event to the FormExtensionAbility, which then updates the widget content. The following is an example of this widget update mode.
- On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the event to the FormExtensionAbility.
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('title') title: string = 'init';
@LocalStorageProp('detail') detail: string = 'init';
build() {
Column() {
Button ('Update')
.onClick(() => {
postCardAction(this, {
'action': 'message',
'params': {
'msgTest': 'messageEvent'
}
});
})
Text(`${this.title}`)
Text(`${this.detail}`)
}
.width('100%')
.height('100%')
}
}
```
- Call the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) API to update the widget in the **onFormEvent** callback of the FormExtensionAbility.
```ts
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import formProvider from '@ohos.app.form.formProvider';
export default class EntryFormAbility extends FormExtensionAbility {
onFormEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
console.info(`FormAbility onEvent, formId = ${formId}, message: ${JSON.stringify(message)}`);
let formData = {
'title':'Title Update Success.', // Matches the widget layout.
'detail':'Detail Update Success.', // Matches the widget layout.
};
let formInfo = formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId, formInfo).then((data) => {
console.info('FormAbility updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
})
}
// ...
}
```
The figure below shows the effect.
![WidgetUpdatePage](figures/WidgetUpdatePage.png)
# Widget Event Capability Overview
The ArkTS widget provides the **postCardAction()** API for interaction between the widget internal and the provider application. Currently, this API supports the router, message, and call events and can be called only in the widget.
![WidgetPostCardAction](figures/WidgetPostCardAction.png)
Definition: postCardAction(component: Object, action: Object): void
Parameters:
| Name| Type| Mandatory| Description|
| -------- | -------- | -------- | -------- |
| component | Object | Yes| Instance of the current custom component. Generally, **this** is transferred.|
| action | Object | Yes| Action description. For details, see the following table.|
Description of the action parameter
| **Key** | **Value** | Description|
| -------- | -------- | -------- |
| "action" | string | Action type.<br>- **"router"**: application redirection. If this type of action is triggered, the corresponding UIAbility is displayed. Only the UIAbility of the current application can be displayed.<br>- **"message"**: custom message. If this type of action is triggered, the [onFormEvent()](../reference/apis/js-apis-app-form-formExtensionAbility.md#onformevent) lifecycle callback of the provider FormExtensionAbility is called.<br>- **"call"**: application startup in the background. If this type of action is triggered, the corresponding UIAbility is started but does not run in the foreground. The target application must have the permission to run in the background ([ohos.permission.KEEP_BACKGROUND_RUNNING](../security/permission-list.md#ohospermissionkeep_background_running)).|
| "bundleName" | string | Name of the target bundle when **action** is **"router"** or **"call"**. This parameter is optional.|
| "moduleName" | string | Name of the target module when **action** is **"router"** or **"call"**. This parameter is optional.|
| "abilityName" | string | Name of the target UIAbility when **action** is **"router"** or **"call"**. This parameter is mandatory.|
| "params" | Object | Additional parameters carried in the current action. The value is a key-value pair in JSON format.|
Sample code of the **postCardAction()** API:
```typescript
Button ('Jump')
.width('40%')
.height('20%')
.onClick(() => {
postCardAction(this, {
'action': 'router',
'bundleName': 'com.example.myapplication',
'abilityName': 'EntryAbility',
'params': {
'message': 'testForRouter' // Customize the message to be sent.
}
});
})
```
The following are typical widget development scenarios that can be implemented through widget events:
- [Updating Widget Content Through FormExtensionAbility](arkts-ui-widget-event-formextensionability.md)
- [Updating Widget Content Through UIAbility](arkts-ui-widget-event-uiability.md)
- [Redirecting to a Specified Page Through the Router Event](arkts-ui-widget-event-router.md)
# Redirecting to a Specified Page Through the Router Event
The **router** capability of the **postCardAction** API can be used in a widget to quickly start the widget provider application. An application can provide different buttons through the widget so that users can jump to different pages at the touch of a button. For example, a camera widget provides the buttons that direct the user to respective pages, such as the page for taking a photo and the page for recording a video.
![WidgerCameraCard](figures/WidgerCameraCard.png)
Generally, a button is used to start a page.
- Design two buttons on the widget page. When one of the buttons is clicked, **postCardAction** is called to send a router event to the specified UIAbility, with the content to be transferred defined in the event.
```ts
@Entry
@Component
struct WidgetCard {
build() {
Column() {
Button ('Function A')
.margin('20%')
.onClick(() => {
console.info('Jump to EntryAbility funA');
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed.
'params': {
'targetPage': 'funA' // Process the information in the EntryAbility.
}
});
})
Button ('Function B')
.margin('20%')
.onClick(() => {
console.info('Jump to EntryAbility funB');
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed.
'params': {
'targetPage': 'funB' // Process the information in the EntryAbility.
}
});
})
}
.width('100%')
.height('100%')
}
}
```
- The UIAbility receives the router event and obtains parameters. It then starts the page specified in the received message.
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
let selectPage = "";
let currentWindowStage = null;
export default class CameraAbility extends UIAbility {
// If the UIAbility is started for the first time, the onCreate lifecycle callback is triggered after the router event is received.
onCreate(want, launchParam) {
// Obtain the targetPage parameter passed in the router event.
console.info("onCreate want:" + JSON.stringify(want));
if (want.parameters.params !== undefined) {
let params = JSON.parse(want.parameters.params);
console.info("onCreate router targetPage:" + params.targetPage);
selectPage = params.targetPage;
}
}
// If the UIAbility is running in the background, the onNewWant lifecycle callback is triggered after the router event is received.
onNewWant(want, launchParam) {
console.info("onNewWant want:" + JSON.stringify(want));
if (want.parameters.params !== undefined) {
let params = JSON.parse(want.parameters.params);
console.info("onNewWant router targetPage:" + params.targetPage);
selectPage = params.targetPage;
}
if (currentWindowStage != null) {
this.onWindowStageCreate(currentWindowStage);
}
}
onWindowStageCreate(windowStage: window.WindowStage) {
let targetPage;
// Start the page specified by targetPage.
switch (selectPage) {
case 'funA':
targetPage = 'pages/FunA';
break;
case 'funB':
targetPage = 'pages/FunB';
break;
default:
targetPage = 'pages/Index';
}
if (currentWindowStage === null) {
currentWindowStage = windowStage;
}
windowStage.loadContent(targetPage, (err, data) => {
if (err && err.code) {
console.info('Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
});
}
};
```
# Updating Widget Content Through UIAbility
On the widget page, the **postCardAction** API can be used to trigger a router or call event to start the UIAbility, which then updates the widget content. The following is an example of this widget update mode.
- On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the event to the FormExtensionAbility.
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('detail') detail: string = 'init';
build() {
Column() {
Button ('Jump')
.margin('20%')
.onClick(() => {
console.info('postCardAction to EntryAbility');
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed.
'params': {
'detail': 'RouterFromCard'
}
});
})
Text(`${this.detail}`).margin('20%')
}
.width('100%')
.height('100%')
}
}
```
- In the **onCreate()** or **onNewWant()** lifecycle callback of the UIAbility, use the input parameter **want** to obtain the ID (**formID**) and other information of the widget, and then call the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) API to update the widget.
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import formInfo from '@ohos.app.form.formInfo';
export default class EntryAbility extends UIAbility {
// If the UIAbility is started for the first time, the onCreate lifecycle callback is triggered after the router event is received.
onCreate(want, launchParam) {
console.info('Want:' + JSON.stringify(want));
if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
let message = JSON.parse(want.parameters.params).detail;
console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
let formData = {
"detail": message +': onCreate UIAbility.', // Matches the widget layout.
};
let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => {
console.info('updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('updateForm failed:' + JSON.stringify(error));
})
}
}
// If the UIAbility is running in the background, the onNewWant lifecycle callback is triggered after the router event is received.
onNewWant(want, launchParam) {
console.info('onNewWant Want:' + JSON.stringify(want));
if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
let message = JSON.parse(want.parameters.params).detail;
console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
let formData = {
"detail": message +': onNewWant UIAbility.', // Matches the widget layout.
};
let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => {
console.info('updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('updateForm failed:' + JSON.stringify(error));
})
}
}
...
}
```
# Updating Local and Online Images in the Widget
Generally, local images or online images downloaded from the network need to be displayed on a widget. To obtain local and online images, use the FormExtensionAbility. The following exemplifies how to show local and online images on a widget.
1. Internet access is required for downloading online images. Therefore, you need to apply for the **ohos.permission.INTERNET** permission. For details, see[Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md).
2. Update local files in the **onAddForm** lifecycle callback of the EntryFormAbility.
```ts
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import request from '@ohos.request';
import fs from '@ohos.file.fs';
export default class EntryFormAbility extends FormExtensionAbility {
...
// When the widget is added, a local image is opened and transferred to the widget page for display.
onAddForm(want) {
// Assume that the local image head.PNG is in the tmp directory of the current widget.
let tempDir = this.context.getApplicationContext().tempDir;
// Open the local image and obtain the FD after the image is opened.
let file;
try {
file = fs.openSync(tempDir + '/' + 'head.PNG');
} catch (e) {
console.error(`openSync failed: ${JSON.stringify(e)}`);
}
let formData = {
'text': 'Image: Bear',
'imgName': 'imgBear',
'formImages': {
'imgBear': file.fd
},
'loaded': true
}
// Encapsulate the FD in formData and return it to the widget page.
return formBindingData.createFormBindingData(formData);
}
...
}
```
3. Update online files in the onFormEvent lifecycle callback of the EntryFormAbility.
```ts
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import request from '@ohos.request';
import fs from '@ohos.file.fs';
export default class EntryFormAbility extends FormExtensionAbility {
// When the message event is triggered on the widget page, an online image is downloaded and transferred to the widget page for display.
onFormEvent(formId, message) {
let formInfo = formBindingData.createFormBindingData({
'text': 'Updating...'
})
formProvider.updateForm(formId, formInfo)
// Note: The FormExtensionAbility is started when the lifecycle callback is triggered. It can run in the background for only 5 seconds.
// When possible, limit the size of the image to download. If an image cannot be downloaded within 5 seconds, it cannot be updated to the widget page.
let netFile = 'https://xxxx/xxxx.png'; // Specify the URL of the image to download.
let tempDir = this.context.getApplicationContext().tempDir;
let fileName = 'file' + Date.now();
let tmpFile = tempDir + '/' + fileName;
request.downloadFile(this.context, {
url: netFile, filePath: tmpFile, enableMetered: true, enableRoaming: true
}).then((task) => {
task.on('complete', function callback() {
console.info('ArkTSCard download complete:' + tmpFile);
let file;
try {
file = fs.openSync(tmpFile);
} catch (e) {
console.error(`openSync failed: ${JSON.stringify(e)}`);
}
let fileInfo = {};
fileInfo[fileName] = file.fd;
let formData = {
'text': 'Image:' + fileName,
'imgName': fileName,
'formImages': fileInfo,
'loaded': true
};
let formInfo = formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId, formInfo).then((data) => {
console.info('FormAbility updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
})
})
task.on('fail', function callBack(err) {
console.info('ArkTSCard download task failed. Cause:' + err);
let formInfo = formBindingData.createFormBindingData({
'text':'Update failed.'
})
formProvider.updateForm(formId, formInfo)
});
}).catch((err) => {
console.error('Failed to request the download. Cause: ' + JSON.stringify(err));
});
}
...
};
```
4. On the widget page, use the **\<Image>** component to display the widget content transferred from the EntryFormAbility.
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('text') text: string = 'Loading...';
@LocalStorageProp('loaded') loaded: boolean = false;
@LocalStorageProp('imgName') imgName: string = 'name';
build() {
Column() {
Text(this.text)
.fontSize('12vp')
.textAlign(TextAlign.Center)
.width('100%')
.height('15%')
Row() {
if (this.loaded) {
Image('memory://' + this.imgName)
.width('50%')
.height('50%')
.margin('5%')
} else {
Image('common/start.PNG')
.width('50%')
.height('50%')
.margin('5%')
}
}.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)
Button ('Update')
.height('15%')
.onClick(() => {
postCardAction(this, {
'action': 'message',
'params': {
'info': 'refreshImage'
}
});
})
}
.width('100%').height('100%')
.alignItems(HorizontalAlign.Center)
.padding('5%')
}
}
```
> **NOTE**
> - The **\<Image>** component displays images in the remote memory based on the **memory://** identifier in the input parameter (**memory://fileName**). The **fileName** value must be consistent with the key in the object (**'formImages': {key: fd}**) passed by the EntryFormAbility.
>
> - The **\<Image>** component determines whether to update the image based on whether the input parameter is changed. Therefore, the value of **imgName** passed by the EntryFormAbility each time must be different. If the two values of **imgName** passed consecutively are identical, the image is not updated.
# Widget Data Interaction
The ArkTS widget framework provides the **updateForm()** and **requestForm()** APIs to proactively trigger widget updates.
![WidgetLocalStorageProp](figures/WidgetLocalStorageProp.png)
| API| System Capability| Constraints|
| -------- | -------- | -------- |
| updateForm | No| 1. Invoked by the provider.<br>2. Allows only the widget provider to update its own widgets. It cannot be used to update widgets by other providers.|
| requestForm | Yes| 1. Invoked by the host.<br>2. Allows only the widget host to update the widgets added to it. It cannot be used to update widgets added to other hosts.|
The following describes the typical use cases of widget updates:
- [Configuring a Widget to Update Periodically](arkts-ui-widget-update-by-time.md)
- [Updating Local and Online Images](arkts-ui-widget-image-update.md)
- [Updating Widget Content by State](arkts-ui-widget-update-by-status.md)
- [Updating Widget Content by Widget Host (for System Applications Only)](arkts-ui-widget-content-update.md)
# Widget Lifecycle Management
When creating an ArkTS widget, you need to implement the [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) lifecycle APIs.
1. Import related modules to **EntryFormAbility.ts**.
```ts
import formInfo from '@ohos.app.form.formInfo';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import formProvider from '@ohos.app.form.formProvider';
```
2. In **EntryFormAbility.ts**, implement the [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) lifecycle APIs, including **onAddForm**, whose **want** parameter can be used to obtain the widget information through [FormParam](../reference/apis/js-apis-app-form-formInfo.md#formparam).
```typescript
import formInfo from '@ohos.app.form.formInfo';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import formProvider from '@ohos.app.form.formProvider';
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want) {
console.info('[EntryFormAbility] onAddForm');
// Obtain the unique widget ID formId from the want parameter.
let formId: string = want.parameters[formInfo.FormParam.IDENTITY_KEY];
// Called when the widget is created. The widget provider should return the widget data binding class.
let obj = {
'title': 'titleOnAddForm',
'detail': 'detailOnAddForm'
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
onCastToNormalForm(formId) {
// Called when the form provider is notified that a temporary form is successfully
// converted to a normal form.
// Called when the widget host converts the temporary widget into a normal one. The widget provider should do something to respond to the conversion.
console.info(`[EntryFormAbility] onCastToNormalForm, formId: ${formId}`);
}
onUpdateForm(formId) {
// Override this method to support scheduled updates, periodic updates, or updates requested by the widget host.
console.info('[EntryFormAbility] onUpdateForm');
let obj = {
'title': 'titleOnUpdateForm',
'detail': 'detailOnUpdateForm'
};
let formData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, formData).catch((err) => {
if (err) {
// Print errors.
console.error(`[EntryFormAbility] Failed to updateForm. Code: ${err.code}, message: ${err.message}`);
return;
}
});
}
onChangeFormVisibility(newStatus) {
// Called when the form provider receives form events from the system.
// The callback is performed only when formVisibleNotify is set to true and the application is a system application.
console.info('[EntryFormAbility] onChangeFormVisibility');
}
onFormEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
// If the widget supports event triggering, override this method and implement the trigger.
console.info('[EntryFormAbility] onFormEvent');
}
onRemoveForm(formId) {
// Called to notify the form provider that a specified form has been destroyed.
// Called when the corresponding widget is deleted. The input parameter is the ID of the deleted card.
console.info('[EntryFormAbility] onRemoveForm');
}
onConfigurationUpdate(config) {
// Called when the system configuration is updated.
console.info('[EntryFormAbility] configurationUpdate:' + JSON.stringify(config));
}
onAcquireFormState(want) {
// Called to return a {@link FormState} object.
// Called when the widget provider receives the status query result of a widget. By default, the initial state of the widget is returned.
return formInfo.FormState.READY;
}
}
```
> **NOTE**
> The FormExtensionAbility cannot reside in the background. Therefore, continuous tasks cannot be processed in the widget lifecycle callbacks. The FormExtensionAbility persists for 5 seconds after the lifecycle callback is completed and will exit if no new lifecycle callback is invoked during this time frame. For the service logic that may take more than 5 seconds to complete, it is recommended that you [start the application](arkts-ui-widget-event-uiability.md). After the processing is complete, use the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) to notify the widget of the update.
# ArkTS Widget Related Modules
**Figure 1** ArkTS widget related modules
![WidgetModules](figures/WidgetModules.png)
- [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md): provides lifecycle callbacks invoked when a widget is created, destroyed, or updated.
- [FormExtensionContext](../reference/apis/js-apis-inner-application-formExtensionContext.md): provides context for FormExtensionAbilities. You can use the APIs of this module to start FormExtensionAbilities.
- [formProvider](../reference/apis/js-apis-app-form-formProvider.md): provides APIs related to the widget provider. You can use the APIs to update a widget, set the next update time for a widget, obtain widget information, and request a widget release.
- [formInfo](../reference/apis/js-apis-app-form-formInfo.md): provides types and enums related to the widget information and state.
- [formBindingData](../reference/apis/js-apis-app-form-formBindingData.md): provides APIs for widget data binding. You can use the APIs to create a **FormBindingData** object and obtain related information.
- [Page Layout (Card.ets)](arkts-ui-widget-page-overview.md): provides APIs for a declarative paradigm UI.
- [ArkTS widget capabilities](arkts-ui-widget-event-overview.md): include the **postCardAction** API used for interaction between the widget internal and the provider application and can be called only in the widget.
- [ArkTS widget capability list](arkts-ui-widget-page-overview.md#page-capabilities-supported-by-arkts-widgets): lists the APIs, components, events, attributes, and lifecycle callbacks that can be used in ArkTS widgets.
- [Widget configuration](arkts-ui-widget-configuration.md): includes FormExtensionAbility configuration and widget configuration.
- Configure FormExtensionAbility information under **extensionAbilities** in the [module.json5 file](../quick-start/module-configuration-file.md).
- Configure the widget configuration information (**WidgetCard.ets**) in the [form_config.json](arkts-ui-widget-configuration.md) file in **resources/base/profile**.
# Using Animations in the Widget
To make your ArkTS widget more engaging, you can apply animations to it, including [explicit animation](../reference/arkui-ts/ts-explicit-animation.md), [attribute animation](../reference/arkui-ts/ts-animatorproperty.md), and [component transition](../reference/arkui-ts/ts-transition-animation-component.md). Note the following restrictions when using the animations in ArkTS widgets.
**Table 1** Restrictions on animation parameters
| Name| Description| Description|
| -------- | -------- | -------- |
| duration | Animation playback duration| The maximum value is 1 second. If a larger value is set, the animation is still played for 1 second.|
| tempo | Animation playback speed.| Do not set this parameter in the widget. Use the default value 1.|
| delay | Animation delay duration.| Do not set this parameter in the widget. Use the default value 0.|
| iterations | Number of times that the animation is played.| Do not set this parameter in the widget. Use the default value 1.|
The following sample code implements the animation effect of button rotation:
![WidgetAnimation](figures/WidgetAnimation.gif)
```ts
@Entry
@Component
struct AttrAnimationExample {
@State rotateAngle: number = 0;
build() {
Column() {
Button('change rotate angle')
.onClick(() => {
this.rotateAngle = 90;
})
.margin(50)
.rotate({ angle: this.rotateAngle })
.animation({
curve: Curve.EaseOut,
playMode: PlayMode.AlternateReverse
})
}.width('100%').margin({ top: 20 })
}
}
```
# Applying Custom Drawing in the Widget
You can apply custom drawing in your ArkTS widget to create a more vibrant experience. Use the [Canvas](../reference/arkui-ts/ts-components-canvas-canvas.md) component to create a canvas on the widget, and then use the [CanvasRenderingContext2D](../reference/arkui-ts/ts-canvasrenderingcontext2d.md) object to draw custom graphics on the canvas. The following code shows how to draw a smiling face in the center of the canvas.
```typescript
@Entry
@Component
struct Card {
private canvasWidth: number = 0;
private canvasHeight: number = 0;
// Initialize CanvasRenderingContext2D and RenderingContextSettings.
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
build() {
Column() {
Row() {
Canvas(this.context)
.margin('5%')
.width('90%')
.height('90%')
.onReady(() => {
console.info('[ArkTSCard] onReady for canvas draw content');
// Obtain the actual width and height of the canvas in the onReady callback.
this.canvasWidth = this.context.width;
this.canvasHeight = this.context.height;
// Draw the background of the canvas.
this.context.fillStyle = 'rgba(203, 154, 126, 1.00)';
this.context.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
// Draw a red circle in the center of the canvas.
this.context.beginPath();
let radius = this.context.width / 3
let circleX = this.context.width / 2
let circleY = this.context.height / 2
this.context.moveTo(circleX - radius, circleY);
this.context.arc(circleX, circleY, radius, 2 * Math.PI, 0, true);
this.context.closePath();
this.context.fillStyle = 'red';
this.context.fill();
// Draw the left eye of the smiling face.
let leftR = radius / 4
let leftX = circleX - (radius / 2)
let leftY = circleY - (radius / 3.5)
this.context.beginPath();
this.context.arc(leftX, leftY, leftR, 0, Math.PI, true);
this.context.strokeStyle = '#ffff00'
this.context.lineWidth = 10
this.context.stroke()
// Draw the right eye of the smiling face.
let rightR = radius / 4
let rightX = circleX + (radius / 2)
let rightY = circleY - (radius / 3.5)
this.context.beginPath();
this.context.arc(rightX, rightY, rightR, 0, Math.PI, true);
this.context.strokeStyle = '#ffff00'
this.context.lineWidth = 10
this.context.stroke()
// Draw the mouth of the smiling face.
let mouthR = radius / 2.5
let mouthX = circleX
let mouthY = circleY + (radius / 3)
this.context.beginPath();
this.context.arc(mouthX, mouthY, mouthR, Math.PI, 0, true);
this.context.strokeStyle = '#ffff00'
this.context.lineWidth = 10
this.context.stroke()
})
}
}.height('100%').width('100%')
}
}
```
The figure below shows the effect.
![WidgetCanvasDemo](figures/WidgetCanvasDemo.jpeg)
# Widget Page Capability Overview
You can leverage the ArkUI declarative paradigm to develop ArkTS widget pages. The following widget pages are automatically generated by a DevEco Studio template. You can adjust the pages based on the real-world service scenarios.
![WidgetPreviewPage](figures/WidgetPreviewPage.png)
ArkTS widgets have full capabilities of JS widgets, with added animation and custom drawing capabilities plus partial support for components, events, animations, data management, and state management capabilities of the [declarative paradigm](../reference/arkui-ts/ts-components-summary.md). For details, see [Page Capabilities Supported by ArkTS Widgets](#page-capabilities-supported-by-arkts-widgets).
## Page Capabilities Supported by ArkTS Widgets
For details about the page capabilities supported by ArkTS widgets, see [Learning ArkTS](../quick-start/arkts-create-custom-components.md) and [ArkTS-based Declarative Development Paradigm](../reference/arkui-ts/ts-components-summary.md).
Only the APIs marked with "supported in ArkTS widgets" can be used for ArkTS widgets. Pay special attention to the capability differences with applications.
For example, the following description indicates that the @Component decorator can be used in ArkTS widgets.
![WidgetSupportApi](figures/WidgetSupportApi.png)
# Updating Widget Content by State
Multiple widgets of the same application can be configured to implement different features. For example, two weather widgets can be added to the home screen: one for displaying the weather of London, and the other Beijing. The widget is set to be updated at 07:00 every morning. It needs to detect the configured city, and then updates the city-specific weather information. The following example describes how to dynamically update the widget content based on the state.
- Widget configuration file: Configure the widget to be updated at 07:00 every morning.
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,"scheduledUpdateTime": "07:00",
"updateDuration": 0,
"defaultDimension": "2*2",
"supportDimensions": ["2*2"]
}
]
}
```
- Widget page: A widget has different states and needs to be updated by state. When the state changes, **postCardAction** is called to notify the EntryFormAbility.
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('textA') textA: string = 'To be updated...';
@LocalStorageProp('textB') textB: string ='To be updated...';
@State selectA: boolean = false;
@State selectB: boolean = false;
build() {
Column() {
Row() {
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.select(false)
.onChange((value: boolean) => {
this.selectA = value;
postCardAction(this, {
'action': 'message',
'params': {
'selectA': JSON.stringify(value)
}
});
})
Text ('State A')
}
Row() {
Checkbox({ name: 'checkbox2', group: 'checkboxGroup' })
.select(false)
.onChange((value: boolean) => {
this.selectB = value;
postCardAction(this, {
'action': 'message',
'params': {
'selectB': JSON.stringify(value)
}
});
})
Text ('State B')
}
Row() {// Content that is updated only in state A
Text('状态A: ')
Text(this.textA)
}
Row() { // Content that is updated only in state B
Text ('State B:')
Text(this.textB)
}
}.padding('10%')
}
}
```
- EntryFormAbility: The widget state data is stored in the local database. When the update event callback is triggered, the current widget state is obtained through **formId**, and then content is updated based on the state obtained.
```ts
import formInfo from '@ohos.app.form.formInfo'
import formProvider from '@ohos.app.form.formProvider';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import dataStorage from '@ohos.data.storage'
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want) {
let formId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
let isTempCard: boolean = want.parameters[formInfo.FormParam.TEMPORARY_KEY];
if (isTempCard === false) {// If the widget is a normal one, the widget information is persisted.
console.info('Not temp card, init db for:' + formId);
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
storeDB.putSync('A' + formId, 'false');
storeDB.putSync('B' + formId, 'false');
storeDB.flushSync();
}
let formData = {};
return formBindingData.createFormBindingData(formData);
}
onRemoveForm(formId) {
console.info('onRemoveForm, formId:' + formId);
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
storeDB.deleteSync('A' + formId);
storeDB.deleteSync('B' + formId);
}
// If the widget is a temporary one, it is recommended that the widget information be persisted when the widget is converted to a normal one.
onCastToNormalForm(formId) {
console.info('onCastToNormalForm, formId:' + formId);
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
storeDB.putSync('A' + formId, 'false');
storeDB.putSync('B' + formId, 'false');
storeDB.flushSync();
}
onUpdateForm(formId) {
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
let stateA = storeDB.getSync('A' + formId, 'false').toString()
let stateB = storeDB.getSync('B' + formId, 'false').toString()
// Update textA in state A.
if (stateA === 'true') {
let formInfo = formBindingData.createFormBindingData({
'textA': 'AAA'
})
formProvider.updateForm(formId, formInfo)
}
// Update textB in state B.
if (stateB === 'true') {
let formInfo = formBindingData.createFormBindingData({
'textB': 'BBB'
})
formProvider.updateForm(formId, formInfo)
}
}
onFormEvent(formId, message) {
// Store the widget state.
console.info('onFormEvent formId:' + formId + 'msg:' + message);
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
let msg = JSON.parse(message)
if (msg.selectA != undefined) {
console.info('onFormEvent selectA info:' + msg.selectA);
storeDB.putSync('A' + formId, msg.selectA);
}
if (msg.selectB != undefined) {
console.info('onFormEvent selectB info:' + msg.selectB);
storeDB.putSync('B' + formId, msg.selectB);
}
storeDB.flushSync();
}
};
```
> **NOTE**
> When the local database is used for widget information persistence, it is recommended that [TEMPORARY_KEY](../reference/apis/js-apis-app-form-formInfo.md#formparam) be used to determine whether the currently added widget is a normal one in the [onAddForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onaddform) lifecycle callback. If the widget is a normal one, the widget information is directly persisted. If the widget is a temporary one, the widget information is persisted when the widget is converted to a normal one ([onCastToNormalForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#oncasttonormalform)). In addition, the persistent widget information needs to be deleted when the widget is destroyed ([onRemoveForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onremoveform)), preventing the database size from continuously increasing due to repeated widget addition and deletion.
# Configuring a Widget to Update Periodically
Before configuring a widget to update periodically, enable the periodic update feature by setting the **updateEnabled** field to **true** in the **form_config.json** file.
The widget framework provides the following modes of updating widgets periodically:
- Set the update interval: The widget will be updated at the specified interval. You can specify the interval by setting the [updateDuration](arkts-ui-widget-configuration.md) field in the **form_config.json** file. For example, you can configure the widget to update once an hour.
> **NOTE**
>
> **updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true, // Enable the periodic update feature.
"scheduledUpdateTime": "10:30",
"updateDuration": 2, // Set the interval to update the widget. The value is a natural number, in the unit of 30 minutes.
"defaultDimension": "2*2",
"supportDimensions": ["2*2"]
}
]
}
```
- Set the scheduled update time: The widget will be updated at the scheduled time every day. You can specify the time by setting the [scheduledUpdateTime](arkts-ui-widget-configuration.md) field in the **form_config.json** file. For example, you can configure the widget to update at 10:30 a.m. every day.
> **NOTE**
>
> **updateDuration** takes precedence over **scheduledUpdateTime**. For the **scheduledUpdateTime** settings to take effect, set **updateDuration** to **0**.
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true, // Enable the periodic update feature.
"scheduledUpdateTime": "10:30", // Set the scheduled time to update the widget.
"updateDuration": 0,
"defaultDimension": "2*2",
"supportDimensions": ["2*2"]
}
]
}
```
- Set the next update time: The widget will be updated next time at the specified time. You can specify the time by calling the [setFormNextRefreshTime()](../reference/apis/js-apis-app-form-formProvider.md#setformnextrefreshtime) API. The minimum update interval is 5 minutes. For example, you can configure the widget to update within 5 minutes after the API is called.
```ts
import formProvider from '@ohos.app.form.formProvider';
let formId = '123456789'; // Use the actual widget ID in real-world scenarios.
try {
// Configure the widget to update in 5 minutes.
formProvider.setFormNextRefreshTime(formId, 5, (err, data) => {
if (err) {
console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`);
return;
} else {
console.info('Succeeded in setFormNextRefreshTimeing.');
}
});
} catch (err) {
console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`);
}
```
When periodic update is triggered, the system calls the [onUpdateForm()](../reference/apis/js-apis-app-form-formExtensionAbility.md#onupdateform) lifecycle callback of the FormExtensionAbility. In the callback, [updateForm()](../reference/apis/js-apis-app-form-formProvider.md#updateform) can be used to update the widget by the provider. For details about how to use **onUpdateForm()**, see [Updating Widget Content Through FormExtensionAbility](arkts-ui-widget-event-formextensionability.md).
> **NOTE**
> 1. Each widget can be updated at the specified interval for a maximum of 50 times every day, including updates triggered by setting [updateDuration](arkts-ui-widget-configuration.md) or calling [setFormNextRefreshTime()](../reference/apis/js-apis-app-form-formProvider.md#setformnextrefreshtime). When the limit is reached, the widget cannot be updated in this mode again. The number of update times is reset at 00:00 every day.
>
> 2. The same timer is used for timing updates at the specified interval. Therefore, the first scheduled update of widgets may have a maximum deviation of 30 minutes. For example, the first widget A (updated every half an hour) is added at 03:20. The timer starts and triggers an update every half an hour. The second widget B (updated every half an hour) is added at 03:40. When the timer event is triggered at 03:50, widget A is updated, and widget B will be updated at 04:20 next time.
>
> 3. Updates at the specified interval and updates at the scheduled time are triggered only when the screen is on. When the screen is off, the update action is merely recorded. When the screen is on, the update action is performed.
# ArkTS Widget Working Principles
## Implementation Principles
**Figure 1** ArkTS widget implementation principles
![WidgetPrinciple](figures/WidgetPrinciple.png)
- Widget host: an application that displays the widget content and controls the widget location. Only the system application can function as a widget host.
- Widget provider: an application that provides the widget content to display and controls how widget components are laid out and how they interact with users.
- Widget Manager: a resident agent that manages widgets in the system. It provides the [formProvider](../reference/apis/js-apis-app-form-formProvider.md) and [formHost](../reference/apis/js-apis-app-form-formHost.md) APIs as well as widget management, usage, and periodic updates.
- Widget rendering service: a service that manages widget rendering instances. Widget rendering instances are bound to the [widget components](../reference/arkui-ts/ts-basic-components-formcomponent.md) on the widget host on a one-to-one basis. The widget rendering service runs the widget page code **widgets.abc** for rendering, and sends the rendered data to the corresponding widget component on the widget host.
**Figure 2** Working principles of the ArkTS widget rendering service
![WidgetRender](figures/WidgetRender.png)
Unlike JS widgets, ArkTS widgets support logic code running. To avoid potential ArkTS widget issues from affecting the use of applications, the widget page code **widgets.abc** is executed by the widget rendering service, which is managed by the Widget Manager. Each widget component of a widget host corresponds to a rendering instance in the widget rendering service. Rendering instances of an application provider run in the same virtual machine operating environment, and rendering instances of different application providers run in different virtual machine operating environments. In this way, the resources and state data are isolated between widgets of different application providers. During development, pay attention to the use of the [globalThis](uiability-data-sync-with-ui.md#using-globalthis-between-uiability-and-page) object. Use one **globalThis** object for widgets by the same application provider, and different **globalThis** objects for widgets by different application providers.
## Advantages of ArkTS Widgets
As a quick entry to applications, ArkTS widgets have the following advantages over JS widgets:
- Improved development experience and efficiency, thanks to the unified development paradigm
ArkTS widgets share the same declarative UI development framework as application pages. This means that the page layouts can be directly reused in widgets, improving development experience and efficiency.
**Figure 3** Comparison of widget project structures
![WidgetProject](figures/WidgetProject.png)
- More widget features
- Animation: The ArkTS widget supports the [attribute animation](../reference/arkui-ts/ts-animatorproperty.md) and [explicit animation](../reference/arkui-ts/ts-explicit-animation.md) capabilities, which can be leveraged to deliver a more engaging experience.
- Custom drawing: The ArkTS widget allows you to draw graphics with the [Canvas](../reference/arkui-ts/ts-components-canvas-canvas.md) component to present information more vividly.
- Logic code execution: The capability to run logic code in widgets means that service logic can be self-closed in widgets, expanding the service application scenarios of widgets.
## Constraints on ArkTS Widgets
Compared with JS widgets, ArkTS widgets provide more capabilities, but they are also more prone to malicious behavior. The ArkTS widget is displayed in the widget host, which is usually the home screen. To ensure user experience and power consumption, the ArkTS widget capability is restricted as follows:
- The .so file cannot be loaded.
- The native programming language cannot be used for development.
- Only [partial](arkts-ui-widget-page-overview.md) components, events, animations, data management, state management, and API capabilities of the declarative paradigm are supported.
- The event processing of the widget is independent of that of the widget host. It is recommended that you do not use the left and right sliding components when the widget host supports left and right swipes to prevent gesture conflicts.
The following features are coming to ArkTS widgets in later versions:
- Breakpoint debugging
- import statements
- Instant preview
...@@ -25,7 +25,8 @@ An [ExtensionAbilityType](../reference/apis/js-apis-bundleManager.md#extensionab ...@@ -25,7 +25,8 @@ An [ExtensionAbilityType](../reference/apis/js-apis-bundleManager.md#extensionab
- [EnterpriseAdminExtensionAbility](../reference/apis/js-apis-EnterpriseAdminExtensionAbility.md): ExtensionAbility component of the enterprise_admin type, which provides APIs for processing enterprise management events, such as application installation events on devices and events indicating too many incorrect screen-lock password attempts. - [EnterpriseAdminExtensionAbility](../reference/apis/js-apis-EnterpriseAdminExtensionAbility.md): ExtensionAbility component of the enterprise_admin type, which provides APIs for processing enterprise management events, such as application installation events on devices and events indicating too many incorrect screen-lock password attempts.
> **NOTE**<br> > **NOTE**
>
> 1. Third-party applications cannot implement ServiceExtensionAbility, DataShareExtensionAbility, StaticSubscriberExtensionAbility, or WindowExtensionAbility. > 1. Third-party applications cannot implement ServiceExtensionAbility, DataShareExtensionAbility, StaticSubscriberExtensionAbility, or WindowExtensionAbility.
> >
> 2. To implement transaction processing in the background for a third-party application, use background tasks rather than ServiceExtensionAbility. For details, see [Background Task](../task-management/background-task-overview.md). > 2. To implement transaction processing in the background for a third-party application, use background tasks rather than ServiceExtensionAbility. For details, see [Background Task](../task-management/background-task-overview.md).
...@@ -45,7 +46,7 @@ The following uses [InputMethodExtensionAbility](../reference/apis/js-apis-input ...@@ -45,7 +46,7 @@ The following uses [InputMethodExtensionAbility](../reference/apis/js-apis-input
## Implementing ExtensionAbility of the Specified Type ## Implementing ExtensionAbility of the Specified Type
The following uses [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) as an example. The widget framework provides the base class [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md). You derive this base class to create your own class (such as **MyFormExtensionAbility**), implement the callbacks, such as **onCreate()** and **onUpdateForm()**, to provide specific widget functionalities. For details, see [FormExtensionAbility](Widget-development-stage.md). The following uses [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) as an example. The widget framework provides the base class [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md). You derive this base class to create your own class (such as **MyFormExtensionAbility**), implement the callbacks, such as **onCreate()** and **onUpdateForm()**, to provide specific widget functionalities. For details, see [FormExtensionAbility](service-widget-overview.md).
You do not need to care when to add or delete a widget. The lifecycle of the FormExtensionAbility instance and the lifecycle of the ExtensionAbility process where the FormExtensionAbility instance is located are scheduled and managed by FormManagerService. You do not need to care when to add or delete a widget. The lifecycle of the FormExtensionAbility instance and the lifecycle of the ExtensionAbility process where the FormExtensionAbility instance is located are scheduled and managed by FormManagerService.
...@@ -63,3 +64,5 @@ You do not need to care when to add or delete a widget. The lifecycle of the For ...@@ -63,3 +64,5 @@ You do not need to care when to add or delete a widget. The lifecycle of the For
> - The two FormExtensionAbility components run in an independent process. > - The two FormExtensionAbility components run in an independent process.
> >
> - The two ImeExtensionAbility components run in an independent process. > - The two ImeExtensionAbility components run in an independent process.
<!--no_check-->
\ No newline at end of file
# Service Widget Overview
A service widget (also called widget) is a set of UI components that display important information or operations specific to an application. It provides users with direct access to a desired application service, without the need to open the application first. A widget usually appears as a part of the UI of another application (which currently can only be a system application, such as the home screen) and provides basic interactive features such as opening a UI page or sending a message.
## Service Widget Architecture
**Figure 1** Service widget architecture
![WidgetArchitecture](figures/WidgetArchitecture.png)
Before you get started, it would be helpful if you have a basic understanding of the following concepts:
- Widget host: an application that displays the widget content and controls the widget location. An example is the home screen in the preceding figure.
- Application icon: an application entry icon, clicking which starts the application process. The icon content does not support interactions.
- Widget: an interactive UI in various sizes. It may provide buttons to implement different functions, such as the button to [update the widget content](arkts-ui-widget-event-formextensionability.md) or [switch to an application](arkts-ui-widget-event-router.md).
- Card provider: an application that provides service widget content to be displayed. It controls the display content, display logic, and component click events triggered on a service widget.
- FormExtensionAbility: widget service logic module, which provides lifecycle callbacks invoked when a widget is created, destroyed, or updated.
- Widget page: widget UI module, which contains display and interaction information such as components, layouts, and events.
Below is the typical procedure of using the widget:
**Figure 2** Typical procedure of using the widget
![WidgetUse](figures/WidgetUse.png)
1. Touch and hold an application icon on the home screen to display the shortcut menu.
2. Touch **Service widget** to access the preview screen.
3. Touch the **Add to home** button. The widget is then added to the home screen.
## Widget UI Development Mode
In the stage model, the UI of a widget can be developed in [ArkTS](arkts-ui-widget-working-principles.md) or [JS](js-ui-widget-development.md).
- A widget developed in the ArkTS-based declarative development paradigm is called ArkTS widget.
- A widget developed in the JS-compatible web-like development paradigm is called JS widget.
ArkTS widgets and JS widgets have different implementation principles and features. The following table lists the differences in capabilities.
| Category| JS widget| ArkTS widget|
| -------- | -------- | -------- |
| Development paradigm| Web-like paradigm| Declarative paradigm|
| Component capability| Supported| Supported|
| Layout capability| Supported| Supported|
| Event capability| Supported| Supported|
| Custom animation| Not supported| Supported|
| Custom drawing| Not supported| Supported|
| Logic code execution (excluding the import capability)| Not supported| Supported|
As can be seen above, ArkTS widgets have more capabilities and use cases than JS widgets. Therefore, ArkTS widgets are always recommended, except for the case where the widget consists of only static pages.
...@@ -232,7 +232,7 @@ The widget configuration file is named **config.json**. Find the **config.json** ...@@ -232,7 +232,7 @@ The widget configuration file is named **config.json**. Find the **config.json**
"type": "service", "type": "service",
"srcLanguage": "ets", "srcLanguage": "ets",
"formsEnabled": true, "formsEnabled": true,
"formConfigAbility": "ability://com.example.entry.EntryAbility", "formConfigAbility": "ability://com.example.entry.MainAbility",
"forms": [{ "forms": [{
"colorMode": "auto", "colorMode": "auto",
"defaultDimension": "2*2", "defaultDimension": "2*2",
...@@ -323,7 +323,7 @@ async function deleteFormInfo(formId: string) { ...@@ -323,7 +323,7 @@ async function deleteFormInfo(formId: string) {
// ... // ...
``` ```
For details about how to implement persistent data storage, see [Lightweight Data Store Development](../database/database-preference-guidelines.md). For details about how to implement persistent data storage, see [Application Data Persistence Overview](../database/app-data-persistence-overview.md).
The **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary. The **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary.
...@@ -434,7 +434,7 @@ You can use the web-like paradigm (HML+CSS+JSON) to develop JS widget pages. Thi ...@@ -434,7 +434,7 @@ You can use the web-like paradigm (HML+CSS+JSON) to develop JS widget pages. Thi
"actions": { "actions": {
"routerEvent": { "routerEvent": {
"action": "router", "action": "router",
"abilityName": "com.example.entry.EntryAbility", "abilityName": "com.example.entry.MainAbility",
"params": { "params": {
"message": "add detail" "message": "add detail"
} }
...@@ -452,8 +452,8 @@ You can set router and message events for components on a widget. The router eve ...@@ -452,8 +452,8 @@ You can set router and message events for components on a widget. The router eve
2. Set the router event. 2. Set the router event.
- **action**: **"router"**, which indicates a router event. - **action**: **"router"**, which indicates a router event.
- **abilityName**: name of the ability to redirect to (PageAbility component in the FA model and UIAbility component in the stage model). For example, the default UIAbility name created by DevEco Studio in the FA model is com.example.entry.EntryAbility. - **abilityName**: name of the ability to redirect to (PageAbility component in the FA model and UIAbility component in the stage model). For example, the default MainAbility name created by DevEco Studio in the FA model is com.example.entry.MainAbility.
- **params**: custom parameters passed to the target ability. Set them as required. The value can be obtained from **parameters** in **want** used for starting the target ability. For example, in the lifecycle function **onCreate** of the EntryAbility in the FA model, **featureAbility.getWant()** can be used to obtain **want** and its **parameters** field. - **params**: custom parameters passed to the target ability. Set them as required. The value can be obtained from **parameters** in **want** used for starting the target ability. For example, in the lifecycle function **onCreate** of the MainAbility in the FA model, **featureAbility.getWant()** can be used to obtain **want** and its **parameters** field.
3. Set the message event. 3. Set the message event.
- **action**: **"message"**, which indicates a message event. - **action**: **"message"**, which indicates a message event.
...@@ -529,7 +529,7 @@ The following is an example: ...@@ -529,7 +529,7 @@ The following is an example:
"actions": { "actions": {
"routerEvent": { "routerEvent": {
"action": "router", "action": "router",
"abilityName": "com.example.entry.EntryAbility", "abilityName": "com.example.entry.MainAbility",
"params": { "params": {
"message": "add detail" "message": "add detail"
} }
......
...@@ -68,7 +68,7 @@ When data is added, deleted, or modified, a notification is sent to the subscrib ...@@ -68,7 +68,7 @@ When data is added, deleted, or modified, a notification is sent to the subscrib
- The KV stores do not support custom conflict resolution policies for applications. - The KV stores do not support custom conflict resolution policies for applications.
- A maximum of 16 distributed KV stores can be opened simultaneously for an application. - A maximum of 16 KV stores can be opened simultaneously for an application.
- Each KV store supports a maximum of eight callbacks for subscription of data change notifications. - Each KV store supports a maximum of eight callbacks for subscription of data change notifications.
...@@ -79,19 +79,19 @@ When data is added, deleted, or modified, a notification is sent to the subscrib ...@@ -79,19 +79,19 @@ When data is added, deleted, or modified, a notification is sent to the subscrib
The following table lists the APIs for cross-device data synchronization of the single KV store. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md). The following table lists the APIs for cross-device data synchronization of the single KV store. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md).
| API| Description| | API| Description|
| -------- | -------- | | -------- | -------- |
| createKVManager(config: KVManagerConfig): KVManager | Creates a **KvManager** instance to manage database objects.| | createKVManager(config: KVManagerConfig): KVManager | Creates a **KvManager** instance to manage database objects.|
| getKVStore&lt;T&gt;(storeId: string, options: Options, callback: AsyncCallback&lt;T&gt;): void | Creates and obtains a KV store of the specified type.| | getKVStore&lt;T&gt;(storeId: string, options: Options, callback: AsyncCallback&lt;T&gt;): void | Creates and obtains a KV store of the specified type.|
| put(key: string, value: Uint8Array\|string\|number\|boolean, callback: AsyncCallback&lt;void&gt;): void | Inserts and updates data.| | put(key: string, value: Uint8Array\|string\|number\|boolean, callback: AsyncCallback&lt;void&gt;): void | Inserts and updates data.|
| on(event: 'dataChange', type: SubscribeType, listener: Callback&lt;ChangeNotification&gt;): void | Subscribes to data changes in the KV store.| | on(event: 'dataChange', type: SubscribeType, listener: Callback&lt;ChangeNotification&gt;): void | Subscribes to data changes in the KV store.|
| get(key: string, callback: AsyncCallback&lt;boolean \| string \| number \| Uint8Array&gt;): void | Queries the value of the specified key.| | get(key: string, callback: AsyncCallback&lt;boolean \| string \| number \| Uint8Array&gt;): void | Queries the value of the specified key.|
| sync(deviceIds: string[], mode: SyncMode, delayMs?: number): void | Triggers a manual synchronization of the KV store.| | sync(deviceIds: string[], mode: SyncMode, delayMs?: number): void | Triggers a manual synchronization of the KV store.|
## How to Develop ## How to Develop
The following uses a single KV store as an example to describe how to implement cross-device data synchronization. The development process is as follows. The following uses a single KV store as an example to describe how to implement cross-device data synchronization. The following describes the development process.
![kvStore_development_process](figures/kvStore_development_process.png) ![kvStore_development_process](figures/kvStore_development_process.png)
...@@ -100,7 +100,7 @@ The following uses a single KV store as an example to describe how to implement ...@@ -100,7 +100,7 @@ The following uses a single KV store as an example to describe how to implement
> The data on a device can be synchronized only to the devices whose data security labels are not higher than the security level of the device. For details, see [Access Control Mechanism in Cross-Device Synchronization](sync-app-data-across-devices-overview.md#access-control-mechanism-in-cross-device-synchronization). > The data on a device can be synchronized only to the devices whose data security labels are not higher than the security level of the device. For details, see [Access Control Mechanism in Cross-Device Synchronization](sync-app-data-across-devices-overview.md#access-control-mechanism-in-cross-device-synchronization).
1. Import the module. 1. Import the module.
```js ```js
import distributedKVStore from '@ohos.data.distributedKVStore'; import distributedKVStore from '@ohos.data.distributedKVStore';
``` ```
...@@ -115,7 +115,7 @@ The following uses a single KV store as an example to describe how to implement ...@@ -115,7 +115,7 @@ The following uses a single KV store as an example to describe how to implement
1. Create a **kvManagerConfig** object based on the application context. 1. Create a **kvManagerConfig** object based on the application context.
2. Create a **KvManager** instance. 2. Create a **KvManager** instance.
```js ```js
// Obtain the context of the stage model. // Obtain the context of the stage model.
import UIAbility from '@ohos.app.ability.UIAbility'; import UIAbility from '@ohos.app.ability.UIAbility';
...@@ -152,7 +152,7 @@ The following uses a single KV store as an example to describe how to implement ...@@ -152,7 +152,7 @@ The following uses a single KV store as an example to describe how to implement
1. Declare the ID of the distributed KV store to create. 1. Declare the ID of the distributed KV store to create.
2. Disable the auto synchronization function (**autoSync:false**) to facilitate subsequent verification of the synchronization function. If synchronization is required, call the **sync()** interface. 2. Disable the auto synchronization function (**autoSync:false**) to facilitate subsequent verification of the synchronization function. If synchronization is required, call the **sync()** interface.
```js ```js
try { try {
const options = { const options = {
...@@ -179,7 +179,7 @@ The following uses a single KV store as an example to describe how to implement ...@@ -179,7 +179,7 @@ The following uses a single KV store as an example to describe how to implement
``` ```
5. Subscribe to changes of distributed data. 5. Subscribe to changes of distributed data.
```js ```js
try { try {
kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => { kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {
...@@ -195,7 +195,7 @@ The following uses a single KV store as an example to describe how to implement ...@@ -195,7 +195,7 @@ The following uses a single KV store as an example to describe how to implement
1. Construct the key and value to be written to the single KV store. 1. Construct the key and value to be written to the single KV store.
2. Write KV pairs to the single KV store. 2. Write KV pairs to the single KV store.
```js ```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string'; const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string'; const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
...@@ -217,7 +217,7 @@ The following uses a single KV store as an example to describe how to implement ...@@ -217,7 +217,7 @@ The following uses a single KV store as an example to describe how to implement
1. Construct the key to be queried from the single KV store. 1. Construct the key to be queried from the single KV store.
2. Query data from the single KV store. 2. Query data from the single KV store.
```js ```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string'; const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string'; const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
...@@ -249,7 +249,7 @@ The following uses a single KV store as an example to describe how to implement ...@@ -249,7 +249,7 @@ The following uses a single KV store as an example to describe how to implement
> >
> If manual synchronization is used, **deviceIds** is obtained by using [devManager.getTrustedDeviceListSync](../reference/apis/js-apis-device-manager.md#gettrusteddevicelistsync). The APIs of the **deviceManager** module are all system interfaces and available only to system applications. > If manual synchronization is used, **deviceIds** is obtained by using [devManager.getTrustedDeviceListSync](../reference/apis/js-apis-device-manager.md#gettrusteddevicelistsync). The APIs of the **deviceManager** module are all system interfaces and available only to system applications.
```js ```js
import deviceManager from '@ohos.distributedHardware.deviceManager'; import deviceManager from '@ohos.distributedHardware.deviceManager';
......
# DataShare Overview # Cross-Application Data Sharing Overview
## Function ## Function
The **DataShare** module allows an application to share its data with other applications. Currently, data can be shared only between applications on the same device. The application data on a device, such as the Contacts, short message service (SMS), and Gallery data, always needs to be shared with other applications. However, certain data, such as the accounts and passwords, cannot be shared. Certain data, such as SMS messages, can be accessed but not modified by other applications. The **DataShare** module provides a secure and easy-to-use mechanism for sharing data of an application with other applications on the same device.
Data needs to be shared in a wealth of scenarios. For example, the Contacts, short message service (SMS), and Gallery data always needs to be shared with other applications. However, certain data, such as accounts and passwords, cannot be shared. Some data, such as SMS messages, can be queried but not modified by other applications. **DataShare** provides a secure and convenient sharing mechanism for application data. ## Basic Concepts
The data provider can directly use **DataShare** to share data with other applications without complex encapsulation. The data consumer only needs to use a set of APIs because the DataShare access mode does not vary with the data provision mode. This greatly reduces the learning time and development difficulty. Before developing cross-application data sharing on a device, understand the following concepts:
Data can be shared across applications in either of the following ways: - Data provider: an application that provides data and implements related services. It is also called the data producer or server.
- Using **DataShareExtensionAbility** - Data consumer: an application that accesses the data or services provided by the data provider. It is also called the client.
An extension ability is implemented in the HAP to invoke a callback. When the data consumer calls an API, the extension ability of the data provider will be automatically started to invoke the registered callback.
You can use **DataShareExtensionAbility** when the cross-application data access involves service operations other than mere addition, deletion, modification, and query of data in databases. - **ValuesBucket**: a set of data to be inserted. It can be one or more data records in KV pairs. In each KV pair, the key must be of the string type, and the value can be a number, a string, a Boolean value, or an unsigned integer array.
- Using Silent Access - **ResultSet**: a set of query results. It provides flexible modes for obtaining various data.
Database access rules are configured in the HAP. When the data consumer calls an API, the system ability automatically obtains the access rules in the HAP and returns data without starting the data provider.
You can use the silent access feature when the cross-application data access involves only the operations for adding, deleting, modifying, and querying data in databases. - **Predicates**: an object that specifies the conditions for updating, deleting, or querying data in a database.
## Basic Concepts ## Implementation
Before developing cross-application data sharing on a device, understand the following concepts: The data provider can directly use **DataShare** to share data with other applications without complex encapsulation. The data consumer only needs to use a set of APIs to access the data, because the **DataShare** access mode does not vary with the data provisioning mode. This greatly reduces the learning time and development difficulty.
- Data provider: an application that provides data and implements related services. It is also called the producer or server. The cross-application data sharing can be implemented in either of the following ways:
- Data consumer: an application that accesses the data or services provided by the data provider. It is also called the client. - **DataShareExtensionAbility**
- **ValuesBucket**: a set of data to be inserted. It can be one or more data records in KV pairs. In each KV pair, the key must be of the string type, and the value can be a number, a string, a Boolean value, or an unsigned integer array. You can implement an ExtensionAbility with a callback in the HAP. When the data consumer calls an API, the ExtensionAbility of the data provider will be automatically started to invoke the registered callback.
- **ResultSet**: a set of query results. It provides flexible modes for users to obtain various data. This method is recommended when the cross-application data access involves service operations other than mere addition, deletion, modification, and query of data in databases.
- **Predicates**: an object that specifies the conditions for updating, deleting, or querying data in a database. - Silent access
You can configure database access rules in the HAP. When the data consumer calls an API, the system ability automatically obtains the access rules in the HAP and returns data without starting the data provider.
This method is recommended when the cross-application data access involves only the operations for adding, deleting, modifying, and querying data in databases.
## Constraints ## Constraints
- **DataShare** is subject to the limitations on the database used by the data provider. For example, the supported data models, length of the keys and values, and maximum number of databases that can be accessed at a time by each application vary with the database in use. - **DataShare** is subject to the limitations on the database used by the data provider. The supported data models, length of the keys and values, and maximum number of databases that can be accessed at a time by each application vary with the database in use.
- The payloads of **ValuesBucket**, **Predicates**, and **ResultSet** are restricted by IPC. - The payloads of **ValuesBucket**, **Predicates**, and **ResultSet** are restricted by inter-process communication (IPC).
- Currently, **dataShare** supports development based on the stage model only. - Currently, **dataShare** supports development based on the stage model only.
# FAQs # FAQs
- [Ability Development](faqs-ability.md) - [Ability Framework Development](faqs-ability.md)
- [Bundle Management Development](faqs-bundle.md)
- [DFX Development](faqs-dfx.md)
- [Common Event and Notification Development](faqs-event-notification.md)
- [File Management Development](faqs-file-management.md)
- [Resource Management Development](faqs-globalization.md) - [Resource Management Development](faqs-globalization.md)
- [Common Event and Notification Development](faqs-event-notification.md)
- [Graphics and Image Development](faqs-graphics.md) - [Graphics and Image Development](faqs-graphics.md)
- [File Management Development](faqs-file-management.md)
- [Network Management Development](faqs-network-management.md) - [Network Management Development](faqs-network-management.md)
- [DFX Development](faqs-dfx.md)
- [Startup Development](faqs-startup.md) - [Startup Development](faqs-startup.md)
- [Usage of Third- and Fourth-Party Libraries](faqs-third-party-library.md) - [Usage of Third- and Fourth-Party Libraries](faqs-third-fourth-party-library.md)
\ No newline at end of file \ No newline at end of file
...@@ -117,12 +117,12 @@ To fully understand the preceding example, a knowledge of the following concepts ...@@ -117,12 +117,12 @@ To fully understand the preceding example, a knowledge of the following concepts
## Member Functions/Variables ## Member Functions/Variables
In addition to the mandatory** build()** function, a custom component may implement other member functions with the following restrictions: In addition to the mandatory **build()** function, a custom component may implement other member functions with the following restrictions:
- Static functions are not supported. - Static functions are not supported.
- Access to the member functions is always private. Defining **private** access is optional. Defining access other than **private** is a syntax error. - Access to the member functions is always private.
A custom component can also implement member variables with the following restrictions: A custom component can also implement member variables with the following restrictions:
...@@ -130,7 +130,7 @@ A custom component can also implement member variables with the following restri ...@@ -130,7 +130,7 @@ A custom component can also implement member variables with the following restri
- Static member variables are not supported. - Static member variables are not supported.
- Access to the member variables is always private.The access rules of member variables are the same as those of member functions. - Access to the member variables is always private. The access rules of member variables are the same as those of member functions.
- Local initialization is optional for some member variables and mandatory for others. For details about whether local initialization or initialization from the parent component is required, see [State Management](arkts-state-management-overview.md). - Local initialization is optional for some member variables and mandatory for others. For details about whether local initialization or initialization from the parent component is required, see [State Management](arkts-state-management-overview.md).
......
...@@ -129,12 +129,12 @@ struct MyComponent { ...@@ -129,12 +129,12 @@ struct MyComponent {
build() { build() {
Column() { Column() {
// When this.showChild is true, the Child child component is created, and Child aboutToAppear is invoked. // When this.showChild is true, the Child component is created, and Child aboutToAppear is invoked.
if (this.showChild) { if (this.showChild) {
Child() Child()
} }
// When this.showChild is false, the Child child component is deleted, and Child aboutToDisappear is invoked. // When this.showChild is false, the Child component is deleted, and Child aboutToDisappear is invoked.
Button('create or delete Child').onClick(() => { Button('delete Child').onClick(() => {
this.showChild = false; this.showChild = false;
}) })
// Because of the pushing from the current page to Page2, onPageHide is invoked. // Because of the pushing from the current page to Page2, onPageHide is invoked.
......
...@@ -275,7 +275,7 @@ struct ReaderComp { ...@@ -275,7 +275,7 @@ struct ReaderComp {
build() { build() {
Row() { Row() {
Text(this.title) Text(this.title)
Text(`... ${this.readIt ? 'I have read' : 'I have bot read it'}`) Text(`... ${this.readIt ? 'I have read' : 'I have not read it'}`)
.onClick(() => this.readIt = true) .onClick(() => this.readIt = true)
} }
} }
...@@ -350,17 +350,12 @@ struct MainProgram { ...@@ -350,17 +350,12 @@ struct MainProgram {
} }
Row() { Row() {
Column( Column()
// customCounter must be initialized from the parent component due to lack of local initialization. Here, customCounter2 does not need to be initialized. // customCounter must be initialized from the parent component due to lack of local initialization. Here, customCounter2 does not need to be initialized.
MyComponent({ customCounter: this.mainCounter }) MyComponent({ customCounter: this.mainCounter })
// customCounter2 of the child component can also be initialized from the parent component. The value from the parent component overwrites the locally assigned value of customCounter2 during initialization. // customCounter2 of the child component can also be initialized from the parent component. The value from the parent component overwrites the locally assigned value of customCounter2 during initialization.
MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter }) MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })
}.width('40%') }.width('40%')
}
Row() {
Text('').width(480).height(10)
}
} }
} }
} }
......
...@@ -235,7 +235,7 @@ struct MyComponent { ...@@ -235,7 +235,7 @@ struct MyComponent {
}) })
Button(`Click to increase count=${this.count}`).onClick(() => { Button(`Click to increase count=${this.count}`).onClick(() => {
// The update of the @State decorated variable triggers the update of the <Text> component. // The update of the @State decorated variable triggers the update of the <Button> component.
this.count += this.increaseBy; this.count += this.increaseBy;
}) })
} }
......
...@@ -29,7 +29,7 @@ Creates a **Filter** instance based on the pixel map. ...@@ -29,7 +29,7 @@ Creates a **Filter** instance based on the pixel map.
| Name | Type | Mandatory| Description | | Name | Type | Mandatory| Description |
| ------- | ----------------- | ---- | -------- | | ------- | ----------------- | ---- | -------- |
| source | [image.PixelMap](js-apis-image.md#pixelmap7) | Yes | **PixelMap** instance created by the image module. | | source | [image.PixelMap](js-apis-image.md#pixelmap7) | Yes | **PixelMap** instance created by the image module. An instance can be obtained by decoding an image or directly created. For details, see [Image Overview](../../media/image-overview.md). |
**Return value** **Return value**
...@@ -61,7 +61,7 @@ Creates a **ColorPicker** instance based on the pixel map. This API uses a promi ...@@ -61,7 +61,7 @@ Creates a **ColorPicker** instance based on the pixel map. This API uses a promi
| Name | Type | Mandatory| Description | | Name | Type | Mandatory| Description |
| -------- | ----------- | ---- | -------------------------- | | -------- | ----------- | ---- | -------------------------- |
| source | [image.PixelMap](js-apis-image.md#pixelmap7) | Yes | **PixelMap** instance created by the image module.| | source | [image.PixelMap](js-apis-image.md#pixelmap7) | Yes | **PixelMap** instance created by the image module. An instance can be obtained by decoding an image or directly created. For details, see [Image Overview](../../media/image-overview.md).|
**Return value** **Return value**
...@@ -95,7 +95,7 @@ Creates a **ColorPicker** instance based on the pixel map. This API uses an asyn ...@@ -95,7 +95,7 @@ Creates a **ColorPicker** instance based on the pixel map. This API uses an asyn
| Name | Type | Mandatory| Description | | Name | Type | Mandatory| Description |
| -------- | ------------------ | ---- | -------------------------- | | -------- | ------------------ | ---- | -------------------------- |
| source | [image.PixelMap](js-apis-image.md#pixelmap7) | Yes |**PixelMap** instance created by the image module. | | source | [image.PixelMap](js-apis-image.md#pixelmap7) | Yes |**PixelMap** instance created by the image module. An instance can be obtained by decoding an image or directly created. For details, see [Image Overview](../../media/image-overview.md). |
| callback | AsyncCallback\<[ColorPicker](#colorpicker)> | Yes | Callback used to return the **ColorPicker** instance created.| | callback | AsyncCallback\<[ColorPicker](#colorpicker)> | Yes | Callback used to return the **ColorPicker** instance created.|
**Example** **Example**
...@@ -131,13 +131,13 @@ A class that stores the color picked. ...@@ -131,13 +131,13 @@ A class that stores the color picked.
## ColorPicker ## ColorPicker
A class used to obtain the main color of an image from its data. Before calling any method of **ColorPicker**, use [createColorPicker](#effectkitcreatecolorpicker) to create a **ColorPicker** instance. A class used to obtain the color from an image. Before calling any method of **ColorPicker**, use [createColorPicker](#effectkitcreatecolorpicker) to create a **ColorPicker** instance.
### getMainColor ### getMainColor
getMainColor(): Promise\<Color> getMainColor(): Promise\<Color>
Obtains the main color of the image and writes the result to a **[Color](#color)** instance. This API uses a promise to return the result. Obtains the main color from the image and writes the result to a [Color](#color) instance. This API uses a promise to return the result.
**System capability**: SystemCapability.Multimedia.Image.Core **System capability**: SystemCapability.Multimedia.Image.Core
...@@ -162,7 +162,7 @@ colorPicker.getMainColor().then(color => { ...@@ -162,7 +162,7 @@ colorPicker.getMainColor().then(color => {
getMainColorSync(): Color getMainColorSync(): Color
Obtains the main color of the image and writes the result to a **[Color](#color)** instance. This API returns the result in synchronous mode. Obtains the main color from the image and writes the result to a [Color](#color) instance. This API returns the result in synchronous mode.
**System capability**: SystemCapability.Multimedia.Image.Core **System capability**: SystemCapability.Multimedia.Image.Core
......
...@@ -161,6 +161,7 @@ ...@@ -161,6 +161,7 @@
- [Time Picker Dialog Box](ts-methods-timepicker-dialog.md) - [Time Picker Dialog Box](ts-methods-timepicker-dialog.md)
- [Text Picker Dialog Box](ts-methods-textpicker-dialog.md) - [Text Picker Dialog Box](ts-methods-textpicker-dialog.md)
- [Menu](ts-methods-menu.md) - [Menu](ts-methods-menu.md)
- [Custom Component Lifecycle](ts-custom-component-lifecycle.md)
- [State Management with Application-level Variables](ts-state-management.md) - [State Management with Application-level Variables](ts-state-management.md)
- [Pixel Units](ts-pixel-units.md) - [Pixel Units](ts-pixel-units.md)
- [Enums](ts-appendix-enums.md) - [Enums](ts-appendix-enums.md)
......
...@@ -16,12 +16,11 @@ Search(options?: { value?: string; placeholder?: string; icon?: string; controll ...@@ -16,12 +16,11 @@ Search(options?: { value?: string; placeholder?: string; icon?: string; controll
**Parameters** **Parameters**
| Name | Type | Mandatory| Description | | Name | Type | Mandatory| Description |
| ----------- | ---------------- | ---- | ------------------------------------------------------------ | | ----------- | ---------------------------------------------------- | ---- | ------------------------------------------------------------ |
| value | string | No | Text input in the search text box. | | value | string | No | Text input in the search text box.<br>Since API version 10, this parameter supports two-way binding through [$$](../../quick-start/arkts-two-way-sync.md).|
| placeholder | string | No | Text displayed when there is no input. | | icon | string | No | Path to the search icon. By default, the system search icon is used.<br>For details about the supported image types, see [Image](ts-basic-components-image.md).|
| icon | string | No | Path to the search icon. By default, the system search icon is used. The supported icon formats are .svg, .jpg, and .png.| | controller | SearchController | No | Controller of the **\<Search>** component. |
| controller | SearchController | No | Controller of the **\<Search>** component. |
## Attributes ## Attributes
......
...@@ -47,7 +47,7 @@ struct Page45 { ...@@ -47,7 +47,7 @@ struct Page45 {
grad.addColorStop(0.5, '#ffffff') grad.addColorStop(0.5, '#ffffff')
grad.addColorStop(1.0, '#00ff00') grad.addColorStop(1.0, '#00ff00')
this.context.fillStyle = grad this.context.fillStyle = grad
this.context.fillRect(0, 0, 500, 500) this.context.fillRect(0, 0, 400, 400)
}) })
} }
.width('100%') .width('100%')
......
# Path2D # Path2D
**Path2D** allows you to describe a path through an existing path. This path can be drawn through the **stroke** API of **Canvas**. **Path2D** allows you to describe a path through an existing path. This path can be drawn through the **stroke** or **fill** API of **Canvas**.
> **NOTE** > **NOTE**
> >
> The APIs of this module are supported since API version 8. Updates will be marked with a superscript to indicate their earliest API version. > The APIs of this module are supported since API version 8. Updates will be marked with a superscript to indicate their earliest API version.
......
...@@ -39,7 +39,7 @@ In addition to the [universal attributes](ts-universal-attributes-size.md), the ...@@ -39,7 +39,7 @@ In addition to the [universal attributes](ts-universal-attributes-size.md), the
| strokeDashArray | Array&lt;Length&gt; | [] | Stroke dashes.<br>Since API version 9, this API is supported in ArkTS widgets.| | strokeDashArray | Array&lt;Length&gt; | [] | Stroke dashes.<br>Since API version 9, this API is supported in ArkTS widgets.|
| strokeDashOffset | number \| string | 0 | Offset of the start point for drawing the stroke.<br>Since API version 9, this API is supported in ArkTS widgets.| | strokeDashOffset | number \| string | 0 | Offset of the start point for drawing the stroke.<br>Since API version 9, this API is supported in ArkTS widgets.|
| strokeLineCap | [LineCapStyle](ts-appendix-enums.md#linecapstyle) | LineCapStyle.Butt | Cap style of the stroke.<br>Since API version 9, this API is supported in ArkTS widgets.| | strokeLineCap | [LineCapStyle](ts-appendix-enums.md#linecapstyle) | LineCapStyle.Butt | Cap style of the stroke.<br>Since API version 9, this API is supported in ArkTS widgets.|
| strokeLineJoin | [LineJoinStyle](ts-appendix-enums.md#linejoinstyle) | LineJoinStyle.Miter | Join style of the stroke.<br>Since API version 9, this API is supported in ArkTS widgets.<br>**NOTE**<br>This attribute does not work for the **\<Line>** component, which does not have corners. | | strokeLineJoin | [LineJoinStyle](ts-appendix-enums.md#linejoinstyle) | LineJoinStyle.Miter | Join style of the stroke.<br>Since API version 9, this API is supported in ArkTS widgets.<br>**NOTE**<br>This attribute does not work for the **\<Line>** component, which does not have corners.|
| strokeMiterLimit | number \| string | 4 | Limit value when the sharp angle is drawn as a miter.<br>Since API version 9, this API is supported in ArkTS widgets.<br>**NOTE**<br>This attribute does not take effect because the **\<Line>** component cannot be used to draw a shape with a sharp angle.| | strokeMiterLimit | number \| string | 4 | Limit value when the sharp angle is drawn as a miter.<br>Since API version 9, this API is supported in ArkTS widgets.<br>**NOTE**<br>This attribute does not take effect because the **\<Line>** component cannot be used to draw a shape with a sharp angle.|
| strokeOpacity | number \| string \| [Resource](ts-types.md#resource)| 1 | Stroke opacity.<br>Since API version 9, this API is supported in ArkTS widgets.<br>**NOTE**<br>The value range is [0.0, 1.0]. If the set value is less than 0.0, **0.0** will be used. If the set value is greater than 1.0, **1.0** will be used.| | strokeOpacity | number \| string \| [Resource](ts-types.md#resource)| 1 | Stroke opacity.<br>Since API version 9, this API is supported in ArkTS widgets.<br>**NOTE**<br>The value range is [0.0, 1.0]. If the set value is less than 0.0, **0.0** will be used. If the set value is greater than 1.0, **1.0** will be used.|
| strokeWidth | Length | 1 | Stroke width.<br>Since API version 9, this API is supported in ArkTS widgets.<br>**NOTE**<br>The value cannot be a percentage.| | strokeWidth | Length | 1 | Stroke width.<br>Since API version 9, this API is supported in ArkTS widgets.<br>**NOTE**<br>The value cannot be a percentage.|
......
...@@ -59,6 +59,6 @@ Application resources can be accessed via an absolute or relative path. In this ...@@ -59,6 +59,6 @@ Application resources can be accessed via an absolute or relative path. In this
## Configuration Files ## Configuration Files
If you are developing a widget in the FA model, configure the **config.json** file. If you are developing a widget in the FA model, configure the **config.json** file. For details, see [Configuring the Widget Configuration File in FA Model](../../application-models/widget-development-fa.md#configuring-the-widget-configuration-file).
If you are developing a widget in the stage model, configure **ExtensionAbility** under **extensionAbilities** in the **module.json5** file. If you are developing a widget in the stage model, configure **ExtensionAbility** under **extensionAbilities** in the **module.json5** file. For details, see [Configuring the Widget Configuration File in Stage Model](../../application-models/js-ui-widget-development.md#configuring-the-widget-configuration-files).
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册