接下来,我们将了解最佳模型,并深入探讨让您能够接收和处理原始触控事件的 API。
与手势相同,您在应用程序的 WndProc 函数中处理 WM_TOUCH 消息。单条 WM_TOUCH 消息可能包含多条不同的“接触点消息”,需要将它们解包为一个触控输入结构数组。标准做法是将 WM_TOUCH 消息解包为一个 TOUCHINPUT 结构数组,该数组中的每个结构表示来自单个接触点的数据。若要进行解包,需要调用GetTouchInputInfo(HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) 函数,并向其传递 WM_TOUCH 消息的 lParam 以及一个新创建的接触点数组,如图 所示。
对 WM_TOUCH 进行解包
case WM_TOUCH:
{
unsigned int numInputs = (unsigned int) wParam;
TOUCHINPUT* ti = new TOUCHINPUT[numInputs];
if(GetTouchInputInfo((HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT)))
{
// Handle each contact point
for(unsigned int i=0; i< numInputs; ++i)
{
/* handle ti[i] */
}
}
CloseTouchInputHandle((HTOUCHINPUT)lParam);
delete [] ti;
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
这里,您可以看到我们如何使用来自每个接触点的数据来填充 TOUCHPOINT ti 数组。接下来,我们将遍历接触点数组,将逻辑应用于每个接触点 handle ti[i] 注释。最后,我们需要通过调用CloseTouchInputHandle(HTOUCHINPUT hTouchInput) 清除触控句柄,传递原始 WinProc 的 lParam。不这么做将会导致内存泄漏
以上代码表示处理 WM_TOUCH 消息的第一个步骤。单个触控输入结构 TOUCHINPUT 包含了有关您需要使用的单个接触点的所有必需信息:
- dwID - 接触点标识符,用于区分特定触控输入与其他输入
- dwFlags - 一组位标志,用于指定接触点的状态
- 接触点的 X 和 Y 坐标(基本上是每个接触点的位置)
- dwTime - 事件的时间步长,以毫秒为单位
- dwMask - 一组位标志,用于指定结构中的哪些可选字段包含有效值
请务必注意,X 和 Y 坐标的单位是实际屏幕坐标的像素的百分之一(即 centa-pixel)。对于可能需要高分辨率的其他应用程序而言,这种超高分辨率有利于达到高精度,实现更加精确的手写识别。但在大多数情况下,在开始使用这些坐标之前,请务必记住应将接触点的 X 和 Y 坐标除以一百,从而将接触点坐标转换为可用的屏幕坐标。
接下来让我们利用这些知识来构建一个多点触控画图应用程序,也称为“草稿板”。
在将触控消息解包为触控输入结构数组 ti 之后,需要检查每个接触点状态,并针对每个接触点状态应用不同的逻辑。在“草稿板”示例中,新接触点由向下状态 TOUCHEVENTF_DOWN 进行标识。您注册新的接触点 ID,并为其指定一种颜色。删除接触点 TOUCHEVENTF_UP 之后,您完成最后的绘图,并注销接触点 ID。在向上和向下事件之间,您很可能会收到大量移动消息 TOUCHEVENTF_MOVE。收到每条移动消息时,您向现有线条添加一个新点,然后绘制新的线段。图 显示了“草稿板”应用程序支持多点触控所需的整个 WM_TOUCH处理程序。
WM_TOUCH 处理程序
跟踪各个接触点的关键是使用 dwID,在特定触控笔划的整个过程中,它都保持不变。在OnTouchDownHandler 帮助函数中,您将此 ID 指定给 CStroke 对象,该对象基本上是一个代表线条的点数组。这个线条是您在触敏式设备上拖动手指时形成的轨迹。我们将不再介绍支持应用程序并在屏幕上实际绘制线条的完整代码示例。在前面的代码示例中,您基本上可以找到支持多点触控所需的全部操作。