Windows Phone 7 - 淺談手勢(Gestures)運作

Windows Phone 7 - 淺談手勢(Gestures)運作

之前研究WP7時,總是針對應用層去研究,其實還有一塊是要去學習,也就是UI與UX。

然而,UI是要靠本身有點色系、排版等Sense的人,UX則是需要有點工業工程再加點程式設計在裡面的部分,

為什麼會這樣解釋呢?因為UX是用戶實際體驗所得到有經驗,那用戶要驗體的功能則是由程式撰寫而來的。

因此,最常見就是透過手勢的操作,讓整個UX的分數更高,那麼話就不多說,來看看WP7上怎麼做手勢的處理。

在WP7處理Gestures分成Silverlight與XNA二種方案,由於我比較熟悉寫應用程式,所以針對Silverlight進行說明。

 

〉Silverlight Framework – Manipulation Events:

在此Framework中允許開發人員處理手勢經由「manipulation events」。透過它來移動與取得影響範圍,進一步

回應Touch與Multi-Touch Input的事件。另外,UIElement、繼承UIElement的控件均支援手勢操作,包括:tap、double-tap、

hold、pan、flick…等,更方便開發人員使用。然而,Silverlight在處理manipulation events類型主要分成三種:

 

‧ManipulationStarted:

     該事件發生於當用戶將手指放置於螢幕上開始操作的時機點。

     ‧ManipulationStartedEventArgs:提供ManipulationStarted的基本資料,例如:Touch下去的啟動座標。

 

‧ManipulationDelta:該事件發生於當用戶在螢幕上移動手指,並且它會重覆觸生。

     ‧ManipulationDeltaEventArgs:

                    負責ManipulationDelta事件觸發時的主要參數,其中屬性記錄了到目前為止移動的manipulation或

                    當前的manipulation等,協助處理時可以得知目前移動的狀態與類型,在這些屬性裡以「DeltaManipulation」

                    需特別注意,它內部包括很多transform物件。

 

‧ManipulationCompleted:該事件發生於當用戶將手指離開螢幕所觸發。

     ‧ManipulationCompletedEventArgs:提供ManipulationCompleted的事件資料,可取得最後影響的TotalManipulation。

                    其中有三個屬性要特別注意:

                    *FinalVelocities:取得操作的使用速度,它可用於協助判斷移動的量;

                    *IsInterial:用於識別發生ManipulationCompleted是否為一連貫的動作;

                   *TotalManipulation:取得當前操作的所有轉化總合;

 

這三個manipulation events是Windows Phone預設支援的機制,有關手勢的操作事件均以此為基礎。透過下方的範例簡單說明,

透過manipulation來完成一個手勢的功能,最後再補上其他手勢事件的處理機制。

[注意]

在該篇文章所舉用的範例,請透過實機測試才能呈現預期的效果。

 

範例1:透過ManipulationDelta事件,讓指定的物件隨著手指移動而跟著移動。

(a) 建立一個Rectangle,並且設定它的RenderTransform屬性;

   1: <!-- 在XAML檔中,增加需要的Rectangle -->
   2: <!--ContentPanel - place additional content here-->
   3: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
   4:     <Canvas>
   5:         <Rectangle
   6:             Name="rectangle" Width="200" Height="200"
   7:             Fill="Blue" Stroke="Blue" StrokeThickness="1" 
   8:             ManipulationDelta="rectangle_ManipulationDelta" />                
   9:     </Canvas>            
  10: </Grid>

(b) 為Rectangle註冊ManipulationDelta事件,透過ManipluationDelta來移動它的位置;

   1: public MainPage()
   2: {
   3:     InitializeComponent();
   4:     //初始化TransformGroup
   5:     Init_Transform();
   6: }
   7:  
   8: //Transform需要的變數
   9: private TransformGroup transformGroup;
  10: private TranslateTransform translation;
  11:  
  12: private void Init_Transform()
  13: {
  14:     this.transformGroup = new TransformGroup();
  15:     this.translation = new TranslateTransform();
  16:  
  17:     //建立一個新的TransformGroup,並且設定該Group要呈現的影響項目:位置
  18:     this.transformGroup.Children.Add(this.translation);
  19:     //將TransformGroup指定給Rectangle物件
  20:     this.rectangle.RenderTransform = this.transformGroup;
  21: }
  22:  
  23: private void rectangle_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
  24: {
  25:     //透過手指移動的x與y,指定給定義好的Translatefofrm變數,進而影響Rectangle物件
  26:     this.translation.X += e.DeltaManipulation.Translation.X;
  27:     this.translation.Y += e.DeltaManipulation.Translation.Y;
  28: }

以上是簡單的範例,在<How to: Handle Manipulation Events>該篇裡有提到幾個類別是需要注意的,以下簡單說明:

‧TransformGroup:代表一個由多個transform物件組合而成的物件,它的Children屬性用於描述該Group該有的樣子;

‧TranslateTransform:代表一個移動(move)的轉換物件,具有二個維度:x與y座標,呈現於物件移動的位置,使用一指操作;

‧ScaleTransform:代表一個尺度(scale)的轉換物件,具有二個維度:x與y座標,呈現於物件改變的尺度,使用二指操作;

 

 

〉Silverlight Framework - GestureListener:

接著介紹在Silverlight for Windows Phone Toolkit中的一個元件:GestureListener ,該元件具有豐富的手勢處理功能與使用者經驗。

如果未安裝Toolkit的話,可以先下載並安裝它;其GestureListener 已增加了數個蠻好用的Event Handler來處理手勢的事件,如下:

[範例事前準備]

1. 安裝Silverlight for Windows Phone Toolkit;

2. 於MainPage.xaml中,新增一個Recentagle物件,並且加上GestureLisener元件,如下:

   1: <!--ContentPanel - place additional content here-->
   2: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
   3:     <Canvas>
   4:         <!-- 新增一個Rectangle物件,並指定GestureListener處理事件-->
   5:         <Rectangle x:Name="rectangle" Width="200" Height="200"
   6:                    Fill="Blue" Stroke="Blue" StrokeThickness="5">
   7:             <my:GestureService.GestureListener>
   8:                 <my:GestureListener
   9:                     Tap="GestureListener_Tap"
  10:                     Hold="GestureListener_Hold"
  11:                     Flick="GestureListener_Flick"
  12:                     DoubleTap="GestureListener_DoubleTap"
  13:                     GestureBegin="GestureListener_GestureBegin"
  14:                     GestureCompleted="GestureListener_GestureCompleted"
  15:                     DragDelta="GestureListener_DragDelta"
  16:                     DragStarted="GestureListener_DragStarted"
  17:                     DragCompleted="GestureListener_DragCompleted"
  18:                     PinchStarted="GestureListener_PinchStarted"
  19:                     PinchDelta="GestureListener_PinchDelta"
  20:                     PinchCompleted="GestureListener_PinchCompleted">                            
  21:                 </my:GestureListener>
  22:             </my:GestureService.GestureListener>
  23:             <Rectangle.RenderTransform>
  24:                 <TranslateTransform x:Name="translation" />
  25:             </Rectangle.RenderTransform>
  26:         </Rectangle>
  27:     </Canvas>
  28: </Grid>

 

A. Tap (搭配GestureEventArgs):

     事件發生用手指觸控(輕敲)UIElement與離開UIElement。

