
Touchscreen devices are the most common input devices, supported by nearly all handheld terminal devices for user touch operations. Touch events are also one of the most frequently handled event types by application developers.
It should be noted that for touch-like operations from non-touchscreen devices, for example, mouse button clicks and swipes, the system converts them into touch events for consistency. In this case, applications may receive both touch events and mouse events. To distinguish between genuine touchscreen events and converted events, check the SourceType property in the event object.
Touch events can be received on a component through the universal event onTouch, and the callback response follows the hit test rules.
The reporting frequency of touch events is downsampled by the system to match the screen refresh rate. For details, see Resampling and Historical Points.
On devices that support multi-touch, simultaneous finger operations generate multiple touch points. All touch points are accessible through the touches property of the TouchEvent object, while changedTouches indicates which touch points triggered the current event.
For more details about events, see the basic class BaseEvent of TouchEvent.
Refer to Event Bubbling to understand the bubbling mechanism. Below is an example where touch events are blocked from reaching the parent component when the child component is clicked.
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG = '[Sample_PreventBubbling]';
const DOMAIN = 0xF811;
const BUNDLE = 'MyApp_PreventBubbling';
@Entry
@ComponentV2
struct PreventBubbling {
build() {
RelativeContainer() {
Column() { // Parent component
// Replace $r('app.string.preventEvent') with the actual resource file. In this example, the value in the resource file is "If you tap me, the parent will not receive touch events."
Text($r('app.string.preventEvent'))
.fontColor(Color.White)
.height('40%')
.width('80%')
.backgroundColor(Color.Brown)
.alignSelf(ItemAlign.Center)
.padding(10)
.margin(20)
.onTouch((event: TouchEvent) => {
event.stopPropagation(); // After the child component receives the touch event first, prevent it from bubbling to the parent component.
})
}
.justifyContent(FlexAlign.End)
.backgroundColor(Color.Green)
.height('100%')
.width('100%')
.onTouch((event: TouchEvent) => {
hilog.info(DOMAIN, TAG, BUNDLE + 'touch event received on parent');
})
}
.height('100%')
.width('100%')
}
}
Controlling event bubbling does not affect gesture recognition. Both aspects need to be considered separately.
The reporting frequency of basic events depends on the specific input device type, but it is generally very high. For example, a touchscreen typically reports a point every 5 to 7 ms, while some high-precision mice can report at a frequency of up to once every 1 ms. Since the response to input events is intended to produce UI changes in response to user actions, reporting such high-frequency basic events to the app is redundant in most cases. To address this, the system resamples the basic events received between two frames and reports them to the app only once within a frame. Resampling is performed independently for each touch point, and different touch points are resampled separately.

Resampling merges multiple move events reported by the same touch point within a single frame and calculates an appropriate coordinate to report to the app through an algorithm. As a result, the coordinate information after resampling may differ slightly from the actual points reported by the underlying device. These differences are beneficial, as the resampled points generally exhibit better smoothness.
To maintain full data transparency, the system preserves all original touch points prior to resampling. You can access the complete historical dataset when needed through getHistoricalPoints(): Array.
The following is a simple example.
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG = '[Sample_Sampling]';
const DOMAIN = 0xF811;
const BUNDLE = 'MyApp_Sampling';
@Entry
@ComponentV2
struct Sampling {
build() {
RelativeContainer() {
Column()
.backgroundColor(Color.Green)
.height('100%')
.width('100%')
.onTouch((event: TouchEvent) => {
// Obtain historical points from the event.
let allHistoricalPoints = event.getHistoricalPoints();
if (allHistoricalPoints.length !== 0) {
for (const point of allHistoricalPoints) {
hilog.info(DOMAIN, TAG, BUNDLE + 'historical point: [' + point.touchObject.windowX +
', ' + point.touchObject.windowY + ']');
}
}
})
}
.height('100%')
.width('100%')
}
} For multi-touch-capable devices, the reported events contain information about all touch points, which can be obtained through touches as follows:
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG = '[Sample_MultipleFingerInformation]';
const DOMAIN = 0xF811;
const BUNDLE = 'MyApp_MultipleFingerInformation';
@Entry
@ComponentV2
struct MultipleFingerInformation {
private currentFingerCount: number = 0;
private allFingerIds: number[] = [];
build() {
RelativeContainer() {
Column()
.backgroundColor(Color.Green)
.height('100%')
.width('100%')
.onTouch((event: TouchEvent) => {
if (event.source !== SourceType.TouchScreen) {
return;
}
// Clear the array.
this.allFingerIds.splice(0, this.allFingerIds.length);
// Obtain all touch point information from the event.
let allFingers = event.touches;
if (allFingers.length > 0 && this.currentFingerCount === 0) {
// The first finger is pressed.
hilog.info(DOMAIN, TAG, BUNDLE + 'fingers start to press down');
this.currentFingerCount = allFingers.length;
}
if (allFingers.length !== 0) {
for (const finger of allFingers) {
this.allFingerIds.push(finger.id);
}
hilog.info(DOMAIN, TAG, BUNDLE + 'current all fingers : ' + this.allFingerIds.toString());
}
if (event.type === TouchType.Up && event.touches.length === 1) {
// All fingers are lifted.
hilog.info(DOMAIN, TAG, BUNDLE + 'all fingers already up');
this.currentFingerCount = 0;
}
})
}
.height('100%')
.width('100%')
}
} Different touch points are distinguished by IDs. These IDs are assigned sequentially based on the order of contact with the screen and do not correspond to physical touch sources (fingers). When working with the touches array, note that the points are not sorted by their ID values, so do not rely on the array order for access. The ID allocation system maintains an efficient reuse policy: When a touch point is released (finger lifted), its ID becomes available for reassignment to new touch points that may occur before all current contacts are removed from the screen.
Below is the log output of the preceding example during the following operation sequence.

Press finger 1 -> Press finger 2 -> Press finger 3 -> Lift finger 2 -> Lift finger 3 -> Press finger 3 -> Lift finger 1 -> Lift finger 3
fingers start to press down // Press finger 1. current all fingers: 0 ... ... current all fingers: 0,1 // Press finger 2. ... ... current all fingers: 0,1,2 // Press finger 3. ... ... current all fingers: 0,2 // Lift finger 2. ... ... current all fingers: 0 // Lift finger 3. ... ... current all fingers: 0,1 // Press finger 3. ... ... current all fingers: 1 // Lift finger 1. ... ... all fingers already up // Lift finger 3.
Stylus operations on the touchscreen are similar to finger operations in that both generate touch events, which can be distinguished by sourceTool. For some active capacitive styluses, the reported touch events also include the angle information of the stylus in contact with the screen. For details, see BaseEvent.