手势 - MFC

概述

Windows 7 支持用户通过手指接触来管理应用程序,无需使用触笔。这种新的手势功能允许多个输入事件在不同的指向位置上同时发生,它还支持复杂的场景,比如通过十个手指或多个并发用户来管理应用程序。但是,要实现这些功能,我们必须调整应用程序的用户界面和行为,以支持这种新的输入模型。

目标

在本次动手实验中,您将学习如何管理手势事件,包括:

•              理解使用手势事件操作对象的含义

•              查看多点触控硬件是否存在及其就绪情况

•              从手势 Windows 消息中提取信息

系统要求

您必须拥有以下工具才能完成本实验:

•              Microsoft Visual Studio 2010 Beta 2(或更高版本)

•             Windows 7

•             多点触控硬件设备 

简介

要创建一个多点触控驱动的应用程序,您应该将触控功能设计到应用程序的用户界面中。例如,您使用的控件应该相对较大,以支持手指而不是鼠标或触笔。除此之外,您还可以考虑 3 中不同的方法:

1.            依赖触控功能已经是现有 Win32 控件的一部分。例如,Win32 控件已经支持一些触控功能,比如无需额外编码即可滚动应用程序。尝试使用手指滚动您正在阅读的文档。

2.       阅读并处理低水平的触控事件,创建您自己的高级触控处理界面。这是用户可以同时操作的多个复杂滑块,以及“Piano”或 MS 等应用程序所使用的方法。例如,运行 MS Paint,从库中选择绘制工具并使用四个手指进行绘制(如果硬件允许):

 

3.            使用新的多点触控手势让应用程序获得触控事件,以支持缩放、旋转、平移等自定义操作,无需读取和操作原始触控事件。这是本动手实验使用的方法。

关于多点触控手势演示

多点触控手势演示展示一个简单的窗口,其中包含一个矩形,可以使用多点触控手势对它进行修改。在 Sources\MFC_GestureDemoSource 文件夹中,您可以找到一个 Starter 文件夹,其中包含本实验所需的一些文件,以及本实验的一个完成版本,位于 Final 文件夹中。

该应用程序通过与绘制的矩形交互来响应多点触控手势输入。矩形响应以下手势:

平移

要平移图像,将一个或两个手指放在应用程序窗口中,并向您期望的方向拖动。确保两个手指之间留有空间,以便触控界面将其视为不同的接触点。

旋转

使用两个手指接触矩形并在一个圆中旋转手指。

 

缩放

用两个手指接触屏幕,将它们分离或靠拢。

 

双指点击

使用两个手指同时点击可以切换红色矩形内的对角线开关状态。

 

双指交替点击

按住一个手指,然后使用另一个手指点击,并移开第一个手指可以更改矩形的颜色。

 

练习 1:构建多点触控手势应用程序

任务 1 –使用 Application Wizard 创建 MFC 应用程序

1.            启动 Visual Studio 2010

2.            新建一个 MFC 应用程序项目,将其命名为 TouchGestureDemo 并按 OK

 

3.            在 MFC Application Wizard 的 Application Type 对话框中,选择 Single Document。为了保持应用程序简单,根据以下屏幕截图在 MFC Application Wizard 的各种对话框中选择(或不选择)选项,然后在每个对话框后按 Next > 。

 

4.            继续单击 Next,直到最后在 Generated Classes 向导对话框中单击 Finish:

 

               

任务 2 –检查多点触控硬件是否存在及就绪情况

1.            我们正在构建的应用程序需要支持触控的硬件,因此我们需要在应用程序中查看这一点。在 TouchGestureDemo.cpp 中,在 CTouchGestureDemoApp::InitInstance() 后添加以下代码:

C++

BYTE digitizerStatus = (BYTE) GetSystemMetrics(SM_DIGITIZER);

if ((digitizerStatus & (0x80 + 0x40)) == 0) //Stack Ready + MultiTouch

{

AfxMessageBox(L"No touch input is currently available.");

return false;

}

BYTE nInputs = (BYTE) GetSystemMetrics(SM_MAXIMUMTOUCHES);

CString str;

str.Format(L"Touch input available with %d touch points.", nInputs);

AfxMessageBox(str);

return true;

2.            您可以看到,除了查看触控可用性以外,我们还可以发现硬件支持的触控输入数量。

3.            编译并运行。

4.            根据机器上触控输入的数量,您应该看到类似以下屏幕截图的输出:

 

任务 3 –向项目添加绘制对象源和头文件并绘制矩形

在 Starter 文件夹中,您将找到两个文件:DrawingObject.h 和 DrawingObject.cpp。将它们复制到项目文件夹下并使用“Add Existing item…”将其添加到项目中。

1.            将一个 #include DrawingObject.h 行添加到 of stdafx.h 头文件尾部的 #include <afxcontrolbars.h> 之后:

C++

   #include "DrawingObject.h"

2.            将以下 protected 成员变量定义添加到 ChildView.h 中。这是主绘制对象:

C++

    // The drawing object

    CDrawingObject m_drawingObject;

3.       此外,将以下 protected 成员变量添加到 ChildView.h 中。有了它们才能够支持定位和调整绘制对象的大小:

C++

    // Needed for drawing object position and size calculations

    double m_dblZoomRatioStart;

    double m_dblZoomRatioTotal;             

    CPoint m_ptCenter;

4.            要绘制矩形,将 ChildView.cpp 中的以下行添加到 CChildCiew::OnPaint()末尾:

C++

    m_drawingObject.Paint(&dc);

5.            重新调整窗口时,我们希望将绘制矩形重置为默认位置和大小(如果移出客户端区域边界,可以让其更容易查找),因此我们添加一个 WM_SIZE 消息处理程序。

