程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Windows 7開發:多點觸摸之手勢 - 本機(動手實驗)

Windows 7開發:多點觸摸之手勢 - 本機(動手實驗)

編輯:關於.NET

目標

在本動手實驗中,我們將學習如何管理手勢事件,包括:

• 理解使用手勢事件操作對象的含義

• 檢查多點觸摸 硬件是否存在及其就緒情況

• 從手勢 Windows Message 中提取信息

系統要求

要完成本實驗,必須擁有以下工具:

• Microsoft Visual Studio 2008 SP1

• Windows 7

• Windows 7 SDK

• 一台多點觸摸硬件設備

引言

要創建多點觸摸驅動的應用程序,有 3 種方法可供 選擇:“好”、“出色”或“最佳”方法。

“好”方法是這些方法中最簡單的。設計應用程序用戶界面時 應該將觸摸能力考慮在內。可以使用大量基於 Win32 的簡單工具構建一種自然的 界面,以提供更出色的用戶體驗。滾動等觸摸能力來自於 Win32 控件,無需額外 的工作。例如,現在嘗試使用手指滾動您正在閱讀的文檔!這就是“好 ”方法。

“最佳”方法是讀取低級觸摸事件,將其作為 應用程序的輸入。“Piano”等應用程序或可供用戶同時操作的多個滑 塊等復雜控件都是不錯的例子。運行 MS Paint,從工具箱中選擇一個繪制工具, 然後使用您的 4 根手指進行繪制(如果硬件支持):

在本動手實驗中,我們將使用“出色”方法。“出色” 方法是為應用程序獲取觸摸事件的最簡單方式,可用於自定義縮放、旋轉和平移 等操作,無需讀取和操作原始的觸摸事件。讓我們來立即體驗多點觸摸手勢!

關於 Multitouch Gesture 應用程序

Multitouch Gesture 應用程 序提供了一個簡單的窗口,其中包含一個矩形。在 HOL 文件夾中,可以找到本實 驗的每項任務所對應的項目。Starter 文件夾包含實驗需要的文件。本實驗的完 成版本位於 Final 文件夾中。

此應用程序通過與一個著色的矩形交互來響應多點觸摸手勢輸入。該矩 形能夠響應以下手勢:

平移

要平移圖像,將兩個手指放在應用程 序窗口中並朝著希望移動的方向拖動。確保在兩個手指之間保留少量空間,以便 觸摸界面能夠將它們看作獨立的接觸點。

旋轉

使用兩個手指觸摸矩形,然後旋轉手指形成一個圓。

縮放

使用兩個手指觸摸矩形,然後讓兩個手指相互靠近或遠離 。

雙指點擊

使用兩個手指同時點擊一次,可以在紅色矩形中顯示 或隱藏對角線。

雙指交替點擊

首先使用一個手指按住矩形,然後使用另一個手 指點擊矩形,再移開第一個手指,這樣可以更改矩形的顏色。

練習 #1 :開發多點觸摸應用程序

任務 1 – 創建 Win32 應用程序

1.啟動 Visual Studio 2008 SP1

2.選擇一個新的 C++ Win32 應用程序項目:

3.編譯並運行

4.我們將使用屬於 Windows 7 的 API 和宏。

a. 在 targetver.h 頭文件中將 WINVER 和 _WIN32_WINNT 定義更改為 0x0601

C++

#ifndef WINVER //Specifies that the  minimum required platform is Windows 7
#define WINVER  0x0601         
#endif

#ifndef _WIN32_WINNT  //Specifies that the minimum required platform is Win 7
#define _WIN32_WINNT 0x0601   
#endif

5.編譯並 運行。

任務 2 – 檢查多點觸摸硬件是否存在及其就緒情況

1.我們將開發的應用程序需要一個啟用了觸摸功能的設備。在調用 _tWinMain() 中的 InitInstance() 之前,添加以下代碼來檢查硬件的觸摸功能 及就緒情況:

C++

BYTE digitizerStatus = (BYTE) GetSystemMetrics(SM_DIGITIZER);
if ((digitizerStatus &  (0x80 + 0x40)) == 0) //Stack Ready + MultiTouch
{
   MessageBox(0, L"No touch support is currently available",  L"Error", MB_OK);
  return 1;
}

BYTE nInputs  = (BYTE)GetSystemMetrics(SM_MAXIMUMTOUCHES);
wsprintf(szTitle,  L"%s - %d touch inputs", szTitle, nInputs);

2.可以 看到,除了檢查觸摸功能可用性和就緒情況,我們還會找到硬件支持的觸摸輸入 數量。

3.編譯並運行

任務 3 – 將繪制對象源和頭文件添加到項目,然後繪制矩形

在 Starter 文件夾中可以找到兩個文件:DrawingObject.h 和 DrawingObject.cpp 。將它們復制到項目文件夾,使用“Add Existing item…”將它們添加到項目中。

1.在 mtGesture.cpp 文件開頭添加一行:#include DrawingObject.h

C++

