Windows Mobile 6.5 手势识别API(Gesture)
一、背景:还是不得不提及iPhone的伟大创造性工作,用手势识别来操作手机,特别是对于滚动条,想想之前是何等的痛苦,拿着触摸板,在那个只有几个像素的滚动条上又是拉又是点的。在WM6.5没有出来之前,自己也实现过手势识别的引擎,包括方向识别、画圈识别。微软为了大家不至于对他失去信心,还是在6.5上了点点东西。对于开发者来说,其实只要添加了Gesture API 和widget。最近做个新的项目,需要在WM6.5上实现,需要用到Gesture API,自己学习了下,随便写下来,自己做个备忘。
二、轨迹识别:
当对屏幕有操作时,微软会发送WM_GESTURE消息。wParam是Gesture ID, lParam指向HGESTUREINFO,通过GetGestureInfo得到GESTUREINFO。
BOOL GetGestureInfo(
HGESTUREINFO hGestureInfo,
PGESTUREINFO pGestureInfo
);
GESTUREINFO 定义
typedef struct tagGESTUREINFO {
UINT cbSize;
DWORD dwFlags;
DWORD dwID; // Gesture command ID
HWND hwndTarget;
POINTS ptsLocation;
DWORD dwInstanceID; // not use
DWORD dwSequenceID;
ULONGLONG ullArguments; //
UINT cbExtraArguments; // 扩展参数buffer大小
} GESTUREINFO, *PGESTUREINFO;
微软定义了一个Gesture ID
ID |
宏定义 |
意义 |
1 |
GID_BEGIN |
按下时发送,与GID_END成对出现 |
2 |
GID_END |
抬起,与GID_BEGIN成对出现 |
4 |
GID_PAN |
移动的 |
5 |
GID_SCROLL |
只有当加速滑动的时候,会产生GID_SCROLL |
9 |
GID_HOLD |
按住不动一定时间 |
10 |
GID_SELECT |
单击,并马上弹起 |
11 |
GID_DOUBLESELECT |
双击 |
在GID_SCROLL、GID_HOLD、GID_SELECT、GID_DOUBLESELECT这四个为连续的数值,说明四个状态时互质的。很容易明白滚动、选择、双击、长按是不可能同时发生的。
其中GID_SCROLL是相对复杂一些的,滚动我们不仅要知道当前坐标,还需要知道方向、速度和角度。微软在ullArguments保持了这三个信息,并定义了三个宏来从参数ullArguments中得到。
GESTUREINFO gi = {sizeof(gi)};
if (GetGestureInfo(reinterpret_cast<HGESTUREINFO>(lParam), &gi))
{
static POINT ptsLast = {0};
switch (wParam)
{
case GID_PAN:
break;
case GID_SCROLL:
{
// 速度
int iVelocity = (int)GID_SCROLL_VELOCITY(gi.ullArguments);
// 角度
int iAngle = (int)GID_SCROLL_ANGLE(gi.ullArguments);
// 方向
int iDirection = (int)GID_SCROLL_DIRECTION(gi.ullArguments);
}
break;
}
}
角度的值是从0到65535,代表0到2PI。定义了四个方向
#define ARG_SCROLL_NONE
#define ARG_SCROLL_RIGHT
#define ARG_SCROLL_UP
#define ARG_SCROLL_LEFT
#define ARG_SCROLL_DOWN
通过上面的介绍,我们了解了如何获取手势轨迹。
下面是微软Gesture API,部分API SDK中没有公布,只给OEM公布的。
Function |
Description |
CloseGestureInfoHandle |
This function cleans up any gesture handle that is not processed by DefWindowProc. |
DisableGestures |
设置指定窗体或者进程Gesture command ID无效 |
EnableGestures |
设置指定窗体或者进程Gesture command ID有效 |
Gesture |
This function enables asynchronous recognizers to produce gesture messages. |
GetAnimateMessageInfo |
Retrieves the animation message associated with flicks or pans for window auto gestures. |
GetGestureExtraArguments |
得到扩展参数 |
GetGestureInfo |
接收到WM_GESTURE时消息,获取GESTUREINFO信息 |
GetWindowAutoGesture |
Returns the current settings for a window's flick and pan gestures. |
QueryGestures |
得到指定窗体或者进程所支持的Gesture ID |
RegisterGesture |
自定义Gesture |
RegisterDefaultGestureHandler |
接收未处理的WM_GESTURE消息 |
SetWindowAutoGesture |
自动处理Gesture消息 |
三、滑动
一般情况下在GID_PAN和GID_ SCROLL处理滑动。GID_PAN处理拖动,GDI_SCROLL处理滑动。GID_PAN时,只要处理每次位移是多少就可以了。然后刷新窗体。GID_SCROLL需要调用
CreatePhysicsEngine来设置负的加速度,使其最终停下来。然后再设置一个TIMER去,在TIMER处理函数中用QueryPhysicsEngine去得到移动的相对位移。然后计算,重新刷新。
SDK参考代码:
HRESULT hr = S_OK;
//int nYExtendedPan = 0;
//int nXExtendedPan = 0;
PHYSICSENGINEINIT initState = {sizeof(initState)};
RECT rctClient = {0};
initState.dwEngineType = 0;
initState.dwFlags = 0;
initState.lInitialVelocity = -nTransitionSpeed; // 设置初始速度
initState.dwInitialAngle = nTransitionAngle; // 设置角度
initState.bXAxisMovementMode = PHYSICSENGINE_MOVEMENT_MODE_DECELERATE;
initState.bYAxisMovementMode = PHYSICSENGINE_MOVEMENT_MODE_DECELERATE;
initState.bXAxisBoundaryMode = PHYSICSENGINE_BOUNDARY_MODE_RUBBERBAND;
initState.bYAxisBoundaryMode = PHYSICSENGINE_BOUNDARY_MODE_RUBBERBAND;
GetClientRect(hwnd, &rctClient);
initState.rcBoundary.left = 0;
initState.rcBoundary.right = rctClient.right + g_nMaxXExtent; // 设置边界
initState.rcBoundary.top = 0;
initState.rcBoundary.bottom = rctClient.bottom + g_nMaxYExtent; // 设置边界
initState.sizeView.cx = rctClient.right;
initState.sizeView.cy = rctClient.bottom;
initState.ptInitialPosition.x = g_nXPos;
initState.ptInitialPosition.y = g_nYPos;
initState.sizeItem.cx = 1;
initState.sizeItem.cy = 1;
// create the physics engine and store it
if (SUCCEEDED(CreatePhysicsEngine(&initState, &g_hPhysicsEngine)))
{
g_fAnimating = true; // we are now animating
// Setup the timer
g_uTimerId = SetTimer( hwnd,
GESTURE_ANIMATION_TIMER_ID,
20,
AnimationTimerProc);
}
OK,终于写完了,有什么不对的地方,请指正。