(笔记) MultiTouchInputMapper手势识别算法深度分析
·
概述
MultiTouchInputMapper是Android输入系统中处理多点触摸事件的核心组件,它不仅负责处理原始触摸数据,还实现了复杂的手势识别算法。本文深入分析MultiTouchInputMapper的实现机制,包括触摸状态管理、多指跟踪、手势检测算法等关键技术。
AOSP源码基于 Android 7.0
MultiTouchInputMapper整体架构
┌─────────────────────────────────────────────────────────────┐
│ MultiTouchInputMapper │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 原始事件处理 │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ MultiTouchMotionAccumulator │ │ │
│ │ │ ┌─────────────────────────────────────┐ │ │ │
│ │ │ │ Slot-based Protocol B │ │ │ │
│ │ │ │ (ABS_MT_SLOT, ABS_MT_TRACKING_ID) │ │ │ │
│ │ │ └─────────────────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 触摸状态管理 │ │
│ │ ┌─────────────────┐ ┌─────────────────────────┐ │ │
│ │ │ RawState │ │ CookedState │ │ │
│ │ │ (原始触摸数据) │ │ (处理后的触摸数据) │ │ │
│ │ └─────────────────┘ └─────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 手势识别算法 │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ TAP手势 │ │ DRAG手势 │ │ ZOOM手势 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ SWIPE手势 │ │ FREEFORM手势 │ │ HOVER手势 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 事件分发 │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ NotifyMotionArgs │ │ │
│ │ │ 发送给InputDispatcher │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
1. MultiTouchInputMapper基础架构
1.1 继承关系和初始化
文件路径: frameworks/native/services/inputflinger/InputReader.cpp
// MultiTouchInputMapper继承自TouchInputMapper
class MultiTouchInputMapper : public TouchInputMapper {
public:
MultiTouchInputMapper(InputDevice* device);
virtual ~MultiTouchInputMapper();
virtual void reset(nsecs_t when);
virtual void process(const RawEvent* rawEvent);
protected:
virtual void syncTouch(nsecs_t when, RawState* outState);
virtual void configureRawPointerAxes();
virtual bool hasStylus() const;
private:
MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
BitSet32 mPointerIdBits;
};
MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device)
: TouchInputMapper(device) {
}
void MultiTouchInputMapper::reset(nsecs_t when) {
mMultiTouchMotionAccumulator.reset(getDevice());
mPointerIdBits.clear();
TouchInputMapper::reset(when);
}
1.2 设备类型检测和配置
// frameworks/native/services/inputflinger/InputReader.cpp
// 在createDeviceLocked中根据设备类型创建相应的InputMapper
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
const InputDeviceIdentifier& identifier, uint32_t classes) {
InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
controllerNumber, identifier, classes);
// 触摸设备检测和Mapper创建
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
// 多点触摸设备 - 创建MultiTouchInputMapper
device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
// 单点触摸设备 - 创建SingleTouchInputMapper
device->addMapper(new SingleTouchInputMapper(device));
}
return device;
}
1.3 轴配置和校准
void MultiTouchInputMapper::configureRawPointerAxes() {
TouchInputMapper::configureRawPointerAxes();
// 配置多点触摸相关的轴信息
getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
// 检查设备是否支持Slot-based Protocol B
if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
if (slotCount > MAX_SLOTS) {
ALOGW("MultiTouch Device %s reported %zu slots but the framework "
"only supports a maximum of %zu slots at this time.",
getDeviceName().string(), slotCount, MAX_SLOTS);
slotCount = MAX_SLOTS;
}
mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/);
} else {
mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS, false /*usingSlotsProtocol*/);
}
}
2. 原始事件处理:MultiTouchMotionAccumulator
2.1 Slot-based Protocol B
MultiTouchInputMapper支持Linux多点触摸协议B,这是一种基于槽位的协议:
| 事件类型 | 事件代码 | 说明 |
|---|---|---|
| EV_ABS | ABS_MT_SLOT | 当前槽位选择 |
| EV_ABS | ABS_MT_TRACKING_ID | 触点跟踪ID(-1表示触点离开) |
| EV_ABS | ABS_MT_POSITION_X | 触点X坐标 |
| EV_ABS | ABS_MT_POSITION_Y | 触点Y坐标 |
| EV_ABS | ABS_MT_PRESSURE | 触点压力 |
| EV_ABS | ABS_MT_TOUCH_MAJOR | 触摸区域长轴 |
| EV_ABS | ABS_MT_TOUCH_MINOR | 触摸区域短轴 |
| EV_ABS | ABS_MT_ORIENTATION | 触摸方向 |
2.2 事件累积器结构
class MultiTouchMotionAccumulator {
public:
class Slot {
public:
inline bool isInUse() const { return mInUse; }
inline int32_t getX() const { return mAbsMTPositionX; }
inline int32_t getY() const { return mAbsMTPositionY; }
inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; }
inline int32_t getTouchMinor() const { return mAbsMTTouchMinor; }
inline int32_t getToolMajor() const { return mAbsMTWidthMajor; }
inline int32_t getToolMinor() const { return mAbsMTWidthMinor; }
inline int32_t getOrientation() const { return mAbsMTOrientation; }
inline int32_t getTrackingId() const { return mAbsMTTrackingId; }
inline int32_t getPressure() const { return mAbsMTPressure; }
inline int32_t getDistance() const { return mAbsMTDistance; }
inline int32_t getToolType() const;
private:
bool mInUse;
bool mHaveAbsMTPositionX;
bool mHaveAbsMTPositionY;
bool mHaveAbsMTTouchMajor;
bool mHaveAbsMTTouchMinor;
bool mHaveAbsMTWidthMajor;
bool mHaveAbsMTWidthMinor;
bool mHaveAbsMTOrientation;
bool mHaveAbsMTTrackingId;
bool mHaveAbsMTPressure;
bool mHaveAbsMTDistance;
bool mHaveAbsMTToolType;
int32_t mAbsMTPositionX;
int32_t mAbsMTPositionY;
int32_t mAbsMTTouchMajor;
int32_t mAbsMTTouchMinor;
int32_t mAbsMTWidthMajor;
int32_t mAbsMTWidthMinor;
int32_t mAbsMTOrientation;
int32_t mAbsMTTrackingId;
int32_t mAbsMTPressure;
int32_t mAbsMTDistance;
int32_t mAbsMTToolType;
};
private:
Slot* mSlots;
size_t mSlotCount;
int32_t mCurrentSlot;
bool mUsingSlotsProtocol;
};
2.3 原始事件处理
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
TouchInputMapper::process(rawEvent);
// 将原始事件传递给累积器处理
mMultiTouchMotionAccumulator.process(rawEvent);
}
void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
if (rawEvent->type == EV_ABS) {
bool newSlot = false;
if (mUsingSlotsProtocol) {
if (rawEvent->code == ABS_MT_SLOT) {
mCurrentSlot = rawEvent->value;
newSlot = true;
}
} else if (mCurrentSlot < 0) {
mCurrentSlot = 0;
}
if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
return; // 无效槽位
}
Slot* slot = &mSlots[mCurrentSlot];
switch (rawEvent->code) {
case ABS_MT_POSITION_X:
slot->mAbsMTPositionX = rawEvent->value;
slot->mHaveAbsMTPositionX = true;
break;
case ABS_MT_POSITION_Y:
slot->mAbsMTPositionY = rawEvent->value;
slot->mHaveAbsMTPositionY = true;
break;
case ABS_MT_TRACKING_ID:
if (mUsingSlotsProtocol && rawEvent->value < 0) {
// 触点离开
slot->clear();
} else {
slot->mAbsMTTrackingId = rawEvent->value;
slot->mHaveAbsMTTrackingId = true;
slot->mInUse = true;
}
break;
case ABS_MT_PRESSURE:
slot->mAbsMTPressure = rawEvent->value;
slot->mHaveAbsMTPressure = true;
break;
// ... 其他轴的处理
}
}
}
3. 触摸状态管理
3.1 状态数据结构
TouchInputMapper使用两个主要的状态结构来管理触摸数据:
struct RawState {
nsecs_t when;
// 原始触摸数据
RawPointerData rawPointerData;
// 按钮状态
int32_t buttonState;
// 滚轮状态
int32_t rawVScroll;
int32_t rawHScroll;
};
struct CookedState {
// 处理后的触摸数据
CookedPointerData cookedPointerData;
// 按钮状态
int32_t buttonState;
// 手指ID位集
BitSet32 fingerIdBits;
BitSet32 stylusIdBits;
BitSet32 mouseIdBits;
};
3.2 触摸数据转换:syncTouch
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
size_t outCount = 0;
BitSet32 newPointerIdBits;
// 遍历所有槽位,提取有效的触摸点
for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
const MultiTouchMotionAccumulator::Slot* inSlot =
mMultiTouchMotionAccumulator.getSlot(inIndex);
if (!inSlot->isInUse()) {
continue;
}
if (outCount >= MAX_POINTERS) {
break; // 触点数量超过限制
}
RawPointerData::Pointer& outPointer =
outState->rawPointerData.pointers[outCount];
outPointer.x = inSlot->getX();
outPointer.y = inSlot->getY();
outPointer.pressure = inSlot->getPressure();
outPointer.touchMajor = inSlot->getTouchMajor();
outPointer.touchMinor = inSlot->getTouchMinor();
outPointer.toolMajor = inSlot->getToolMajor();
outPointer.toolMinor = inSlot->getToolMinor();
outPointer.orientation = inSlot->getOrientation();
outPointer.distance = inSlot->getDistance();
outPointer.tiltX = 0;
outPointer.tiltY = 0;
outPointer.toolType = inSlot->getToolType();
outPointer.isHovering = (inSlot->getDistance() != 0);
// 分配指针ID
uint32_t id = inSlot->getTrackingId();
if (id < 0) {
id = mPointerIdBits.markFirstUnmarkedBit();
}
outPointer.id = id;
outState->rawPointerData.idToIndex[id] = outCount;
outState->rawPointerData.markIdBit(id, outPointer.isHovering);
newPointerIdBits.markBit(id);
outCount += 1;
}
outState->rawPointerData.pointerCount = outCount;
mPointerIdBits = newPointerIdBits;
}
3.3 坐标变换和校准
void TouchInputMapper::cookPointerData() {
uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;
mCurrentCookedState.cookedPointerData.clear();
mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
mCurrentCookedState.cookedPointerData.hoveringIdBits =
mCurrentRawState.rawPointerData.hoveringIdBits;
mCurrentCookedState.cookedPointerData.touchingIdBits =
mCurrentRawState.rawPointerData.touchingIdBits;
// 复制并转换每个触点的数据
for (uint32_t i = 0; i < currentPointerCount; i++) {
const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
CookedPointerData::Pointer& out = mCurrentCookedState.cookedPointerData.pointers[i];
out.id = in.id;
out.x = in.x;
out.y = in.y;
// 应用仿射变换(旋转、缩放、平移)
float xTransformed = in.x, yTransformed = in.y;
mAffineTransform.applyTo(xTransformed, yTransformed);
// 应用表面参数转换
float left, top, right, bottom;
switch (mSurfaceOrientation) {
case DISPLAY_ORIENTATION_90:
left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
break;
case DISPLAY_ORIENTATION_180:
left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
break;
case DISPLAY_ORIENTATION_270:
left = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
right = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
break;
default:
left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
break;
}
out.x = (left + right) * 0.5f;
out.y = (top + bottom) * 0.5f;
// 计算压力、尺寸等其他属性
out.pressure = in.pressure;
out.touchMajor = in.touchMajor;
out.touchMinor = in.touchMinor;
out.toolMajor = in.toolMajor;
out.toolMinor = in.toolMinor;
out.orientation = in.orientation;
out.distance = in.distance;
out.toolType = in.toolType;
out.isHovering = in.isHovering;
}
}
4. 手势识别算法
4.1 手势类型定义
struct PointerGesture {
enum Mode {
NEUTRAL, // 无手势
QUIET, // 静默模式
HOVER, // 悬停
TAP, // 点击
TAP_DRAG, // 点击拖拽
BUTTON_CLICK_OR_DRAG, // 按钮点击或拖拽
SWIPE, // 滑动
FREEFORM, // 自由形式
PRESS, // 按压
};
Mode currentGestureMode;
Mode lastGestureMode;
nsecs_t downTime;
int32_t activeGestureId;
uint32_t activeTouchId;
BitSet32 currentGestureIdBits;
// 手势坐标和属性
PointerProperties currentGestureProperties[MAX_POINTER_ID + 1];
PointerCoords currentGestureCoords[MAX_POINTER_ID + 1];
uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1];
// 速度跟踪
VelocityTracker velocityTracker;
// TAP手势参数
nsecs_t tapDownTime;
nsecs_t tapUpTime;
float tapX, tapY;
// SWIPE手势参数
nsecs_t swipeDownTime;
float swipeStartX, swipeStartY;
// ZOOM手势参数
float zoomCenterX, zoomCenterY;
float zoomMagnification;
};
4.2 手势检测主函数
void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags,
bool isTimeout) {
// 更新手势检测器状态
mPointerGesture.reset();
// 获取当前触摸点信息
uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count();
// 更新速度跟踪器
bool down, up, moved, resampled;
mPointerGesture.velocityTracker.clear();
BitSet32 idBits = mCurrentCookedState.fingerIdBits;
while (!idBits.isEmpty()) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
mPointerGesture.velocityTracker.addMovement(when, BitSet32::valueForBit(id),
pointer.x, pointer.y);
}
// 根据当前状态和触摸点数量进行手势检测
if (currentFingerCount == 0) {
// 无触摸点 - 检测TAP手势或返回NEUTRAL
detectTapGesture(when, &mPointerGesture);
} else if (currentFingerCount == 1) {
// 单点触摸 - 检测HOVER、TAP_DRAG或BUTTON_CLICK_OR_DRAG
detectSingleTouchGesture(when, &mPointerGesture);
} else {
// 多点触摸 - 检测SWIPE、FREEFORM或PRESS手势
detectMultiTouchGesture(when, currentFingerCount, &mPointerGesture);
}
// 分发手势事件
dispatchPointerGestureMotion(when, policyFlags, mSource,
mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits,
mPointerGesture.currentGestureProperties, mPointerGesture.currentGestureCoords);
}
4.3 TAP手势检测
void TouchInputMapper::detectTapGesture(nsecs_t when, PointerGesture* outGesture) {
// 检查是否从HOVER或TAP_DRAG状态转换而来
if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER ||
mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) &&
mLastCookedState.fingerIdBits.count() == 1) {
// 检查时间间隔
if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
float x, y;
mPointerController->getPosition(&x, &y);
// 检查距离是否在TAP容差范围内
if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
// 确认为TAP手势
mPointerGesture.tapUpTime = when;
outGesture->currentGestureMode = PointerGesture::TAP;
// 设置手势坐标
outGesture->currentGestureCoords[0].clear();
outGesture->currentGestureCoords[0].setAxisValue(
AMOTION_EVENT_AXIS_X, mPointerGesture.tapX);
outGesture->currentGestureCoords[0].setAxisValue(
AMOTION_EVENT_AXIS_Y, mPointerGesture.tapY);
outGesture->currentGestureCoords[0].setAxisValue(
AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
return;
}
}
}
// 不满足TAP条件,返回NEUTRAL状态
outGesture->currentGestureMode = PointerGesture::NEUTRAL;
}
4.4 多点触摸手势检测
void TouchInputMapper::detectMultiTouchGesture(nsecs_t when, uint32_t fingerCount,
PointerGesture* outGesture) {
// 计算触摸点的质心
float centroidX = 0, centroidY = 0;
for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
centroidX += pointer.x;
centroidY += pointer.y;
}
centroidX /= fingerCount;
centroidY /= fingerCount;
// 计算质心移动距离
float deltaX = centroidX - mPointerGesture.referenceCentroidX;
float deltaY = centroidY - mPointerGesture.referenceCentroidY;
float distance = hypotf(deltaX, deltaY);
// 计算触摸点间的距离变化(用于检测缩放)
float currentSpan = calculateSpan();
float lastSpan = mPointerGesture.referenceSpan;
float spanDelta = currentSpan - lastSpan;
if (mPointerGesture.lastGestureMode == PointerGesture::PRESS ||
mPointerGesture.lastGestureMode == PointerGesture::SWIPE ||
mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) {
// 判断是否为SWIPE手势
if (fingerCount == 2 && distance > mConfig.pointerGestureMultitouchMinDistance) {
// 计算两个触点的速度方向
float vx1, vy1, vx2, vy2;
bool hasVelocity = true;
BitSet32 idBits(mCurrentCookedState.fingerIdBits);
uint32_t id1 = idBits.clearFirstMarkedBit();
uint32_t id2 = idBits.clearFirstMarkedBit();
if (!mPointerGesture.velocityTracker.getVelocity(id1, &vx1, &vy1) ||
!mPointerGesture.velocityTracker.getVelocity(id2, &vx2, &vy2)) {
hasVelocity = false;
}
if (hasVelocity) {
// 计算速度向量的夹角余弦值
float dot = vx1 * vx2 + vy1 * vy2;
float mag1 = hypotf(vx1, vy1);
float mag2 = hypotf(vx2, vy2);
float cosine = dot / (mag1 * mag2);
if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
// 两个触点方向一致,判定为SWIPE
outGesture->currentGestureMode = PointerGesture::SWIPE;
} else {
// 方向不一致,判定为FREEFORM(可能包含缩放、旋转)
outGesture->currentGestureMode = PointerGesture::FREEFORM;
}
}
} else {
// 多于2个触点或移动距离较小,判定为FREEFORM
outGesture->currentGestureMode = PointerGesture::FREEFORM;
}
} else {
// 初始状态,判定为PRESS
outGesture->currentGestureMode = PointerGesture::PRESS;
mPointerGesture.referenceCentroidX = centroidX;
mPointerGesture.referenceCentroidY = centroidY;
mPointerGesture.referenceSpan = currentSpan;
}
// 设置手势坐标(以质心为基准)
outGesture->currentGestureCoords[0].clear();
outGesture->currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, centroidX);
outGesture->currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, centroidY);
outGesture->currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
// 如果是FREEFORM手势,还需要设置缩放信息
if (outGesture->currentGestureMode == PointerGesture::FREEFORM && lastSpan > 0) {
float scale = currentSpan / lastSpan;
outGesture->currentGestureCoords[0].setAxisValue(
AMOTION_EVENT_AXIS_GENERIC_1, scale);
}
}
4.5 速度计算和平滑
float TouchInputMapper::calculateSpan() {
BitSet32 idBits(mCurrentCookedState.fingerIdBits);
if (idBits.count() < 2) {
return 0;
}
// 找到距离最远的两个触点
float maxDistance = 0;
while (!idBits.isEmpty()) {
uint32_t id1 = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer1 =
mCurrentRawState.rawPointerData.pointerForId(id1);
BitSet32 remainingBits(idBits);
while (!remainingBits.isEmpty()) {
uint32_t id2 = remainingBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer2 =
mCurrentRawState.rawPointerData.pointerForId(id2);
float distance = hypotf(pointer1.x - pointer2.x, pointer1.y - pointer2.y);
if (distance > maxDistance) {
maxDistance = distance;
}
}
}
return maxDistance;
}
void TouchInputMapper::updatePointerGestureVelocity() {
BitSet32 idBits = mCurrentCookedState.fingerIdBits;
while (!idBits.isEmpty()) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
mPointerGesture.velocityTracker.addMovement(
mCurrentRawState.when, BitSet32::valueForBit(id),
pointer.x, pointer.y);
}
}
5. 触摸事件分发
5.1 事件分发流程
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentCookedState.buttonState;
if (currentIdBits == lastIdBits) {
if (!currentIdBits.isEmpty()) {
// 无指针ID变化,这是一个移动事件
dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
currentIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
}
} else {
// 有指针ID变化,需要处理按下和抬起事件
BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);
BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);
BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
BitSet32 dispatchedIdBits(lastIdBits.value);
// 分发指针抬起事件
while (!upIdBits.isEmpty()) {
uint32_t upId = upIdBits.clearFirstMarkedBit();
dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
metaState, buttonState, 0,
mLastCookedState.cookedPointerData.pointerProperties,
mLastCookedState.cookedPointerData.pointerCoords,
mLastCookedState.cookedPointerData.idToIndex,
dispatchedIdBits, upId,
mOrientedXPrecision, mOrientedYPrecision, mDownTime);
dispatchedIdBits.clearBit(upId);
}
// 分发移动事件
if (!moveIdBits.isEmpty()) {
dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
metaState, buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
}
// 分发指针按下事件
while (!downIdBits.isEmpty()) {
uint32_t downId = downIdBits.clearFirstMarkedBit();
dispatchedIdBits.markBit(downId);
if (dispatchedIdBits.count() == 1) {
// 第一个指针按下,设置按下时间
mDownTime = when;
}
dispatchMotion(when, policyFlags, mSource,
dispatchedIdBits.count() == 1 ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_POINTER_DOWN,
0, 0, metaState, buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
dispatchedIdBits, downId,
mOrientedXPrecision, mOrientedYPrecision, mDownTime);
}
}
}
5.2 MotionEvent构造
void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
int32_t action, int32_t actionButton, int32_t flags,
int32_t metaState, int32_t buttonState, int32_t edgeFlags,
const PointerProperties* properties, const PointerCoords* coords,
const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId,
float xPrecision, float yPrecision, nsecs_t downTime) {
PointerCoords pointerCoords[MAX_POINTERS];
PointerProperties pointerProperties[MAX_POINTERS];
uint32_t pointerCount = 0;
// 构造指针数组
while (!idBits.isEmpty()) {
uint32_t id = idBits.clearFirstMarkedBit();
uint32_t index = idToIndex[id];
pointerProperties[pointerCount].copyFrom(properties[index]);
pointerCoords[pointerCount].copyFrom(coords[index]);
if (changedId >= 0 && id == uint32_t(changedId)) {
action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
pointerCount += 1;
}
ALOG_ASSERT(pointerCount != 0);
if (changedId >= 0 && pointerCount == 1) {
// 对于单指事件,使用简化的动作码
if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
action = AMOTION_EVENT_ACTION_DOWN;
} else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
action = AMOTION_EVENT_ACTION_UP;
} else {
// 不应该发生
ALOG_ASSERT(false);
}
}
// 创建并发送NotifyMotionArgs
NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
action, actionButton, flags, metaState, buttonState, edgeFlags,
getDisplayId(), pointerCount, pointerProperties, pointerCoords,
xPrecision, yPrecision, downTime);
getListener()->notifyMotion(&args);
}
6. 性能优化和调试
6.1 关键性能优化
| 优化技术 | 实现方式 | 性能收益 |
|---|---|---|
| 槽位协议优化 | Protocol B减少数据传输 | 降低CPU使用率 |
| 批量事件处理 | 多个原始事件合并处理 | 减少函数调用开销 |
| 内存池化 | 重用触摸状态对象 | 减少内存分配 |
| 速度计算优化 | 增量式速度跟踪 | 提高响应速度 |
6.2 调试开关和日志
// 调试开关定义
#define DEBUG_POINTERS 0 // 指针调试
#define DEBUG_GESTURES 0 // 手势调试
#define DEBUG_VIRTUAL_KEYS 0 // 虚拟按键调试
#define DEBUG_RAW_EVENTS 0 // 原始事件调试
// 关键调试日志
#if DEBUG_GESTURES
ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
"bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed);
#endif
#if DEBUG_POINTERS
ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
"ignoring the rest.", getDeviceName().string(), MAX_POINTERS);
#endif
6.3 性能监控指标
| 监控指标 | 获取方式 | 用途 |
|---|---|---|
| 触摸延迟 | 事件时间戳对比 | 性能分析 |
| 手势识别准确率 | 用户反馈统计 | 算法优化 |
| CPU使用率 | 系统监控 | 资源使用分析 |
| 内存使用 | 对象计数 | 内存泄漏检测 |
7. 总结
7.1 MultiTouchInputMapper核心价值
- 多点触摸支持: 完整实现Linux多点触摸协议B
- 智能手势识别: 支持TAP、DRAG、SWIPE、ZOOM等多种手势
- 精确坐标转换: 处理屏幕旋转、缩放和校准
- 高性能处理: 优化的事件处理和状态管理
- 可扩展架构: 支持不同类型的触摸设备
7.2 技术特点
- 协议支持: 兼容Protocol A和Protocol B
- 状态管理: 精确的触摸状态跟踪和转换
- 手势算法: 基于几何和速度的智能识别
- 坐标系统: 完整的仿射变换和校准支持
- 性能优化: 槽位优化、批量处理等技术
7.3 系统架构意义
MultiTouchInputMapper作为触摸输入的核心处理器:
- 承接硬件: 处理来自触摸屏驱动的原始数据
- 智能识别: 将复杂的多点触摸转换为有意义的手势
- 标准接口: 为上层应用提供标准化的触摸事件
- 性能保障: 确保触摸系统的低延迟和高精度
相关文件路径
核心实现文件
frameworks/native/services/inputflinger/InputReader.cpp- MultiTouchInputMapper实现frameworks/native/services/inputflinger/InputReader.h- MultiTouchInputMapper头文件
相关组件
MultiTouchMotionAccumulator- 多点触摸事件累积器TouchInputMapper- 触摸输入基类PointerGesture- 手势识别结构VelocityTracker- 速度跟踪器
配置文件
frameworks/native/data/etc/- 触摸设备配置system/usr/idc/- 输入设备特性文件
更多推荐


所有评论(0)