手勢功能
自 2002 年第一批 Windows Mobile 設備問世以來,觸控螢幕已經成為該設備的代名詞;不過,據稱 Windows Mobile 6.5 才是第一個向開發人員公開所有形式手勢支持的版本。那么什麼是手勢,它又為何如此風靡?
Windows Mobile Professional 設備的傳統觸控螢幕提供的是滑鼠模擬圖面,它通過螢幕驅動程式接口生成滑鼠左鍵和滑鼠移動訊息。在處理和傳送這些訊息時,螢幕和觸筆就像實際的滑鼠一樣,它們之間存在一定的相似性:滑鼠與螢幕上的觸筆一樣,以線性方式產生一系列位置坐標,都可用作非常精確的指針設備。
兩者之間也有一些區別。比如,滑鼠傳送的位置信息與按鈕信息無關,而觸控螢幕總是模擬按下的左鍵,僅在發生螢幕接觸時才傳送位置信息。只要觸控螢幕與滑鼠保持足夠的相似性,此模式就仍然有用武之地。然而,隨著現代話機螢幕的不斷增大,用戶食指已然迅速變成最自然而直觀的觸筆。在個人消費市場上,精度要求和易失性都比較高的觸筆很快就會過時,取而代之的是對粗獷的互動式界面的需求,這種界面會勾起消費者的觸摸衝動,促進與用戶之間的情感聯繫。
與觸筆筆尖的精確度要求形成鮮明的對比,手指活動提供了一種不同的操作模式,它與滑鼠輸入截然不同。輸入數據不再是精確定位的,線性輸入軌跡往往更接近於繞月軌道而非直線。區別不僅在於數據;輸入結果也應產生與輸入序列相符的平滑動態回響。現在,滑鼠模式顯然已不再適用,我們需要一種不同的新模式來幫助描述這種輸入並了解其回響方式。答案就是手勢。
不僅僅是觸摸手勢
在詳細了解觸摸手勢之前,我們先在更寬泛的層面思考一下一般意義上的手勢。手勢可包括許多不同的動作。它可以是計算機螢幕上的手指移動,而且就像揮動手臂或與別人握手一樣,搖頭甚至也算得上是一種手勢(姿勢)。依我看來,只將螢幕輸入視為唯一手勢來源似乎有點狹隘。當今的許多設備都有多種感測器,其中包括觸控螢幕、加速感應器、羅盤、GPS 裝置及照相機等。搖晃、翻轉、鏇轉設備,甚至對著鏡頭微笑都可以解釋為需要軟體進行回響的手勢(姿勢),而這些僅僅是我們目前所知感測器能夠識別的手勢。
Windows Mobile 6.5 中的體系結構在設計上考慮到了這一點,因此將手勢來源及識別過程與該手勢的路由、傳送和回響分開。雖然目前我們只有觸摸手勢識別功能,但一旦具備相應感測器和識別組件,就可以通過系統提供新的手勢。新的感測器和識別軟體可由硬體製造商添加,並集成到現有手勢傳送體系結構中, 圖 3 五種核心手勢
您可能會奇怪為什麼要使用其中的一些手勢,因為滑鼠行為似乎已足以獲取相同的信息。比如,選擇手勢就像單擊按鈕一樣,而平移手勢就像滑鼠移動一樣。這五種手勢的重要性體現在兩個方面。
一致性:系統通過兩個訊息接收滑鼠點擊操作:滑鼠按鈕按下和彈起。識別點擊操作的具體行為是識別該操作的控制項所特有的功能。比如,當滑鼠按鈕按下和彈起的位置都位於視窗邊界內時,按鈕控制項會將這兩個動作識別為一次點擊。相比之下,ListView 控制項對其列表中的每一項也會識別同樣的事件。選擇手勢通過使用一致的參數,使其識別獨立於控制項。用於手勢識別的距離閾值可識別解析度(或者更準確地說,可識別到點/英寸),並且是為使用最廣泛的指形(手指形狀範圍異常廣泛)而設的。因此,可在大小不同的螢幕上使用相同的物理距離,從而在設備之間提供一致性。
路由:手指不是準確指針設備,當用戶在移動或走動中時尤為如此,因此應用程式需要最大化可觸摸目標區域,這一點至關重要。手勢傳送組件藉助特定的規則完成此任務,從而提升了這些簡單手勢的價值。
路由
手勢信息通過新的 WM_GESTURE 訊息進行傳送,而且該訊息與所有視窗訊息一樣,也有包含訊息詳細信息的關聯參數 DWORD wParam 和 LONG lParam。WM_GESTURE 訊息參數包含手勢 ID 作為 wParam 以指示要傳送的手勢,並包含完整手勢信息的句柄作為 lParam。滑鼠訊息總是傳送到滑鼠坐標位置所在的最頂層視窗(不包括滑鼠捕獲情形),但對於手勢來說,這些規則將有所不同。手勢訊息與之不同,它們總是傳送到構成完整手勢序列的第一個觸摸點相應的最頂層視窗。對於螢幕移動距離容差很小的選擇、按住和雙選手勢來說,這一細微差異的影響不大。但對於平移手勢,差別就很大了。在開始平移時,所有平移訊息都會傳送到開始平移時所在的視窗,即使平移移動使觸摸點移出原始視窗也如此。
同樣,滾動手勢的識別位置也會從其原始觸摸點延伸到許多像素的距離。但將滾動訊息路由到與上述平移訊息相同的視窗中比較合理,因為用戶在該原始控制項中啟動輸入序列並打算以其作為目標。鑒於平移手勢通常與直接操控有關(即像在桌面上移動紙張一樣在螢幕上移動內容),因此此路由非常合理,因為在螢幕上移動內容期間,在最初觸摸時指下的控制項或螢幕點應始終保持在指下。
未處理的訊息路由
手勢訊息路由的另一個不同尋常的方面是對未處理手勢訊息所採取的操作。與所有未處理的訊息一樣,它們最終也被傳送到 DefWindowProc 以進行默認處理。當 DefWindowProc 收到手勢訊息時,它會嘗試查找當前視窗的父視窗並將該訊息傳送到該父視窗。這樣做可最大限度地擴大可供用戶使用的可觸摸區域。
為便於解釋,請考慮一個包含一些子標籤控件的可滾動視窗。父視窗實現平移和滾動手勢回響邏輯,從而在可視圖面中上下移動子標籤控制項。不過,這些標籤控制項未發生修改,對手勢支持一無所知。如果用戶手勢碰巧始於觸摸標籤控制項而不是父視窗,該用戶也可獲得相同的結果,也就是窗體將相應地移動以回響輸入移動。通過將未處理的手勢訊息從標籤控制項轉發到父視窗,滿足了用戶的期望,內容移動的效果就像用戶直接觸摸窗體一樣。手勢訊息
Windows Mobile 6.5 可識別五種手勢,而應用程式可接收七種手勢。額外的兩種手勢是在手勢序列開始和結束時傳送的 BEGIN 和 END(所有手勢類型都帶有前綴 GID_ 以指示手勢標識符,因此這兩種手勢的標識符是 GID_BEGIN 和 GID_END)。例如,如果識別選擇手勢,則應用程式會收到三個手勢訊息:GID_BEGIN、GID_SELECT 和 GID_END。對於以滾動手勢為結果的平移序列,應用程式會收到 GID_BEGIN、GID_PAN、GID_PAN…、GID_SCROLL 及最後的 GID_END。
GID_BEGIN 十分有用,因為它包含原始觸摸點的螢幕坐標。GID_END 可方便地指示何時用戶輸入已結束並且不再為當前序列傳送手勢。
為了幫助您了解 Windows Mobile 6.5 中的基本手勢識別和傳送系統,我在附帶的示例 SimpleGestureCapture 中包含了一個 Visual Studio 項目。此示例顯示一個列表框,並為主視窗收到的每個手勢訊息添加一個新行,其中包括所有手勢的位置信息及滾動手勢的角度和速度。您將需要安裝 Visual Studio 2005 或 Visual Studio 2008 以及 Windows Mobile 6 Professional SDK 和 Windows Mobile 6.5 Developer Tool Kit。從該示例中,您可以了解如何接收手勢訊息並提取數據。
物理引擎
在手勢支持中,最令人興奮的部分就是操控螢幕內容時用戶體驗到的自然回響。此回響的關鍵之處是設備間的一致、可預測和自然的體驗。為了實現此一致性,在作業系統中新增了一種名為“物理引擎”的組件。此模組提供一套獲取輸入信息(包括滾動手勢的角度和速度)的數字處理算法,並使用特定減速係數按時間來減慢速度。此外,當輸入速度足以將動畫點移到框線以外時,還可使用物理引擎套用邊界動畫。
若要在 Windows Mobile 6.5 中使用物理引擎,必須首選創建並初始化物理引擎的新實例。這樣,系統會以固定的時間間隔定期對其進行輪詢以檢索當前動畫位置,並且調用應用程式會相應地重繪其客戶端區域。物理引擎會持續減慢動畫速度,直至在低於最小閾值,那時會將動畫標記為完成並可將其釋放。
作為初始化數據的一部分,應用程式必須指定數據空間的框線以及顯示空間的視圖框(見圖 5)。如果視圖框移到框線以外,物理引擎會使用所選邊界動畫(也是初始化數據的一部分)將視圖框重新納入框線之內。物理引擎初始化十分靈活,可以只允許在一個軸上設定動畫,必要時也可為每個軸設定不同的邊界動畫。
圖 5 物理引擎如何處理框線和顯示框
默認情況下,物理引擎根據從初始化時刻到每個位置檢索調用時刻這一期間所獲取的時間差值來減慢速度。調用應用程式可通過指定“user time”值來覆蓋此設定,並讓物理引擎計算該時間點的位置。這對於查找動畫結束螢幕位置十分有用。
項大小是另一個引人注目的物理引擎配置。此信息用於向數據空間強加一個有效停止位置格線,從而強制物理引擎允許視圖的最終位置落於其中某一格線坐標。當應用程式要在螢幕上顯示項列表,而不希望在螢幕頂部顯示不完整的項時,此行為十分有用。該行為適用於任意軸或同時適用於兩個軸,並將調整動畫減速和停止算法以延長或縮短動畫持續時間,從而擊中所需的停止點。
綜合講述
若要應用程式完全支持觸摸手勢,需要對其進行改進,使其識別相應的手勢訊息並做出相應的回響。必要時,需要創建和查詢物理引擎實例以驅動螢幕重繪。此外,應用程式還需要考慮在動畫或手勢序列被進一步的用戶輸入或其他事件中斷時應採用什麼操作,並確保這種情況得到有效處理。雖然所有這一切的實現方式相對而言簡單明確,但需要編寫大量必須為回響手勢的每個視窗而創建的 Boilerplate 代碼。因此,Windows Mobile 6.5 中已採取一些步驟來簡化此任務。
首先,一些內置控制項已更新為支持手勢,包括 LISTVIEW、listbox、TREEVIEW 和 WEBVIEW 控制項(有些模式不支持手勢)。如果您已在使用上述任意控制項,則表示您的應用程式已支持手勢。
對於沒有使用這些內置控制項的應用程式,有一個名為 Window Auto Gesture (WAG) 的新 API 可顯著簡化在最常見方案中實現手勢支持所需的工作。
Window Auto Gesture
WAG 邏輯與 DefWindowProc() 處理緊密結合,提供了可用於任何視窗的默認手勢回響。啟用後,WAG 會自動回響 GID_PAN 和 GID_SCROLL 手勢,創建物理引擎實例並通過通知訊息將相關定位數據傳送回應用程式。此外,WAG 還可以通過在平移或滾動手勢進行時監視輸入佇列來執行手勢中斷,從而提供與動畫狀態之間的適當轉換。
WAG 的默認配置是忽略手勢訊息,因此要使用 WAG 行為的所有視窗都必須先啟用手勢支持。若要啟用手勢支持,應用程式必須為需要該支持的每個視窗調用 TKSetWindowAutoGesture,並傳遞所需的配置設定。如前所述,WAG 旨在簡化最常見的手勢支持方案。因此,若要使 WAG 驅動視窗,則創建該視窗時須在可由觸摸手勢操控的軸中設定 WS_VSCROLL 和/或 VS_HSCROLL 樣式。此外,應用程式須正確管理滾動條,能夠根據需要維護範圍、最小化/最大化和頁面大小。這樣 WAG 才能夠計算視窗所顯示的數據區域大小。
WAG 有幾個選項值得一提:
WAG 可處理 GID_PAN 和 GID_SCROLL 手勢,但可根據需要禁用其中任意手勢。與物理引擎一樣,WAG 也支持設定項的寬度和高度。此信息不僅用於設定捕捉點,而且還會將滾動範圍值從項計數擴展到像素計數。例如,假設一個列表包含 10 項,其滾動條範圍為 0 到 9,每項在垂直方向需要占用 20 像素來顯示其內容,則項的高度應設定為 20。WAG 會將該滾動範圍 (10) 與像素高度 (20) 相乘,從而確定數據的完整像素範圍(200 像素)。WAG 支持一種特殊模式,該模式通過向應用程式生成 WM_xSCROLL 訊息來驅動視窗移動,而不是通過生成較常見的所有者動畫訊息。如果您使用舊式應用程式,並且希望在儘可能少更改代碼的情況下獲得觸摸手勢支持,則此模式十分有用。啟用此模式的方法是:將 nOwnerAnimateMessage 值(TKSetWindowAutoGesture() 初始化數據的一部分)設定為 0 而不是常用的 WM_USER + x 值。在此模式下,有些功能是受限的,如不支持逐像素操控,只能逐項操控控制項。此外,在此模式下不能超出滾動範圍,因此將忽略擴充值。此選項不適契約時在兩個軸上滾動,因為每個軸必須獨立移動。擴充描述可將顯示區域拖到數據範圍之外的距離,表示為顯示大小的百分比。啟用擴充時須小心謹慎,因為這會允許用戶將顯示區域拖動到滾動限制之外,並公開許多現有應用程式都無法正常處理的螢幕區域。當空間顯示在數據範圍頂部或左側以外時,請確保應用程式能夠正確清除螢幕。通常,應用程式會將 WAG 的 nOwnerAnimateMessage 配置為一個 WM_USER 到 WM_APP 範圍的值。每次應用程式需要重繪其顯示區域時,WAG 就會在發回到應用程式的訊息中使用此值。在序列中的首個動畫訊息之前有一個狀態訊息,該訊息指示控制項現在正在回響手勢輸入。WAG 會自動聚合 GID_PAN 手勢訊息,並以每秒 24 次的最大頻率(使用來自 Windows Mobile 6.5 Developer Tool Kit 的 gesturephysics.h 中的 GESTURE_ANIMATION_FRAME_DELAY_MS 計時器持續時間進行管理)只向應用程式傳送動畫訊息。這同樣適用於滾動動畫,WAG 使用相同的計時器來查詢其物理引擎,最大頻率為每秒 24 次。
如果控制項支持焦點或者在沒有用戶互動的情況下發生外觀變化(例如通過異步更新),則 WAG 的狀態訊息選項尤為有用。狀態訊息通知控制項用戶正通過觸摸界面進行互動。此類訊息可用作觸發器,用於暫停執行一些更新,這些更新可能更改控制項或其內容的外觀或者因螢幕動畫而不必要地占用資源。生成全螢幕動畫效果可能占用大量資源,因此應暫停所有不必要的後台處理,並集中資源為用戶提供流暢及時的回響,這是非常重要的。完成觸摸互動之後,可根據需要使用狀態訊息觸發數據刷新和更新。
提示和技巧
使用手勢 API 接受和處理手勢信息的思路十分簡單。不過,要生成回響手勢的流暢動畫可能要花一些心思。下面的提示可能對您有所幫助。
首幀時間至關重要。人眼對用戶界面延遲的敏感程度令人吃驚。例如,如果螢幕觸摸與圖形響應之間的延遲超過 100 毫秒,那么即使應用程式隨後保持穩定的每秒 24 幀的速率 (fps),用戶也會感覺到反映遲緩。應設法確保首幀回響速度足夠快,最好低於 50 毫秒。值得一提的是,手勢識別器和手勢傳送功能的開銷已經過精心最佳化,從觸摸到起效的時間僅為 1 毫秒或 2 毫秒。
首選一致的幀速率。在我們的測試中,用戶寧願選擇稍慢但更一致的幀速率,也不是很快但變化較大的速率。利用此信息,我們創建了一個計時器來調控幀更新頻率,並調整了計時器以確保在每個幀中有一些空閒的 CPU 時間可以處理其他任務。
消除動畫期間的不必要開銷。顯而易見,每幀中的工作量越少,每秒繪製的幀數就越多。不過,有時很難確定具體可以留出哪些工作。在觸摸操控期間,尤其是在滾動動畫期間,用戶不太注意細節,而是關注更醒目的內容。例如,在滾動電子郵件列表期間,用戶可能不太注意每封郵件的預覽,而更關注郵件在列表中的位置及其標題。因此,可以停止更新或檢索預覽文本,以騰出額外的時間保持順暢的動畫。
明智地使用屏外緩衝區。雙緩衝可能是提高繪製性能以及減少螢幕零碎繪製的極佳方法。不過,使用該方法時必須謹慎,因為屏外緩衝區會消耗大量資源。應確保該緩衝區保留儘可能短的時間並保持最小大小。使用 ScrollWindowEx API 通常可以實現類似的結果,而不會產生屏外緩衝區的記憶體開銷。
先度量,再套用適當的改進。標準性能分析做法可確保您修復已發生的問題。因此在更改任何代碼之前,您務必先通過度量了解動畫循環中的開銷在哪裡,然後在對應用程式最有益的方面開展工作。