Windows phone 8 的应用 与一般的Pc应用在输入方式上最大的不同就是:Windows phone 8主要依靠触控操作。因此在输入方式上引入一套全新的触控操作方式,我们需要重新定义相关的事件和方法。触控覆盖了Windows phone 8绝大部分用户的输入,如何处理输入呢,微软从SL和XNA两个方面提供了多种选择,并支持多点触控,下面我们看看具体的实现方式。
1)操作事件
用于触控操作是一个过程性的,因此通过三个事件ManipulationStarted、ManipulationDelta、ManipulationCompleted来控制触控的整个过程。他们分别为:
ManipulationStarted: 分别标识了触控开始时发生
ManipulationDelta: 触控过程中位置变更时发生
ManipulationCompleted: 触控结束时发生
2)读取手势
手势是将触控输入数据解释为一组常用动作(如点按、轻拂和捏合)的高级方式。XNA中为我们封装了一些常用的基本手势操作,下表描述了 Windows Phone 中的一些常用笔势。
点按 手指触摸屏幕,然后释放。
连按 手指点按屏幕两次,然后松开。
长按 手指触摸屏幕,并保持短暂停留。
拖动 手指触摸屏幕,并向任何方向移动。
轻拂 手指在屏幕上滑动,且不停止即抬起。
捏合 两个手指在屏幕上点按,并移动。
在各个事件中,我们可以获取不同的坐标偏移位置信息以及缩放大小信息,这样我们可以通过编程方便的控制,由于API提供的各类偏移缩放名称太多,为了方便理解,首先,我们通过图解说明相关的名称概念。见下图,我们实现了一个应用,功能是把一个矩形控件通过触碰操作从虚线所示位置平移到另一位置,黄色轨迹代表拖动的路线:
然后我们看看三个事件,各个事件中分别可以获取一些什么信息,首先,我们要定义一个矩形,然后定义他的CompositeTransform位置缩放转换。另外我们要定义一个颜色刷,当触控操作开始时改变矩形的颜色,结束时恢复颜色。我们需要一些准备信息,然后我们需要在构造方法中初始化作用于矩形的三个触控事件。新建工程,添加如下代码:
[XAML]
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Canvas Margin="0,-58,0,58"> <Rectangle x:Name="rectangle" Width="200" Height="200" Fill="Blue" Stroke="Blue" StrokeThickness="0" RenderTransformOrigin="0.5,0.5" Canvas.Top="37" > </Rectangle> </Canvas> </Grid>
[C#]
public partial class MainPage : PhoneApplicationPage { private CompositeTransform compositeTransform = new CompositeTransform(); private Brush startBgColor; private Brush bgColor = new SolidColorBrush(Colors.Orange); //触碰初始被触碰控件本身平移量 double startX; double startY; //初次触碰点相对于被拖动控件基准点的坐标 double firstTranslateX; double firstTranslateY; public MainPage() { InitializeComponent(); rectangle.RenderTransform = compositeTransform; //触控开始时发生 rectangle.ManipulationStarted += new EventHandler<ManipulationStartedEventArgs>(Rectangle_ManipulationStarted); //触控过程中位置变更时发生 rectangle.ManipulationDelta += new EventHandler<ManipulationDeltaEventArgs>(Rectangle_ManipulationDelta); //触控结束时发生 rectangle.ManipulationCompleted += new EventHandler<ManipulationCompletedEventArgs>(Rectangle_ManipulationCompleted); } private string ConvertString(double value) { return (Math.Round(value * 100) / 100).ToString(); } private string ConvertString(Point value) { return "{" + (Math.Round(value.X * 100) / 100).ToString() + "," + (Math.Round(value.Y * 100) / 100).ToString() + "}"; } }
void Rectangle_ManipulationStarted(object sender, ManipulationStartedEventArgs e) { startBgColor = rectangle.Fill; (e.ManipulationContainer as Rectangle).Fill = bgColor; //这两个值效果一样,都是指被触控控件本身 //(e.OriginalSource as Rectangle).Fill = endBgColor; //触碰初始被触碰控件本身平移量 startX = compositeTransform.TranslateX; startY = compositeTransform.TranslateY; //初次触碰点相对于被拖动控件基准点的坐标 firstTranslateX = e.ManipulationOrigin.X; firstTranslateY = e.ManipulationOrigin.Y; }
void Rectangle_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) { //当前触碰点相对于被触控控件基准点的坐标 ox.Text = ConvertString(e.ManipulationOrigin.X); oy.Text = ConvertString(e.ManipulationOrigin.Y); //当前触碰点相对于上一触碰点的坐标偏移 dx.Text = ConvertString(e.DeltaManipulation.Translation.X); dy.Text = ConvertString(e.DeltaManipulation.Translation.Y); //当前触碰点相对于初次触碰点的坐标偏移 tx.Text = ConvertString(e.CumulativeManipulation.Translation.X); ty.Text = ConvertString(e.CumulativeManipulation.Translation.Y); //控件跟随移动 //compositeTransform.TranslateX += e.DeltaManipulation.Translation.X; //compositeTransform.TranslateY += e.DeltaManipulation.Translation.Y; //实时精准定位移动,不考虑连贯性 compositeTransform.TranslateX += e.DeltaManipulation.Translation.X + e.ManipulationOrigin.X - firstTranslateX; compositeTransform.TranslateY += e.DeltaManipulation.Translation.Y + e.ManipulationOrigin.Y - firstTranslateY; //相对于上次的缩放量 //if (e.DeltaManipulation.Scale.X > 0 && e.DeltaManipulation.Scale.Y > 0) //{ // compositeTransform.ScaleX *= e.DeltaManipulation.Scale.X; // compositeTransform.ScaleY *= e.DeltaManipulation.Scale.Y; //} if (e.PinchManipulation != null) { //初次主次触控点相对于被触控控件基准点的坐标 pa.Text = ConvertString(e.PinchManipulation.Original.PrimaryContact) + " " + ConvertString(e.PinchManipulation.Original.SecondaryContact) + " " + ConvertString(e.PinchManipulation.Original.Center); //当前主次触控点相对于被触控控件基准点的坐标 pb.Text = ConvertString(e.PinchManipulation.Current.PrimaryContact) + " " + ConvertString(e.PinchManipulation.Current.SecondaryContact) + " " + ConvertString(e.PinchManipulation.Current.Center); //当前主次触控点间距÷初次主次触碰点间距 pc.Text = ConvertString(e.PinchManipulation.CumulativeScale); //当前主次触控点间距÷上一主次触碰点间距 pd.Text = ConvertString(e.PinchManipulation.DeltaScale); } }
void Rectangle_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) { rectangle.Fill = startBgColor; }
在XNA中,我们可以通过TouchPanel获取基本手势信息,手势必须被启用才能够识别。因此我们要在构造函数中定义要启用的手势。
[C#]
public MainPage() { InitializeComponent(); rectangle.RenderTransform = compositeTransform; //设置可识别的手势,必须设置才能被启用 TouchPanel.EnabledGestures = GestureType.Hold | GestureType.Tap | GestureType.DoubleTap | GestureType.Flick | GestureType.Pinch; }
这个时候,我们可以在触控结束时,判断当前触控的手势类型,另外,在触控进行的过程中,我们可以实时获取当前每个触控点的相关状态信息,我们还可以通过TouchPanel识别当前设备对触控的支持性。
[C#]
protected override void OnNavigatedTo(NavigationEventArgs e) { //获取设备对触控的支持性 var a = TouchPanel.GetCapabilities(); //当前设备是否支持触控 if (a.IsConnected) { //当前设备支持几个触控点 CapabilitiesText.Text = "当前设备支持[ "+a.MaximumTouchCount+" ]点触控"; } base.OnNavigatedTo(e); } void Rectangle_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) { //全部触控点的绝对坐标和状态 TouchCollection touchCollection = TouchPanel.GetState(); string stateString = string.Empty; foreach (var item in touchCollection) { stateString += " " + item.Position.ToString() + "[" + item.State.ToString() + "]"; } TouchCollectionText.Text = stateString; } void Rectangle_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) { rectangle.Fill = startBgColor; //当手势可用时执行 while (TouchPanel.IsGestureAvailable) { GestureSample gesture = TouchPanel.ReadGesture(); //获取当前的识别的手势类型 p.Text = gesture.GestureType.ToString(); } }
<phone:PhoneApplicationPage x:Class="PhoneApp1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" shell:SystemTray.IsVisible="True"> <!--LayoutRoot 是包含所有页面内容的根网格--> <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitlePanel 包含应用程序的名称和页标题--> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"> <Run Text="触控"/> <Run Text="输入"/> </TextBlock> </StackPanel> <!--ContentPanel - 在此处放置其他内容--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Canvas Margin="0,-58,0,58"> <Rectangle x:Name="rectangle" Width="200" Height="200" Fill="Blue" Stroke="Blue" StrokeThickness="0" RenderTransformOrigin="0.5,0.5" Canvas.Top="37" > </Rectangle> </Canvas> </Grid> <TextBlock x:Name="CapabilitiesText" FontSize="16" Foreground="YellowGreen" HorizontalAlignment="Left" Margin="10,205,0,0" Grid.Row="1" TextWrapping="Wrap" Text="当前设备不支持触控" VerticalAlignment="Top"/> <TextBlock Foreground="Red" HorizontalAlignment="Left" Margin="274,205,0,0" Grid.Row="1" TextWrapping="Wrap" Text="手势:" VerticalAlignment="Top"/> <TextBlock Text="None" x:Name="p" Foreground="Red" HorizontalAlignment="Left" Margin="323,205,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock Text="无触控点" x:Name="TouchCollectionText" Foreground="Coral" HorizontalAlignment="Left" Margin="10,255,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock FontSize="16" Text="全部触控点的绝对坐标和状态" Foreground="Coral" HorizontalAlignment="Left" Margin="10,235,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock Foreground="White" HorizontalAlignment="Left" Margin="10,343,0,0" Grid.Row="1" TextWrapping="Wrap" Text="X:" VerticalAlignment="Top"/> <TextBlock FontSize="16" Foreground="White" HorizontalAlignment="Left" Margin="10,323,0,0" Grid.Row="1" TextWrapping="Wrap" Text="当前触碰点相对于被触控控件基准点的坐标:" VerticalAlignment="Top"/> <TextBlock Foreground="White" HorizontalAlignment="Left" Margin="97,343,0,0" Grid.Row="1" TextWrapping="Wrap" Text="Y:" VerticalAlignment="Top"/> <TextBlock Text="0" x:Name="ox" Foreground="White" HorizontalAlignment="Left" Margin="29,343,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock Text="0" x:Name="oy" Foreground="White" HorizontalAlignment="Left" Margin="115,343,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock Foreground="Yellow" HorizontalAlignment="Left" Margin="10,393,0,0" Grid.Row="1" TextWrapping="Wrap" Text="X:" VerticalAlignment="Top"/> <TextBlock FontSize="16" Foreground="Yellow" HorizontalAlignment="Left" Margin="10,374,0,0" Grid.Row="1" TextWrapping="Wrap" Text="当前触碰点相对于上一触碰点的坐标偏移:" VerticalAlignment="Top"/> <TextBlock Foreground="Yellow" HorizontalAlignment="Left" Margin="97,393,0,0" Grid.Row="1" TextWrapping="Wrap" Text="Y:" VerticalAlignment="Top"/> <TextBlock Text="0" x:Name="dx" Foreground="Yellow" HorizontalAlignment="Left" Margin="29,393,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock Text="0" x:Name="dy" Foreground="Yellow" HorizontalAlignment="Left" Margin="115,393,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock Foreground="Red" HorizontalAlignment="Left" Margin="10,440,0,0" Grid.Row="1" TextWrapping="Wrap" Text="X:" VerticalAlignment="Top"/> <TextBlock FontSize="16" Foreground="Red" HorizontalAlignment="Left" Margin="10,424,0,0" Grid.Row="1" TextWrapping="Wrap" Text="当前触碰点相对于初次触碰点的坐标偏移:" VerticalAlignment="Top"/> <TextBlock Foreground="Red" HorizontalAlignment="Left" Margin="97,440,0,0" Grid.Row="1" TextWrapping="Wrap" Text="Y:" VerticalAlignment="Top"/> <TextBlock Text="0" x:Name="tx" Foreground="Red" HorizontalAlignment="Left" Margin="29,440,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock Text="0" x:Name="ty" Foreground="Red" HorizontalAlignment="Left" Margin="115,440,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock FontSize="16" Foreground="DarkKhaki" HorizontalAlignment="Left" Margin="10,489,0,0" Grid.Row="1" TextWrapping="Wrap" Text="初次主次触控点相对于被触控控件基准点的坐标:" VerticalAlignment="Top"/> <TextBlock Text="Null" x:Name="pa" Foreground="DarkKhaki" HorizontalAlignment="Left" Margin="10,511,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock FontSize="16" Foreground="Fuchsia" HorizontalAlignment="Left" Margin="10,535,0,0" Grid.Row="1" TextWrapping="Wrap" Text="初次主次触控点相对于被触控控件基准点的坐标:" VerticalAlignment="Top"/> <TextBlock Text="Null" x:Name="pb" Foreground="Fuchsia" HorizontalAlignment="Left" Margin="10,553,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock FontSize="16" Foreground="CadetBlue" HorizontalAlignment="Left" Margin="10,583,0,0" Grid.Row="1" TextWrapping="Wrap" Text="当前主次触控点间距÷初次主次触碰点间距:" VerticalAlignment="Top" /> <TextBlock Text="Null" x:Name="pc" Foreground="CadetBlue" HorizontalAlignment="Left" Margin="10,596,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> <TextBlock FontSize="16" Foreground="Aqua" HorizontalAlignment="Left" Margin="10,625,0,0" Grid.Row="1" TextWrapping="Wrap" Text="当前主次触控点间距÷初次主次触碰点间距:" VerticalAlignment="Top" /> <TextBlock Text="Null" x:Name="pd" Foreground="Aqua" HorizontalAlignment="Left" Margin="10,642,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"/> </Grid> </phone:PhoneApplicationPage>
[C#]
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; using PhoneApp1.Resources; using System.Windows.Media; using System.Windows.Input; using System.Windows.Shapes; using Microsoft.Xna.Framework.Input.Touch; namespace PhoneApp1 { public partial class MainPage : PhoneApplicationPage { private CompositeTransform compositeTransform = new CompositeTransform(); private Brush startBgColor; private Brush bgColor = new SolidColorBrush(Colors.Orange); //触碰初始被触碰控件本身平移量 double startX; double startY; //初次触碰点相对于被拖动控件基准点的坐标 double firstTranslateX; double firstTranslateY; public MainPage() { InitializeComponent(); rectangle.RenderTransform = compositeTransform; //设置可识别的手势,必须设置才能被启用 TouchPanel.EnabledGestures = GestureType.Hold | GestureType.Tap | GestureType.DoubleTap | GestureType.Flick | GestureType.Pinch; //触控开始时发生 rectangle.ManipulationStarted += new EventHandler<ManipulationStartedEventArgs>(Rectangle_ManipulationStarted); //触控过程中位置变更时发生 rectangle.ManipulationDelta += new EventHandler<ManipulationDeltaEventArgs>(Rectangle_ManipulationDelta); //触控结束时发生 rectangle.ManipulationCompleted += new EventHandler<ManipulationCompletedEventArgs>(Rectangle_ManipulationCompleted); } protected override void OnNavigatedTo(NavigationEventArgs e) { //获取设备对触控的支持性 var a = TouchPanel.GetCapabilities(); //当前设备是否支持触控 if (a.IsConnected) { //当前设备支持几个触控点 CapabilitiesText.Text = "当前设备支持[ "+a.MaximumTouchCount+" ]点触控"; } base.OnNavigatedTo(e); } void Rectangle_ManipulationStarted(object sender, ManipulationStartedEventArgs e) { startBgColor = rectangle.Fill; (e.ManipulationContainer as Rectangle).Fill = bgColor; //这两个值效果一样,都是指被触控控件本身 //(e.OriginalSource as Rectangle).Fill = endBgColor; //触碰初始被触碰控件本身平移量 startX = compositeTransform.TranslateX; startY = compositeTransform.TranslateY; //初次触碰点相对于被拖动控件基准点的坐标 firstTranslateX = e.ManipulationOrigin.X; firstTranslateY = e.ManipulationOrigin.Y; } void Rectangle_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) { //当前触碰点相对于被触控控件基准点的坐标 ox.Text = ConvertString(e.ManipulationOrigin.X); oy.Text = ConvertString(e.ManipulationOrigin.Y); //当前触碰点相对于上一触碰点的坐标偏移 dx.Text = ConvertString(e.DeltaManipulation.Translation.X); dy.Text = ConvertString(e.DeltaManipulation.Translation.Y); //当前触碰点相对于初次触碰点的坐标偏移 tx.Text = ConvertString(e.CumulativeManipulation.Translation.X); ty.Text = ConvertString(e.CumulativeManipulation.Translation.Y); //控件跟随移动 //compositeTransform.TranslateX += e.DeltaManipulation.Translation.X; //compositeTransform.TranslateY += e.DeltaManipulation.Translation.Y; //实时精准定位移动,不考虑连贯性 //compositeTransform.TranslateX += e.DeltaManipulation.Translation.X + e.ManipulationOrigin.X - firstTranslateX; //compositeTransform.TranslateY += e.DeltaManipulation.Translation.Y + e.ManipulationOrigin.Y - firstTranslateY; //相对于上次的缩放量 //if (e.DeltaManipulation.Scale.X > 0 && e.DeltaManipulation.Scale.Y > 0) //{ // compositeTransform.ScaleX *= e.DeltaManipulation.Scale.X; // compositeTransform.ScaleY *= e.DeltaManipulation.Scale.Y; //} if (e.PinchManipulation != null) { //初次主次触控点相对于被触控控件基准点的坐标 pa.Text = ConvertString(e.PinchManipulation.Original.PrimaryContact) + " " + ConvertString(e.PinchManipulation.Original.SecondaryContact) + " " + ConvertString(e.PinchManipulation.Original.Center); //当前主次触控点相对于被触控控件基准点的坐标 pb.Text = ConvertString(e.PinchManipulation.Current.PrimaryContact) + " " + ConvertString(e.PinchManipulation.Current.SecondaryContact) + " " + ConvertString(e.PinchManipulation.Current.Center); //当前主次触控点间距÷初次主次触碰点间距 pc.Text = ConvertString(e.PinchManipulation.CumulativeScale); //当前主次触控点间距÷上一主次触碰点间距 pd.Text = ConvertString(e.PinchManipulation.DeltaScale); } //全部触控点的绝对坐标和状态 TouchCollection touchCollection = TouchPanel.GetState(); string stateString = string.Empty; foreach (var item in touchCollection) { stateString += " " + item.Position.ToString() + "[" + item.State.ToString() + "]"; } TouchCollectionText.Text = stateString; } void Rectangle_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) { rectangle.Fill = startBgColor; //当手势可用时执行 while (TouchPanel.IsGestureAvailable) { GestureSample gesture = TouchPanel.ReadGesture(); //获取当前的识别的手势类型 p.Text = gesture.GestureType.ToString(); } } private string ConvertString(double value) { return (Math.Round(value * 100) / 100).ToString(); } private string ConvertString(Point value) { return "{" + (Math.Round(value.X * 100) / 100).ToString() + "," + (Math.Round(value.Y * 100) / 100).ToString() + "}"; } } }
最后,我啰嗦一下,有些朋友没有WP8手机,但是这里却要用到多点触控,可是WP8模拟器却不支持,怎么办呢,本人首先想到寻找一个虚拟鼠标的软件,但是发现很难实现,其实WIN8模拟器支持多点触控,我们可以在WIN8模拟器中打开WP8模拟器,这样就可以利用WIN8模拟器的多点触控功能了.