#include  "DrawingObject.h"

2.在 mtGesture.cpp 文件開頭的 //Global Variables: 部分添加一個全局變量定義:

C++

CDrawingObject g_drawingObject;

3.將以下行添加到 WndProc 中,注意 WM_already PAINT 已經由應用程序向導創建:

C++

                 case WM_SIZE:
                                 g_drawingObject.ResetObject(LOWORD(lParam),  HIWORD(lParam));
                                 break;

                 case WM_PAINT:
                                 hdc = BeginPaint(hWnd, &ps);
                                 g_drawingObject.Paint (hdc);
                                 EndPaint(hWnd, &ps);
                                 break;

4.編譯並運行

5.更改 Windows 的大小以觸發發送給應用程序的 WM- Paint 消息。窗口中央應該出現一個紅色矩形:

任務 4 – 立即開始觸摸!

可以開始使用應用程序了。默認情況 下,啟用了觸摸功能的系統會向目標窗口提供 WM_GESTURE 消息。這有點類似於 鼠標和鍵盤消息。系統使用低級觸摸輸入事件並計算生成的手勢。使用 lParam 參數作為句柄來提取手勢信息。GetGestureInfo() API 獲取 lParam 手勢句柄和 GESTUREINFO 結構變量的一個地址:

C++(摘自 WinUser.h)

typedef struct tagGESTUREINFO {
    UINT  cbSize;     // size, in bytes, (including variable length  Args field)
    DWORD dwFlags;   // see GF_* flags
    DWORD dwID;      // gesture ID, see GID_*  defines
    HWND hwndTarget; // handle to window  targeted by this gesture
    POINTS ptsLocation;  //  current location of this gesture
    DWORD dwInstanceID;      // internally used
    DWORD dwSequenceID;      // internally used
    ULONGLONG ullArguments; //  arguments for gestures (8 BYTES)
    UINT cbExtraArgs;  // size, of extra arguments, that accompany this gesture
} GESTUREINFO, *PGESTUREINFO;
typedef GESTUREINFO const *  PCGESTUREINFO;

/*
 * Gesture flags -  GESTUREINFO.dwFlags
 */
#define GF_BEGIN                         0x00000001
#define GF_INERTIA                       0x00000002
#define GF_END                           0x00000004

/*
 * Gesture IDs
 */
#define GID_BEGIN                        1
#define GID_END                          2
#define GID_ZOOM                         3
#define GID_PAN                          4
#define GID_ROTATE                       5
#define  GID_TWOFINGERTAP                6
#define  GID_PRESSANDTAP                 7
#define  GID_ROLLOVER                     GID_PRESSANDTAP

使用了通過調用 GetGestureInfo() 獲得的信息之後,我們必須調用 CloseGestureInfoHandle() 來釋放系統分配的內存塊。

當響應手勢消息 時,dwFlags 和 dwID 兩個字段非常重要。dwID 告訴我們用戶執行了何種手勢: 縮放、平移、旋轉等。dwFlags 告訴我們這是它第一次通知我們該手勢,還是最 後一次,或者是否用戶已從屏幕移開了手指,但慣性引擎仍在發出手勢消息。有 許多簡單手勢(比如“雙指點擊”),應用程序對它們只需響應一次 ;其他手勢稍微復雜一些,因為它們在用戶操作期間發送了許多消息。對於這類 手勢(縮放、旋轉和平移),我們需要根據不同的條件采取不同的響應方式。對 於平移和縮放,我們不會對一系列手勢中的第一個采取任何操作。表示旋轉開始 的消息附帶一個旋轉角度,我們需要根據該角度來旋轉繪制對象。只要一個手勢 不是一系列手勢中的第一個,我們就要計算上一個參數與新參數之間的差值,並 提取縮放系數、平移范圍或相對旋轉角度。通過這種方式,我們可以在用戶使用 手指觸摸屏幕並移動手指時更新應用程序。

1.讓我們來移動對象!將以下 代碼添加到 WndProc 中:

C++

                 case WM_GESTURE:
                                 return ProcessGestureMessage(hWnd, wParam,  lParam);

2.將以下函數添加到 WndProc 之前:

C++

LRESULT ProcessGestureMessage(HWND hWnd,  WPARAM wParam, LPARAM lParam)
{
    static POINT  lastPoint;
    static ULONGLONG lastArguments;

     GESTUREINFO gi;

    gi.cbSize = sizeof (GESTUREINFO);
    gi.dwFlags       = 0;
     gi.ptsLocation.x = 0;
    gi.ptsLocation.y = 0;
     gi.dwID          = 0;
    gi.dwInstanceID  =  0;
    gi.dwSequenceID  = 0;

    BOOL  bResult = GetGestureInfo((HGESTUREINFO)lParam, &gi);

                switch(gi.dwID)
                 {
                   case  GID_PAN:
                                 if ((gi.dwFlags & GF_BEGIN) == 0) //not the first  message
                                 {
                                                 g_drawingObject.Move (gi.ptsLocation.x - lastPoint.x,
                                                                                                    gi.ptsLocation.y - lastPoint.y);
                                                  InvalidateRect(hWnd, NULL, TRUE);
                                 }
                 }

                 //Remember last values for delta calculations
                 lastPoint.x = gi.ptsLocation.x;
                 lastPoint.y = gi.ptsLocation.y;
                 lastArguments = gi.ullArguments;

                 CloseGestureInfoHandle((HGESTUREINFO) lParam);
                return 0;
}