[範例]

   1: private void GestureListener_Tap(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
   2: {
   3:     //識別觸發Tap的來源者是否為rectangle物件
   4:     if (e.OriginalSource == rectangle)
   5:     {
   6:         //每Tap一點就換色
   7:         Random tRand = new Random();
   8:         rectangle.Fill = new SolidColorBrush(
   9:                             Color.FromArgb(255, (byte)tRand.Next(256),
  10:                                                 (byte)tRand.Next(256),
  11:                                                 (byte)tRand.Next(256)));
  12:     }
  13: }

 

B. DoubleTap (搭配GestureEventArgs):

     事件發生連續快速二次Tap(輕敲)動作於UIElement。

[範例]

   1: private void GestureListener_DoubleTap(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
   2: {
   3:     //在DoubleTap後,移動現在Rectangle回到指定的座標。
   4:     translation.X = translation.Y = 10;
   5: }

 

C. Hold (搭配GestureEventArgs):

     事件發生手指接觸UIElement,並持續停留在同一個位置一段時間。

[範例]

   1: private void GestureListener_Hold(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
   2: {
   3:     //壓著rectangle一段時間,改變顏色與彈出訊息
   4:     if (e.OriginalSource == rectangle)
   5:         rectangle.Fill = new SolidColorBrush(
   6:                             Color.FromArgb(255, 225, 255, 255));
   7:     MessageBox.Show("You are holding the rectangle!", "Hold", MessageBoxButton.OK);
   8: }

 

D. Flick (搭配FlickGestureEventArgs):

     事件發生手指在螢幕上接觸UIElement,並且進行拖動,移動UIElement超過現有的螢幕,且沒有結束,即啟動該事件。

[範例]

   1: private void GestureListener_Flick(object sender, FlickGestureEventArgs e)
   2: {
   3:     if (e.Direction == System.Windows.Controls.Orientation.Horizontal)
   4:     {
   5:         //利用FlickGestureEventArgs的Angle(甩尾的角度)來識別
   6:         if (e.Angle > 270 || e.Angle < 90)
   7:             ChangeFlickTip(true);
   8:         else
   9:             ChangeFlickTip(false);
  10:     }
  11: }
  12:  
  13: private int gCurrentIdx = 1;
  14: private void ChangeFlickTip(bool pType)
  15: {
  16:     if (pType == true)
  17:     {
  18:         //往下一個
  19:         gCurrentIdx += 1;                
  20:     }
  21:     else
  22:     {
  23:         //往上一個
  24:         gCurrentIdx -= 1;
  25:     }
  26:     Dispatcher.BeginInvoke(() =>
  27:     {
  28:         MessageBox.Show(string.Format("Current Idx: {0}", gCurrentIdx));
  29:     });
  30: }

此範例可參考<Building Touch Interfaces for Windows Phones, Part 4>與<Windows Phone 7 Gestures>有更容易理解的範例。

 

E. GestureBegin、GestureCompleted (搭配GestureEventArgs):

     所有手勢事件觸發的啟始點與結束點,不論是Tap、DoubleTap、Flick或Hold等,均會由GestureBegin開始,GestureCompleted結束。

[範例]

   1: #region GestureBegin與GestureCompleted
   2: private void GestureListener_GestureBegin(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
   3: {
   4:     //取得Gesture的啟始點
   5:     var tPosition = e.GetPosition(rectangle);
   6: }
   7:  
   8: private void GestureListener_GestureCompleted(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
   9: {
  10:     //取得Gesture的結束點
  11:     var tPosition = e.GetPosition(rectangle);
  12: }
  13: #endregion

 

F. DragStarted (搭配DragStartedGestureEventArgs)、DragDelta (搭配DragDeltaGestureEventArgs)、

     DragCompleted (搭配DragCompletedGestureEventArgs):

     事件發生於當手指點壓著UIElement(DragStarted),接著進行拖曳一段距離(DragDelta)後,放開手指壓著的UIEelemnt(DragCompleted)。

[範例]

   1: #region Drag系列
   2: private void GestureListener_DragStarted(object sender, DragStartedGestureEventArgs e)
   3: {
   4:     //取得移動方向
   5:     Orientation Orientation = e.Direction;
   6: }
   7:  
   8: private void GestureListener_DragDelta(object sender, DragDeltaGestureEventArgs e)
   9: {
  10:     //隨著拖曳的垂直/水平變動量,移動x與y的值
  11:     this.translation.X += e.HorizontalChange;
  12:     this.translation.Y += e.VerticalChange;
  13: }
  14:  
  15: private void GestureListener_DragCompleted(object sender, DragCompletedGestureEventArgs e)
  16: {
  17:     Orientation Orientation = e.Direction;
  18:     double tHorChange = e.HorizontalChange;
  19:     double tVerChange = e.VerticalChange;
  20: }
  21: #endregion

 

G. PinchStarted(搭配PinchStartedGestureEventArgs)、PinchDelta(搭配PinchGestureEventArgs)、

     PinchCompleted(搭配PinchGestureEventArgs):

     事件發生於二個手指點壓著UIElement(PinchStarted),接著二指拉開拉近的一段距離(PinchDelta)後,放開手指壓著的UIEelemnt(PinchCompleted)。

[範例]

   1: #region Pinch系列
   2: private void GestureListener_PinchStarted(object sender, PinchStartedGestureEventArgs e)
   3: {
   4:    //取得二個手指之間的距離
   5:    double tDistance = e.Distance;
   6:    //取得經由二個手指定義的Angle角度
   7:    double tAngle = e.Angle;
   8: }
   9:  
  10: private void GestureListener_PinchDelta(object sender, PinchGestureEventArgs e)
  11: {
  12:    //透過二指的縮放,調整rectanlge的規模
  13:    scale.ScaleX = e.DistanceRatio;
  14:    scale.ScaleY = e.DistanceRatio;
  15: }
  16:  
  17: private void GestureListener_PinchCompleted(object sender, PinchGestureEventArgs e)
  18: {
  19:    //二個手指放開後,又自動復原
  20:    scale.ScaleX = scale.ScaleY = 1;
  21:    scale.CenterX = scale.CenterY = 0;
  22: }
  23: #endregion

 

 

[範例程式]

======

[補充]

〉Gestures Support in the XNA Framework:

XNA在Gestures的支援是最完整的,並提供更多元的處理機制,以補捉到所有的Gestures事件。

擷錄<Gesture Support in the XNA Framework>的內容,如下表:

Gesture Type Description
Tap 用手指觸控(輕敲)螢幕與離開螢幕。
DoubleTap 該類型代表連續二個Tap手勢。
Hold 手指接觸螢幕,並持續停留在同一個位置一段時間。
FreeDrag 手指觸控螢幕,並往任何方向移動。
VerticalDrag 手指觸控螢幕,並由上往下或由下往下移動。
HorizontalDrag 手指觸控螢幕,並由左往右或由右往左移動。
DragComplete 當FreeDrag、VerticalDrag、HorizontalDrag移動結束後所觸發。
Flick 手指在螢幕上拖動,移動至不同的螢幕,並且沒有結束。
Pinch 運用二個手指按在螢幕上移動。
PinchComplete Pinch手勢動作完成後,觸發的事件。

 

〉FlickGestureEventArgs重點屬性:

Name Description
Angle Flick轉換的角度。
Direction Flick手勢的方向,協助確定Flick的速度。
Handled 如果該屬性被設定為true,它將不會繼續往下一個事件傳送,取消bubbling的流程。
HorizontalVelocity Flick的水平轉換速度。
OriginalSource Flick觸發的來源者,透過Z-Order的方式找到正確的Touch對象。
VerticalVelocity Flick的垂直轉換速度。
TouchPosition 屬於Protected的屬性,用於記錄接觸點的資訊。

 

======

以上是介紹在WP7處理手勢操作的概念與範例,其實手勢操作的體驗,為第二個最直接的讓用戶覺得有意思的地方,

因此,對於資料的分頁,轉場的效果,通常都會透過手勢來加以定義。

 

References:

‧Gesture Support for Windows Phone & Windows Phone 7 Gestures

‧Touch gestures on Windows Phone 7 (必讀)

‧Windows Phone 7 Gestures Cheat Sheet (原理)

‧Handling Input on Windows Phone 7 : Touch Input (part 3) - Multi-Point Touch

‧Drawing in WP7: #2 Drawing shapes with finger & Understanding Windows Phone 7 Gestures

‧Input in Silverlight for Windows Phone & How to: Handle Manipulation Events (二篇都很重要)

‧Input for Windows Phone

‧Designing for Gestures on Windows Phone 7 (教學)

‧Developing Applications for Windows Phone 7 : Designing with Blend

‧Multi-Touch Manipulation Events in WPF (WPF針對Tocuh事件的原理) & Windows Phone 上的触控手势

‧Windows Phone 7 Silverlight Programming - MultiTouch and Manipulation (重要)

‧How to: Specify On-Screen Keyboard Layout for a TextBox

‧Building Touch Interfaces for Windows Phones, Part 4 (必讀)

=== XNA參考文獻 ===

‧Working with Touch Input (Windows Phone)

‧Detecting Gestures on a Multi-touch Screen (Windows Phone)

‧Windows Phone 7 Gestures Compared

Dotblogs Tags: