一、背景: 还是不得不提及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
在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公布的。
三、滑动
一般情况下在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,终于写完 了,有什么不对的地方,请指正。 (jinhaijian) |