diff --git a/README.en.md b/README.en.md index a207e899e041b565c587c9676023409c3bdb467d..894d6d8afa71323707a6ab50ded444ba5ed101ae 100644 --- a/README.en.md +++ b/README.en.md @@ -6,9 +6,9 @@ This sample describes three typical scenarios of ArkUI component encapsulation: ### Preview -| Home page | Common component | Dialog component | Component factory | -|-------------------------------------------------------|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| -| | | | | +| Home page | Attribute Style | Common component | Dialog component | Component factory | +|-------------------------------------------------------|-------------------------------------------------------|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| +| | | | | | ### How to Use @@ -27,6 +27,7 @@ On the home page, tap the corresponding button to access the page for common com │ │ ├──GetResourceString.ets // Resource-to-string conversion function │ │ └──PopViewUtils.ets // Custom pop-up window │ ├──pages +│ │ ├──AttributeStylePage.ets // Attribute Style │ │ ├──CommonComponent.ets // Common component │ │ ├──ComponentFactory.ets // Component factory │ │ ├──DialogComponent.ets // Dialog component diff --git a/README.md b/README.md index d2f784a75dafd1a3f2b168a5bb9c5642204a5cbb..04c217c9648c70afe16562b8d466de6a0c1527b5 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,17 @@ ### 介绍 -本示例通过系统组件的attributeModifier属性、PromptAction对象、wrapBuilder函数,实现公共组件的封装、弹窗组件的封装、全局@Builder。帮助开发者掌握如何优化重复性的组件、布局,使代码简洁,易维护。 +本示例通过系统组件的attributeModifier属性、PromptAction对象、wrapBuilder函数等,实现组件公共样式封装、自定义组件的封装、弹窗组件的封装、全局@Builder。帮助开发者掌握如何优化重复性的组件、布局,使代码简洁,易维护。 ### 效果图预览 -| 首页 | 公用组件封装场景 | 弹窗组件封装场景 | 组件工厂类封装场景 | -|----------------------------------------|-----------------------------------------|-----------------------------------------|------------------------------------------| -| ![image](screenshots/device/index.png) | ![image](screenshots/device/common.png) | ![image](screenshots/device/dialog.png) | ![image](screenshots/device/factory.png) | +| 首页 | 组件公共样式封装 | 自定义组件封装场景 | 组件工厂类封装场景 | 弹窗组件封装场景 | +|----------------------------------------------------|----------------------------------------|-----------------------------------------|------------------------------------------|-----------------------------------------| +| | | ![image](screenshots/device/common.png) | ![image](screenshots/device/factory.png) | ![image](screenshots/device/dialog.png) | ##### 使用说明 -进入首面,会看到公用组件封装、弹窗组件封装和组件工厂类封装三个按钮,点击按钮会进入对应的封装场景实现的页面,查看效果。 +进入首面,会看到各封装场景的按钮,点击按钮会进入对应的封装场景实现的页面,查看效果。 ### 工程目录 @@ -27,7 +27,8 @@ │ │ ├──GetResourceString.ets // Resource转换String函数 │ │ └──PopViewUtils.ets // 自定义弹窗类 │ ├──pages -│ │ ├──CommonComponent.ets // 公共组件封装 +│ │ ├──AttributeStylePage.ets // 组件公共样式封装 +│ │ ├──CommonComponent.ets // 自定义组件封装 │ │ ├──ComponentFactory.ets // 组件工厂类封装 │ │ ├──DialogComponent.ets // 弹窗组件封装 │ │ └──Index.ets // 首页 @@ -39,7 +40,7 @@ ### 具体实现 -1. 公用组件封装:系统组件提供了attributeModifier属性方法,通过自定义Class类实现AttributeModifier接口对系统组件属性进行扩展。 +1. 自定义组件封装:系统组件提供了attributeModifier属性方法,通过自定义Class类实现AttributeModifier接口对系统组件属性进行扩展。 2. 弹窗组件封装:通过使用UIContext中获取到的PromptAction对象来实现自定义弹窗封装,使用PromptAction对象中openCustomDialog接口打开弹窗,closeCustomDialog接口关闭弹窗。 diff --git a/entry/src/main/ets/model/AttributeModifier.ets b/entry/src/main/ets/model/AttributeModifier.ets deleted file mode 100644 index 51c59c911e77b587ae02c188026f02210c289d0e..0000000000000000000000000000000000000000 --- a/entry/src/main/ets/model/AttributeModifier.ets +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2024 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { MyButton } from '../pages/CommonComponent'; - -// [Start image_modifier] -// The AttributeModifier interface implementation class for the Image component. -export class ImageModifier implements AttributeModifier { - private imageWidth: Length = 0; - private imageHeight: Length = 0; - - constructor(width: Length, height: Length) { - this.imageWidth = width; - this.imageHeight = height; - } - - width(width: Length) { - this.imageWidth = width; - return this; - } - - height(height: Length) { - this.imageHeight = height; - return this; - } - - applyNormalAttribute(instance: ImageAttribute): void { - instance.width(this.imageWidth); - instance.height(this.imageHeight); - instance.borderRadius($r('app.float.padding_l')) - } -} - -// Text component's AttributeModifier interface implementation class. -export class TextModifier implements AttributeModifier { - constructor() { - } - - applyNormalAttribute(instance: TextAttribute): void { - instance.fontSize($r('app.float.font_size_l')); - } -} -// [End image_modifier] - -// [Start my_button_modifier] -// src/main/ets/model/AttributeModifier.ets -// The client implements the AttributeModifier interface with custom class. -class MyButtonModifier implements AttributeModifier { - // Define private properties specific to the Button component - private stateEffectValue: boolean = false; - private buttonType: ButtonType = ButtonType.Normal; - - constructor() { - } - // The system provides styling methods for components in their normal state, along with additional methods for hover and other states. - applyNormalAttribute(instance: ButtonAttribute): void { - instance.stateEffect(this.stateEffectValue); - instance.type(this.buttonType); - } - - stateEffect(enable: boolean): MyButtonModifier { - this.stateEffectValue = enable - return this; - } - // Custom attribute names align with system component property names to ensure consistency during chained calls. - type(buttonType: ButtonType): MyButtonModifier { - this.buttonType = buttonType; - return this; - } -} - -//The user utilizes the provider's public component MyButton. -@Component -struct Index { - capsuleButtonModifier: MyButtonModifier = new MyButtonModifier().stateEffect(true).type(ButtonType.Capsule) - circleButtonModifier: MyButtonModifier = new MyButtonModifier().stateEffect(true).type(ButtonType.Circle) - build() { - Row() { - MyButton({ modifier: this.capsuleButtonModifier, text: 'Capsule Button' }) - .margin({ right: 20 }) - MyButton({ modifier: this.circleButtonModifier, text: 'Circle Button' }) - } - .justifyContent(FlexAlign.Center) - .width('100%') - .height('100%') - } -} -// [End my_button_modifier] \ No newline at end of file diff --git a/entry/src/main/ets/model/PopViewUtils.ets b/entry/src/main/ets/model/PopViewUtils.ets index a69602761e6ba44929034efd799a0f750ebd1159..f67b140bcb099fd4ec7dcfcbd3fa26cdccda7037 100644 --- a/entry/src/main/ets/model/PopViewUtils.ets +++ b/entry/src/main/ets/model/PopViewUtils.ets @@ -71,7 +71,7 @@ export class PopViewUtils { return model.popType === type; }) let info = sameTypeList[sameTypeList.length - 1]; - if (info.com) { + if (info && info.com) { PopViewUtils.shareInstance().infoList = PopViewUtils.shareInstance().infoList.filter((model) => { return model.com !== info.com; }) diff --git a/entry/src/main/ets/pages/AttributeStylePage.ets b/entry/src/main/ets/pages/AttributeStylePage.ets new file mode 100644 index 0000000000000000000000000000000000000000..4399d5d53f789a6f967eed6a9b462c7194ccacfe --- /dev/null +++ b/entry/src/main/ets/pages/AttributeStylePage.ets @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { getResourceString } from "../model/GetResourceString"; + +// [Start my_button_modifier1] +// src/main/ets/pages/CommonComponent.ets +// The provider creates a custom Class that implements the system's AttributeModifier interface. +export class MyButtonModifier implements AttributeModifier { + private buttonType: ButtonType = ButtonType.Normal; + + constructor() { + } + + applyNormalAttribute(instance: ButtonAttribute): void { + instance.type(this.buttonType); + instance.width(200); + instance.height(50); + instance.fontSize(20); + instance.fontColor('#0A59F7') + instance.backgroundColor('#0D000000') + } + + applyPressedAttribute(instance: ButtonAttribute): void { + instance.fontColor('#0A59F7') + instance.backgroundColor('#26000000') + } + + type(type: ButtonType): MyButtonModifier { + this.buttonType = type; + return this; + } +} +// [End my_button_modifier1] + +@Builder +export function AttributeStylePageBuilder() { + AttributeStylePage() +} + +// [Start use_modifier] +@Entry +@Component +struct AttributeStylePage { + modifier = new MyButtonModifier() + .type(ButtonType.Capsule) + + build() { + NavDestination() { + Column() { + Button('Capsule Button') + .attributeModifier(this.modifier) + } + .margin({ top: $r('app.float.margin_top') }) + .justifyContent(FlexAlign.Start) + .alignItems(HorizontalAlign.Center) + .width('100%') + .height('100%') + } + .title(getResourceString($r('app.string.common_style_extract'), this)) + } +} +// [End use_modifier] \ No newline at end of file diff --git a/entry/src/main/ets/pages/CommonComponent.ets b/entry/src/main/ets/pages/CommonComponent.ets index cf194370d4fd3c33ff8e3816595b03610f894a7f..2d0b16cd9e3d02c01afa4f78b2a75d378372b959 100644 --- a/entry/src/main/ets/pages/CommonComponent.ets +++ b/entry/src/main/ets/pages/CommonComponent.ets @@ -12,10 +12,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { CommonConstants } from '../common/CommonConstants'; -import { ImageModifier, TextModifier } from '../model/AttributeModifier'; + import { getResourceString } from '../model/GetResourceString'; -import { CustomImageText } from '../view/CustomImageText'; +import { CustomImageModifier, CustomImageText } from '../view/CustomImageText'; @Builder export function CommonComponentBuilder() { @@ -26,17 +25,18 @@ export function CommonComponentBuilder() { // [Start common_component1] @Component struct CommonComponent { - imageAttribute: ImageModifier = new ImageModifier(330, 330); - textAttribute: TextModifier = new TextModifier(); + imageAttribute: CustomImageModifier = new CustomImageModifier(330, 330); build() { NavDestination() { Column() { CustomImageText({ imageAttribute: this.imageAttribute, - textAttribute: this.textAttribute, imageSrc: $r('app.media.image'), - text: 'Scenery' + text: 'Scenery', + onClickEvent: () => { + this.getUIContext().getPromptAction().showToast({ message: 'Clicked' }) + } }) } .margin({ top: $r('app.float.margin_top') }) @@ -49,72 +49,3 @@ struct CommonComponent { } } // [End common_component1] - -// [Start my_button1] -//Provider customizes and exports components -@Component -export struct MyButton { - @Prop text: string = ''; - // Accept externally passed AttributeModifier class instances - @Prop modifier: AttributeModifier | null = null; - - build() { - // AttributeModifier does not support properties with CustomBuilder or Lambda expressions as parameters, and it does - // not support events or gestures. Here, the text can only be passed separately through parameters. - Button(this.text) - // Bind the input AttributeModifier class instance to the system component. - .attributeModifier(this.modifier) - .fontSize(20) - .width(200) - .height(50) - } -} -// [End my_button1] - -// [Start my_button_modifier1] -// src/main/ets/pages/CommonComponent.ets -// The provider creates a custom Class that implements the system's AttributeModifier interface. -export class MyButtonModifier implements AttributeModifier { - private buttonType: ButtonType = ButtonType.Normal; - private stateEffectValue: boolean = false; - - constructor() { - } - - applyNormalAttribute(instance: ButtonAttribute): void { - instance.stateEffect(this.stateEffectValue); - instance.type(this.buttonType); - instance.width(200); - instance.height(50); - instance.fontSize(20) - } - - stateEffect(enable: boolean): MyButtonModifier { - this.stateEffectValue = enable; - return this; - } - - type(type: ButtonType): MyButtonModifier { - this.buttonType = type; - return this; - } -} -// [End my_button_modifier1] -// [Start index] -// src/main/ets/pages/CommonComponent.ets -@Component -struct Index { - modifier = new MyButtonModifier() - .stateEffect(true) - .type(ButtonType.Capsule) - - build() { - Row() { - Button('Capsule Button') - .attributeModifier(this.modifier) - } - .width('100%') - .height('100%') - } -} -// [End index] \ No newline at end of file diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index bac07162d247bcfcc8cef073ef17757d7dca4582..783c6fdbf5c14a9aee8a9b8dbb2d137327dfe291 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -22,21 +22,26 @@ struct Index { build() { Navigation(this.pageStack) { Column({ space: CommonConstants.BUTTON_SPACING }) { - Button($r('app.string.common')) + Button($r('app.string.common_style_extract')) .width(CommonConstants.ONE_HUNDRED_PERCENT) .onClick(() => { - this.pageStack.pushPathByName('CommonComponent', null, true); + this.pageStack.pushPathByName('AttributeStylePage', null, true); }) - Button($r('app.string.dialog')) + Button($r('app.string.common')) .width(CommonConstants.ONE_HUNDRED_PERCENT) .onClick(() => { - this.pageStack.pushPathByName('DialogComponent', null, true); + this.pageStack.pushPathByName('CommonComponent', null, true); }) Button($r('app.string.factory')) .width(CommonConstants.ONE_HUNDRED_PERCENT) .onClick(() => { this.pageStack.pushPathByName('ComponentFactory', null, true); }) + Button($r('app.string.dialog')) + .width(CommonConstants.ONE_HUNDRED_PERCENT) + .onClick(() => { + this.pageStack.pushPathByName('DialogComponent', null, true); + }) } .padding($r('app.float.padding')) .justifyContent(FlexAlign.End) diff --git a/entry/src/main/ets/view/CustomImageText.ets b/entry/src/main/ets/view/CustomImageText.ets index 7e6c4000e4cfb4dcacf4d7709fbf4eaf96a8d07b..c80fafb1d1c929b50c0d47e7937638f0e29885bb 100644 --- a/entry/src/main/ets/view/CustomImageText.ets +++ b/entry/src/main/ets/view/CustomImageText.ets @@ -12,14 +12,56 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { CommonConstants } from '../common/CommonConstants'; + + +// [Start image_modifier] +// The AttributeModifier interface implementation class for the Image component. +export class CustomImageModifier implements AttributeModifier { + private imageWidth: Length = 0; + private imageHeight: Length = 0; + + constructor(width: Length, height: Length) { + this.imageWidth = width; + this.imageHeight = height; + } + + width(width: Length) { + this.imageWidth = width; + return this; + } + + height(height: Length) { + this.imageHeight = height; + return this; + } + + applyNormalAttribute(instance: ImageAttribute): void { + instance.width(this.imageWidth); + instance.height(this.imageHeight); + instance.borderRadius($r('app.float.border_radius')) + + } +} + +// Text component's AttributeModifier interface implementation class. +export class CustomTextModifier implements AttributeModifier { + constructor() { + } + + applyNormalAttribute(instance: TextAttribute): void { + instance.fontSize($r('app.float.font_size_l')); + } +} +// [End image_modifier] + // [Start custom_image_text] @Component export struct CustomImageText { - @Prop imageAttribute: AttributeModifier; - @Prop textAttribute: AttributeModifier; + @Prop imageAttribute: AttributeModifier = new CustomImageModifier(100, 100); + @Prop textAttribute: AttributeModifier = new CustomTextModifier(); @Prop imageSrc: PixelMap | ResourceStr | DrawableDescriptor; @Prop text: string; + onClickEvent?: () => void; build() { Column({ space: 12 }) { @@ -27,7 +69,11 @@ export struct CustomImageText { .attributeModifier(this.imageAttribute) Text('Scenery') .attributeModifier(this.textAttribute) - } + }.onClick(() => { + if (this.onClickEvent !== undefined) { + this.onClickEvent(); + } + }) } } // [End custom_image_text] diff --git a/entry/src/main/ets/view/FactoryMap.ets b/entry/src/main/ets/view/FactoryMap.ets index 6a992efc5edeabfc21473ac6fc8604d2db75c141..930727df162f56813ce198d38538ed3438494f44 100644 --- a/entry/src/main/ets/view/FactoryMap.ets +++ b/entry/src/main/ets/view/FactoryMap.ets @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { CommonConstants } from '../common/CommonConstants'; + // [Start my_radio] @Builder function myRadio() { diff --git a/entry/src/main/resources/base/element/float.json b/entry/src/main/resources/base/element/float.json index aef0b511aed93d3e5ac4505555dd28d0572d1e6b..5658c976982ba13ac2a6024d3a235cc858c6c20b 100644 --- a/entry/src/main/resources/base/element/float.json +++ b/entry/src/main/resources/base/element/float.json @@ -31,6 +31,10 @@ { "name": "margin_top", "value": "56vp" + }, + { + "name": "page_text_font_size", + "value": "50fp" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json index 86e9f1d40676f06de03ab90a2ff75a2a51e1edb3..5ff0bbb1fc12fbdaa37bc8be4a0c08dca60b5745 100644 --- a/entry/src/main/resources/base/element/string.json +++ b/entry/src/main/resources/base/element/string.json @@ -18,7 +18,7 @@ }, { "name": "common", - "value": "Common component encapsulation" + "value": "Custom component encapsulation" }, { "name": "dialog", @@ -53,8 +53,8 @@ "value": "CheckBox" }, { - "name": "click", - "value": "CheckBox" + "name": "common_style_extract", + "value": "Common style encapsulation" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index 1898d94f58d6128ab712be2c68acc7c98e9ab9ce..55c3f007f87b7ce5206d325f968cc56f2f79441f 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -2,4 +2,4 @@ "src": [ "pages/Index" ] -} +} \ No newline at end of file diff --git a/entry/src/main/resources/base/profile/route_map.json b/entry/src/main/resources/base/profile/route_map.json index 7e8f26a009aeddb1fa857523ac071963af8fa6cd..0e5db57923037e86ea05bcb22438150419d67568 100644 --- a/entry/src/main/resources/base/profile/route_map.json +++ b/entry/src/main/resources/base/profile/route_map.json @@ -8,6 +8,14 @@ "description" : "this is CommonComponent" } }, + { + "name": "AttributeStylePage", + "pageSourceFile": "src/main/ets/pages/AttributeStylePage.ets", + "buildFunction": "AttributeStylePageBuilder", + "data": { + "description" : "this is AttributeStylePage" + } + }, { "name": "DialogComponent", "pageSourceFile": "src/main/ets/pages/DialogComponent.ets", diff --git a/entry/src/main/resources/en_US/element/string.json b/entry/src/main/resources/en_US/element/string.json index 8d61c21208448bd1a4ac7084d90329b01e4d3e94..5ff0bbb1fc12fbdaa37bc8be4a0c08dca60b5745 100644 --- a/entry/src/main/resources/en_US/element/string.json +++ b/entry/src/main/resources/en_US/element/string.json @@ -18,7 +18,7 @@ }, { "name": "common", - "value": "Common component encapsulation" + "value": "Custom component encapsulation" }, { "name": "dialog", @@ -51,6 +51,10 @@ { "name": "checkbox", "value": "CheckBox" + }, + { + "name": "common_style_extract", + "value": "Common style encapsulation" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json index 82248c13665c88dd60a4b283762bbed0608e7821..0314928c3c885adb55f1d1c7cbce1c94f13e775e 100644 --- a/entry/src/main/resources/zh_CN/element/string.json +++ b/entry/src/main/resources/zh_CN/element/string.json @@ -18,7 +18,7 @@ }, { "name": "common", - "value": "公用组件封装" + "value": "自定义组件封装" }, { "name": "dialog", @@ -51,6 +51,10 @@ { "name": "checkbox", "value": "复选框" + }, + { + "name": "common_style_extract", + "value": "组件公共样式封装" } ] } \ No newline at end of file diff --git a/screenshots/device/index.en.png b/screenshots/device/index.en.png index 40901d1ec68094ff6862a3df97f630f3511ef0f6..0b31811aecf4854ed564ada8d09856587ee4918a 100644 Binary files a/screenshots/device/index.en.png and b/screenshots/device/index.en.png differ diff --git a/screenshots/device/index.png b/screenshots/device/index.png index 06a5ab4189fb023c2335f00c0f7b3f6455cc654a..95f1b0aa4974092397b9934d1e4457728f0560ec 100644 Binary files a/screenshots/device/index.png and b/screenshots/device/index.png differ diff --git a/screenshots/device/style.en.png b/screenshots/device/style.en.png new file mode 100644 index 0000000000000000000000000000000000000000..f2fdcb69d0d726d998a761c55e193718adc970f9 Binary files /dev/null and b/screenshots/device/style.en.png differ diff --git a/screenshots/device/style.png b/screenshots/device/style.png new file mode 100644 index 0000000000000000000000000000000000000000..dc798d7c21eb90724216b383382ad2770240d741 Binary files /dev/null and b/screenshots/device/style.png differ