Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

HarmonyOS Development Guide: ArkUI Component Encapsulation Patterns

Tech 1

Overview

During application development with HarmonyOS, encapsulating ArkUI components becomes essential for business reuse. Based on practical migration scenarios, three typical component encapsulation patterns have emerged:

  • Public Component Encapsulation: Wrapping system components to provide unified styling according to UX guidelines, such as login buttons or dialog buttons for shared component libraries.
  • Dialog Component Encapsulation: Internal encapsulation of dialog content with controllers, allowing callers to control visibility through state variables.
  • Component Factory Pattern: Factory classes that expose all components, enabling callers to retrieve specific components via parameters.

This guide details these scenarios and their implementation approaches.

Public Component Encapsulation

Scenario Description

Different business scenarios often require ArkUI components with identical functionality and styling. For instance, a login page submit button and a shopping page checkout button might share the same visual design. The common approach involves extracting shared logic and encapsulating it as a custom component in a shared library.

Taking the Button component as an example, when multiple business scenarios need buttons with consistent styling, the generic logic gets wrapped into a custom component called PrimaryAction. This component defines common fontSize and fontColor properties. When integrating this component into a shared library as an extended Button, developers must enumerate all Button attributes to enable chain-style property access while maintaining Button's native capabilities.

@Component
struct PrimaryAction {
  @Prop label: string = '';
  @Prop active: boolean = true;
  // ... enumerate all Button-specific properties
  
  build() {
    Button(this.label)
      .fontSize(14)
      .fontColor('#FFFFFF')
      .active(this.active)
      .backgroundColor('#007AFF')
      // ... assign other Button-specific properties
  }
}

When using the PrimaryAction component, modifications to display content and interaction effects require passing parameters:

@Component
struct MainScreen {
  build() {
    PrimaryAction({ label: 'Submit', active: true })
  }
}

Limitations of This Approach

The parameter-based encapsulation method presents several challenges:

  1. Inconsistent API with System Components: System components use chain-style property assignment, while this approach requires property configuration through parameters.
  2. Excessive Parameters: Using all system component properties necessitates enumerating each as a paraemter in the custom component, creating verbose usage.
  3. Maintenance Overhead: When system component properties change, custom components require corresponding updates.

Implementation Solution

To address these limitations, ArkTS provides the attributeModifier property for each system component. This approach separates property configuration into a separate AttributeModifier interface implementation class, enabling property extension through custom classes.

Solution: Exposing AttributeModifier via Shared Components

Using the system Button component as an example, the implementation steps are:

  1. Create a reusable custom component in the shared library that accepts external attributeModifier parameters:
@Component
export struct ActionButton {
  @Prop label: string = '';
  @Prop modifier: AttributeModifier<ButtonAttribute> | null = null;
  
  build() {
    Button(this.label)
      .attributeModifier(this.modifier)
  }
}
  1. Callers implement the AttributeModifier interface to customize properties:
class CustomButtonModifier implements AttributeModifier<ButtonAttribute> {
  applyNormalAttribute(instance: ButtonAttribute): void {
    instance
      .fontSize(16)
      .fontColor('#FFFFFF')
      .backgroundColor('#34C759')
      .borderRadius(8)
  }
  
  applyPressedAttribute(instance: ButtonAttribute): void {
    instance.backgroundColor('#248A3D')
  }
}

@Component
struct UserInterface {
  private buttonModifier = new CustomButtonModifier();
  
  build() {
    ActionButton({
      label: 'Confirm',
      modifier: this.buttonModifier
    })
  }
}

This approach provides several advantages:

  • Consistent API: Maintains chain-style property assignment similar to native system components
  • Flexible Customization: Callers can modify properties without modifying the encapsulated component
  • Separation of Concerns: Component logic and styling remain independent
  • Better Maintainability: Property changes in system components don't require updates to custom components

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.