6.            切换到 Class View(在 Solution Explorer 窗口底部切换)并选择 CChildView 类。

7.            在 Properties 页面中,转到 Messages 属性表(按   按钮)并导航到 WM_SIZE,然后从下拉框中添加 OnSize() 消息处理程序:

 

8.            在 CChildView::OnSize() 处理程序中(TODO 注释之后),添加以下代码以重置绘制对象:

C++

    m_drawingObject.ResetObject(cx, cy);

9.            编译并运行。

10.          响应有关触控输入可用性的初始消息后,窗口中央应该出现一个红色矩形:

 

               

任务 4 –立即开始触控!

现在可以开始触控了!默认情况下,支持触控的系统会向目标窗口提供 WM_GESTURE 消息。这类似于鼠标和键盘消息。系统使用低级触控输入事件并为我们计算得到的手势。MFC 提供许多重写的消息处理程序,它们可以接受每种手势类型,每个程序都返回一个 Boolean 值。如果应用程序可以处理手势输入,则相应的重写方法应该返回 TRUE;否则返回 FALSE。

1.            让我们继续!将以下 5 个 protected 方法定义添加到 ChildView.h 中:

C++

// Overrides

protected:

               

                // Gesture handlers

                virtual BOOL OnGestureZoom(CPoint ptCenter, long lDelta);

                virtual BOOL OnGesturePan(CPoint ptFrom, CPoint ptTo);

                virtual BOOL OnGestureRotate(CPoint ptCenter, double dblAngle);

                virtual BOOL OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta);

                virtual BOOL OnGestureTwoFingerTap(CPoint ptCenter);

2.            从 Pan Gesture 处理程序处开始,将以下代码添加到 ChildView.cpp 中:

C++

BOOL CChildView::OnGesturePan(CPoint ptFrom, CPoint ptTo)

{

                int dx = ptTo.x - ptFrom.x;

                int dy = ptTo.y - ptFrom.y;

                if (dx != 0 || dy != 0)

                {

                                m_drawingObject.Move(dx, dy);

                                RedrawWindow();

                }

                return true;

}

3.            现在轮到 Zoom Gesture 实现了,将以下代码添加到 ChildView.cpp 中:

C++

BOOL CChildView::OnGestureZoom(CPoint ptCenter, long lDelta)

{

                if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)

                {

                                m_dblZoomRatioStart = m_dblZoomRatioTotal = lDelta;

                }

                else if (lDelta != 0)

                {

                                m_dblZoomRatioTotal += lDelta;

                                double zoomFactor = (double)m_dblZoomRatioTotal / m_dblZoomRatioStart;

                               

                                m_drawingObject.Zoom(zoomFactor, ptCenter.x, ptCenter.y);

                               

                                m_dblZoomRatioStart = m_dblZoomRatioTotal;

                                RedrawWindow();

                }

                return true;

}

4.            将 Rotate Gesture 处理程序添加到 ChildView.cpp 中:

C++

BOOL CChildView::OnGestureRotate(CPoint ptCenter, double dblAngle)

{

                if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)

                {

                                // Make the first center, the rotating one

                                m_ptCenter = ptCenter;

                }

                else if (dblAngle != 0.)

                {

                                m_drawingObject.Rotate(dblAngle * PI / 100.0, m_ptCenter.x, m_ptCenter.y);

                                RedrawWindow();

                }

                return true;

}

5.            类似地,将“按下并点击”和“双指点击”手势的处理程序添加到 ChildView.cpp 中:

C++

BOOL CChildView::OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta)

{

                if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) != 0)

                {

                                m_drawingObject.ShiftColor();

                                RedrawWindow();

                }

                return true;

}

BOOL CChildView::OnGestureTwoFingerTap(CPoint ptCenter)

{

                m_drawingObject.TogleDrawDiagonals();

                RedrawWindow();

                return true;

}

6.            编译并运行。

7.            尝试使用两个手指移动矩形;您可以看到举行会跟随您的手指移动。

8.            尝试在不接触矩形的情况下移动它;我们在窗口的空区域中触摸屏幕。它动了!我们不执行“单击测试”来查看触控位置是否位于矩形边界内部。我们会响应整个客户端区域内的任何手势。

9.            尝试使用其他手势:旋转、放缩、双指点击和双指交替点击。

               

任务 5 –存在一个 Bug!

1.            尝试旋转矩形。发生了什么?默认情况下,窗口接受旋转之外的所有手势。但是,我们可以配置触控引擎来提供我们想要的任何手势。MFC 提供了一个有用的类 CGestureConfig,它支持您自定义窗口手势功能,比如缩放、平移或旋转。

2.            将以下成员变量添加到 ChildView.h 中:

C++

// Fields

protected:

                // Holds gesture configuration

                CGestureConfig m_gestureConfig;

3.       切换到 Class View 并选择 CChildView 类。在 Properties 页面中,转到 Messages 属性表并导航到 WM_CREATE,然后从下拉框中添加 OnCreate() 消息处理程序:

 

4.            在 CChildView::OnCreate() 处理程序中(TODO 注释之后),添加以下代码以接受旋转手势:

C++

    GetGestureConfig(&m_gestureConfig);

    // Only rotation is not enabled by default

    m_gestureConfig.EnableRotate();

    SetGestureConfig(&m_gestureConfig);

5.            编译并运行。

6.            尝试旋转矩形。成功了!干得好!

7.            以下是一个实际的演示屏幕截图,应用了“旋转”、“按下并点击”和“双指点击”这些手势:

 

               

总结

在本实验中,您学习了如何使用触控手势消息来通过 MFC 在屏幕上操作对象。您还学习了如何确定支持多点触控硬件的可用性,以及如何轻松提取手势消息信息并对其采取操作。

祝您实验愉快!