3.編譯並運行

4.嘗試使用兩個手指移動對象,可以看到,對象在跟隨 手指移動。您應該了解以下事實:

a.觸摸位置由屏幕坐標確定。我們感興 趣的是偏移量,所以我們並不關心精確的位置坐標,如果我們需要了解精確位置 來進行調整,就要調用 ScreenToClient()。

b.嘗試在不觸摸對象的情況 下移動它,觸摸窗口的空白區域。它移動了!我們沒有執行“點擊測試 ”來檢查觸摸位置是否在對象內部。執行點擊測試需要窗口中的位置坐標。

5.完成應用程序並響應所有手勢 ID:

C++

LRESULT  ProcessGestureMessage(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    static POINT lastPoint;
    static  ULONGLONG lastArguments;

    GESTUREINFO gi;

    gi.cbSize = sizeof(GESTUREINFO);
    gi.dwFlags        = 0;
    gi.ptsLocation.x = 0;
     gi.ptsLocation.y = 0;
    gi.dwID          =  0;
    gi.dwInstanceID  = 0;
    gi.dwSequenceID   = 0;

    BOOL bResult = GetGestureInfo ((HGESTUREINFO)lParam, &gi);

    switch(gi.dwID)
    {
        case GID_PAN:
             if ((gi.dwFlags & GF_BEGIN) == 0) //not the first  message
            {
                 g_drawingObject.Move(gi.ptsLocation.x - lastPoint.x,
                                       gi.ptsLocation.y - lastPoint.y);
                 // repaint the rect
                 InvalidateRect(hWnd, NULL, TRUE);
            }
            break;

        case  GID_ZOOM:
            //not the first message
            if ((gi.dwFlags & GF_BEGIN) == 0  && lastArguments != 0)
            {
                double zoomFactor = (double) gi.ullArguments/lastArguments;

                 // Now we process zooming in/out of object
                 g_drawingObject.Zoom(zoomFactor, gi.ptsLocation.x,  gi.ptsLocation.y);

                 InvalidateRect(hWnd,NULL,TRUE);
            }
             break;

        case  GID_ROTATE:
            {
                 //The angle is the rotation delta from the last  message, 
                //or from 0 if it  is a new message
                ULONGLONG  lastAngle = ((gi.dwFlags & GF_BEGIN) != 0) ? 0 :  lastArguments
                int angle =  (int)(gi.ullArguments – lastAngle);

                 g_drawingObject.Rotate(
                                 GID_ROTATE_ANGLE_FROM_ARGUMENT (angle), 
                                                                                  gi.ptsLocation.x,  gi.ptsLocation.y);
                InvalidateRect  (hWnd, NULL, TRUE);
            }
             break;

        case  GID_PRESSANDTAP:
             g_drawingObject.ShiftColor();
             InvalidateRect(hWnd, NULL, TRUE);
             break;

        case GID_TWOFINGERTAP:
             g_drawingObject.TogleDrawDiagonals();
             InvalidateRect(hWnd, NULL, TRUE);
             break;
                }

                 //Remember last values for delta  calculations
                lastPoint.x =  gi.ptsLocation.x;
                lastPoint.y =  gi.ptsLocation.y;
                lastArguments =  gi.ullArguments;

                 CloseGestureInfoHandle((HGESTUREINFO)lParam);
                 return 0;
}

6.編譯並運行

7.測試所 有手勢。

任務 5 – 存在一個 Bug !

1.嘗試旋轉對象。結 果如何?根據定義,窗口將接收除旋轉之外的所有手勢。我們可以配置觸摸引擎 來僅提供可用的手勢或所有手勢。

2.將以下代碼添加到 InitInstance() 函數中的 ShowWindow() 調用之前,以啟用包括 GID_ROTATE 在內的所有手勢類 型:

C++

   //Enable all gesture types
    GESTURECONFIG gestureConfig;
   gestureConfig.dwID =  0;
   gestureConfig.dwBlock = 0;
    gestureConfig.dwWant = GC_ALLGESTURES;

BOOL result =  SetGestureConfig(hWnd, 0, 1, &gestureConfig,sizeof(gestureConfig));

3.編譯並運行

4.嘗試旋轉對象。大功告成!

小結

在本實驗中,我們學習 了如何使用觸摸手勢消息來操作屏幕上的對象。您了解了如何檢查是否存在多點 觸摸硬件;如何配置一個窗口來獲得想要的手勢事件;以及如何提取和操作手勢 參數。

祝您實驗愉快!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved