Mobile Development 13 min read

HarmonyOS Pen Kit: Integrating Stylus Squeeze, Double-Tap & Color Picker

This tutorial demonstrates complete integration of HarmonyOS Pen Kit's stylusInteraction module for squeeze and double-tap events, and imageFeaturePicker for color picking, covering callback reference management, lifecycle-aware registration with try-catch protection, event object fields, and UI feedback patterns including status visualization, counters, and toast notifications.

51CTO HarmonyOS Developer Community
51CTO HarmonyOS Developer Community
51CTO HarmonyOS Developer Community
HarmonyOS Pen Kit: Integrating Stylus Squeeze, Double-Tap & Color Picker

Pen Kit Basics

HarmonyOS Pen Kit ( @kit.Penkit) comprises two independent core modules:

stylusInteraction : Handles stylus-specific interaction events such as squeeze and double-tap.

imageFeaturePicker : Provides screen color picking at specified coordinates.

Both modules can be used separately or combined to build complete stylus-assisted tools.

HarmonyOS Pen Kit overview showing M-Pencil hover squeeze and double-tap events with color picker
HarmonyOS Pen Kit overview showing M-Pencil hover squeeze and double-tap events with color picker

Supported Event Types

The stylusInteraction module currently exposes two events, both requiring the pen to be in a hover (hover) state — i.e., the tip is near but not pressing the screen:

squeeze : Triggered when the user lightly pinches the pen barrel button while hovering. Callback receives a SqueezeEvent object.

doubleTap : Triggered when the user double-taps the pen barrel while hovering. Callback receives a DoubleTapEvent object.

Note: Ordinary tap state does not fire these events; hover is mandatory.
Pen Kit module architecture showing stylusInteraction for events and imageFeaturePicker for color picking
Pen Kit module architecture showing stylusInteraction for events and imageFeaturePicker for color picking

Callback Reference Management

Why Save Callback References

Registering listeners with stylusInteraction.on() and removing them with stylusInteraction.off() requires passing the exact same function reference . If anonymous arrow functions are created inline during registration and again during removal, the references differ and cancellation silently fails, causing memory leaks or duplicate triggers.

Correct pattern: declare callbacks as component properties and initialize them in aboutToAppear:

private squeezeCallback: (event: stylusInteraction.SqueezeEvent) => void = () => {};
private doubleTapCallback: (event: stylusInteraction.DoubleTapEvent) => void = () => {};

// In aboutToAppear:
this.squeezeCallback = (event) => { /* handle squeeze */ };
this.doubleTapCallback = (event) => { /* handle doubleTap */ };
Tip: Writing () => {} directly inside on() and another () => {} inside off() creates two distinct references; removal will fail silently.
Callback reference management comparison: wrong way with anonymous functions vs correct way with saved references
Callback reference management comparison: wrong way with anonymous functions vs correct way with saved references

Initialization Timing

The article recommends a layered lifecycle approach: aboutToAppear: Initialize callback references (assign to member properties). onPageShow: Register event listeners (start listening when page becomes visible). onPageHide: Cancel event listeners (stop listening when page hidden). aboutToDisappear: Fallback cancellation to prevent leaks on destruction.

This design binds listening strictly to page visibility, so background pages consume no resources, and returning to the page seamlessly restores monitoring.

Event Registration and Cancellation

Registering Listeners

Each registration must be wrapped in an independent try-catch block because unsupported devices or missing permissions throw exceptions that would crash the app if uncaught. Separate try-catch ensures one failure does not block the other.

try {
  stylusInteraction.on('squeeze', this.squeezeCallback);
} catch (e) { /* handle */ }
try {
  stylusInteraction.on('doubleTap', this.doubleTapCallback);
} catch (e) { /* handle */ }

On success, set an isListening flag to true for state tracking.

Cancelling Listeners

Before calling off(), check the isListening flag to avoid meaningless cancellations. After removal, reset the flag to false.

if (this.isListening) {
  stylusInteraction.off('squeeze', this.squeezeCallback);
  stylusInteraction.off('doubleTap', this.doubleTapCallback);
  this.isListening = false;
}

Event Object Fields

Both SqueezeEvent and DoubleTapEvent share a core field: timestamp (number): Event occurrence timestamp in milliseconds. Useful for calculating intervals between operations or displaying the last action time in the UI.

Color Picker Integration

Working Principle

imageFeaturePicker.pickForResult(x, y)

accepts screen coordinates, shows a system color-picker overlay, and returns a Promise resolving to a PickedColorInfo object. The key is obtaining correct screen coordinates: a button's onClick callback provides a ClickEvent with displayX and displayY representing absolute screen coordinates, which match pickForResult 's parameter requirements.

.onClick((event) => {
  imageFeaturePicker.pickForResult(event.displayX, event.displayY)
    .then((colorInfo: imageFeaturePicker.PickedColorInfo) => {
      // colorInfo.color holds the picked color (e.g., #FF5733)
    })
    .catch((err: BusinessError) => {
      // Handle user cancellation or device unsupported
    });
})

Return Value

PickedColorInfo

contains: color: Picked color value, typically a hex string such as #FF5733.

Tip: When the user taps Cancel, the Promise rejects with a specific error code in err.code . Distinguish "user cancelled" from genuine errors in the catch block to avoid misreporting cancellation as failure.

UI State Feedback Design

Listening Status Visualization

A status line at the page bottom shows real-time monitoring state with color coding:

Green ( #00C853): Listening active; stylus events can be captured.

Red ( #FF5252): Listening stopped; typical when page moves to background.

This visual cue is invaluable during debugging to confirm listener health.

Event Counters

Two independent counters track cumulative triggers: squeezeCount: Total squeeze events. doubleTapCount: Total double-tap events.

Each event updates lastEventInfo with the latest event type and timestamp. A "Reset Counters" button clears both counts for clean multi-round testing.

Toast Instant Feedback

On every captured stylus event, a short toast appears via getUIContext().getPromptAction().showToast(). Key details:

Uses getUIContext() (recommended in V2 components) instead of global promptAction.

Duration set to 1500 ms to avoid blocking subsequent interactions.

Message includes current count so users instantly see which occurrence this is.

Complete Lifecycle Flow

The page lifecycle management follows this sequence:

Page creation ( aboutToAppear ) : Initialize callback references; no listeners registered yet.

Page show ( onPageShow ) : Register squeeze and doubleTap listeners; set isListening = true.

User operates stylus : Callbacks fire, counters increment, UI updates, toast shows.

Page hide ( onPageHide ) : Cancel both listeners; set isListening = false; stop resource consumption.

Re-enter page ( onPageShow again) : Re-register listeners seamlessly.

Page destroy ( aboutToDisappear ) : Fallback cancellation to prevent leaks.

Pen Kit event listening complete lifecycle from initialization to destruction with page re-entry loop
Pen Kit event listening complete lifecycle from initialization to destruction with page re-entry loop

Core advantages:

Listeners strictly bound to page visibility — zero background resource usage.

Fixed callback references guarantee reliable register/unregister pairing.

Independent try-catch per registration — single-point failure does not affect the whole.

Summary

HarmonyOS Pen Kit usage boils down to three pillars:

Save callback references in aboutToAppear.

Pair registration and cancellation within the page lifecycle.

Guard every registration with try-catch.

Mastering these enables safe, stable integration of stylus squeeze, double-tap, and advanced features like the color picker, delivering a smoother stylus experience.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

HarmonyOSlifecyclecolor pickerPen Kitcallback managementimageFeaturePickerM-PencilstylusInteraction
51CTO HarmonyOS Developer Community
Written by

51CTO HarmonyOS Developer Community

The HarmonyOS Developer Community is a learning-oriented community for developers to learn, communicate, ask questions, and share.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.