DEV Community

xhunmon
xhunmon

Posted on

ZeroOneApp Privacy Agreement Checkbox Component: Design and Implementation

Introduction

In mobile application development, the consent mechanism for privacy policies and user agreements is a crucial环节 to protect user rights and comply with regulatory requirements. The PrivacyCheckbox component in ZeroOneApp provides a fully functional and user-friendly solution for privacy agreement consent. This article will detailedly analyze its design philosophy, implementation details, and best practices.

Core Component Architecture

The PrivacyCheckbox component is located in the project's common/ui module at the path common/ui/src/main/ets/components/PrivacyCheckbox.ets. It is a custom component developed based on the HarmonyOS ArkUI framework. The component mainly consists of the following parts:

File Structure

common/ui/src/main/ets/components/
└── PrivacyCheckbox.ets  # Privacy agreement checkbox component
Enter fullscreen mode Exit fullscreen mode

Core Dependencies

The component depends on utility classes and resource management modules in the project:

  • IntentUtil: Used to handle page navigation after rich text clicks
  • ResColor: Provides unified color resource management
  • loginComponentManager: Defines the data structure and types for privacy text

Component Implementation Details

1. Component Property Definition

The component defines rich configurable parameters to support requirements in different scenarios:

@ComponentV2
export struct PrivacyCheckbox {
  // Agreement content array
  @Param texts: loginComponentManager.PrivacyText[] = [];
  // Whether the agreement is checked
  @Param isCheckboxSelected: boolean = false;
  // Checkbox state change event
  @Event boxChange: (select: boolean) => void;
  // Unselected state animation trigger counter
  @Param unselectAnimalCount: number = 0;
  // Custom rich text click event
  @Event onCustomItemClick?: (item: loginComponentManager.PrivacyText) => void;
  // Animation-related properties
  @Local translateX: number = 0;
}
Enter fullscreen mode Exit fullscreen mode

2. UI Structure Design

The component adopts a Row-nested-Row layout structure to achieve horizontal arrangement of checkbox and text:

build() {
  Row() {
    // Checkbox part
    Row() {
      Checkbox(...) // Checkbox implementation
    }

    // Text content part
    Row() {
      Text() {
        ForEach(this.texts, (item: loginComponentManager.PrivacyText) => {
          // Render different styles according to text type
        })
      }
    }
    .layoutWeight(1)
    .margin({ left: 12 })
  }
  .width('100%')
  .alignItems(VerticalAlign.Top)
  .justifyContent(FlexAlign.Start)
}
Enter fullscreen mode Exit fullscreen mode

3. Core Checkbox Implementation

The checkbox is the core interactive element of the component, implementing state display, animation effects, and event callbacks:

Checkbox({ name: 'privacyCheckbox', group: 'privacyCheckboxGroup' })
  .width(24)
  .height(24)
  .focusable(true)
  .focusOnTouch(true)
  .selectedColor(ResColor.themeLight())
  .margin({ top: 0 })
  .select(this.isCheckboxSelected)
  .unselectedColor(this.translateX === 0 ? Color.Gray : Color.Red)
  .translate({ x: this.translateX })
  .animation({ curve: curves.springMotion() })
  .onChange((value: boolean) => {
    this.boxChange(value);
    log.i(TAG, `agreementChecked: ${value}`);
  })
Enter fullscreen mode Exit fullscreen mode

4. Rich Text Processing Mechanism

The component supports mixed display of plain text and rich text, and provides click interaction for rich text:

ForEach(this.texts, (item: loginComponentManager.PrivacyText) => {
  if (item?.type == loginComponentManager.TextType.PLAIN_TEXT && item?.text) {
    // Plain text rendering
    Span(item?.text)
      .fontColor(ResColor.desc())
      .fontWeight(FontWeight.Regular)
      .fontSize($r('sys.float.ohos_id_text_size_body3'))
  } else if (item?.type == loginComponentManager.TextType.RICH_TEXT && item?.text) {
    // Rich text rendering
    Span(item?.text)
      .fontColor(ResColor.themeLight())
      .fontWeight(FontWeight.Medium)
      .fontSize($r('sys.float.ohos_id_text_size_body3'))
      .focusable(true)
      .focusOnTouch(true)
      .onClick(() => {
        // Rich text click handling
      })
  }
})
Enter fullscreen mode Exit fullscreen mode

5. Unselected State Animation

To enhance user experience, the component implements a shaking animation in the unselected state to remind users to check the agreement:

@Monitor('unselectAnimalCount')
checkAgreeAnimate() {
  if (this.isCheckboxSelected) {
    return;
  }
  let count = 10;
  let t = 0;
  const id = setInterval(() => {
    count -= 1;
    if (count === 0) {
      clearInterval(id);
      this.translateX = 1;
      return;
    }
    t += 30;
    this.translateX = 30 * Math.sin(t);
  }, 100)
}
Enter fullscreen mode Exit fullscreen mode

Component Usage Methods

Basic Usage

PrivacyCheckbox({
  texts: [
    { type: TextType.PLAIN_TEXT, text: 'I have read and agree to the' },
    { type: TextType.RICH_TEXT, text: 'User Agreement', tag: 'user_agreement' },
    { type: TextType.PLAIN_TEXT, text: 'and' },
    { type: TextType.RICH_TEXT, text: 'Privacy Policy', tag: 'privacy_policy' }
  ],
  isCheckboxSelected: this.isAgree,
  onCustomItemClick: (item) => {
    // Custom click handling
  },
  boxChange: (isSelected) => {
    this.isAgree = isSelected;
    // Update button status and other operations
  }
})
Enter fullscreen mode Exit fullscreen mode

Text Data Structure

The loginComponentManager.PrivacyText interface is defined as follows:

interface PrivacyText {
  type: TextType; // PLAIN_TEXT or RICH_TEXT
  text: string;  // Display text
  tag?: string;  // Identifier for navigation
}
Enter fullscreen mode Exit fullscreen mode

Advanced Features and Best Practices

1. Custom Rich Text Click Handling

The onCustomItemClick event allows custom rich text click behavior, such as navigating to in-app pages instead of external links:

onCustomItemClick: (item) => {
  if (item.tag === 'user_agreement') {
    router.pushUrl({
      url: 'pages/UserAgreementPage'
    });
  } else if (item.tag === 'privacy_policy') {
    router.pushUrl({
      url: 'pages/PrivacyPolicyPage'
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Form Validation Integration

It is recommended to use the component with form validation to ensure users check the agreement before proceeding:

Button('Register')
  .enabled(this.isAgree)
  .onClick(() => {
    // Execute registration logic
  })
Enter fullscreen mode Exit fullscreen mode

3. Animation Trigger Timing

Trigger the unselected state animation by modifying the unselectAnimalCount property, typically used when users attempt to submit without checking the agreement:

if (!this.isAgree) {
  this.unselectCount += 1; // Trigger animation
  promptAction.showToast({
    message: 'Please read and agree to the privacy agreement'
  });
}
Enter fullscreen mode Exit fullscreen mode

Summary and Outlook

The PrivacyCheckbox component provides a compliant and user-friendly privacy agreement consent solution through elegant design and comprehensive functionality. Its main advantages include:

  1. Clear Structure: Adopts modular design with distinct code logic
  2. User-Friendly Interaction: Provides intuitive visual feedback and animation effects
  3. Highly Customizable: Supports custom text content, click behavior, and styles
  4. Easy Integration: Simple API design for quick project integration

Future enhancement considerations:

  • Support for multilingual text display
  • Adding agreement content collapse/expand functionality
  • Implementing agreement version control mechanism
  • Supporting dark/light mode adaptation

Reference Resources

Top comments (0)