Windows Phone 8.1触控输入-----手势及指针

Windows Phone 8.1应用中有很多关于跟用户触控交互的效果,比如用手指将图片放大或者缩小以及将图片旋转之类

的操作等等。那么这都如何实现的呢。

以及看到基本每一个UIElement时,将其对应的属性列表看到一长串的事件,整个人都凌乱了。

  

以上这么多圈住的划横线的都是点击手势和指针事件,那怎么区分其中的不同呢?


触控输入:触摸输入 (XAML)

指针:指针 (XAML)


触控输入:属于UIElement事件

首先认识三个不同抽象级别的 UIElement 事件

1.高级事件:用于响应简单的交互,如Tap事件

2.指针事件提供较低级别的详细信息,如指针动作以及区分按压和释放手势,如PointerPressed事件

3.操作事件提供较低级别的详细信息,如手势速度和延迟以及多点触摸数据,如ManipulationStarting事件


其次呢,现如今除了PC,可以触控的PC,平板电脑,平板以及手机等设备,随着这些设备,我们依然可以用传统的鼠

标,当然也可以用手指了,触控笔也应运而生了。暂且分别可以标识为Mouse,Pen,Touch。

如果为每一个触控输入建立3个事件(按下,保持,放开)的话,那就要建立9个事件了,为此,微软特定统一了API,

将指针输入统一为Pointer,这样既简化了代码,又不容易出错。


最后呢,指针输入只针对点对点的,指针事件仅限于一个手指的交互,如点击和滑动,而手势则偏向于丰富多彩的操

作,如点击,长按,轻扫,滑动,收缩,旋转,拉伸。


好了,知道区分手势和指针,就知道具体在什么场合用了。那具体怎么用呢?

高级事件:Tap等事件就不介绍了,比较简单交互,在这边大家不能误解了,说是简单,是因为封装的好,要完成一

个Tap事件其实经历了很多事件,拆开来还是得讲Manipulation事件和指针Pointer事件。

在下面Pointer部分会提及到

Tap的API是通过Manipulation API封装而来的,所以不要看上去就一个Tap事件,其实里面早已经触发了很多事件,

比如ManipulationStarting事件以及指针的好多事件


手势,一切的成功要建立在设置了ManipulationMode这个属性的基础上,要不然一切都发生不了。当然

ManipulationMode属性具体的值可以根据你具体的需求而定,如果怕麻烦,直接设定为All即可

触控触发的事件顺序:(结合Tap事件的话请看下面Pointer部分)

因为太烦了,所以以下用 A 代表Manipulation

->AStarting(触控操作开始前引发)

->AStarted(触控操作开始之后引发)

->ADelta(触控中引起的变化都会引发此事件,也就是说可以多次引发)

   (ADelta里面有两个重要的属性:Delta和Cumulative,同时ADelta事件的Complete()方法可以终止惯性运动)

   (Delta属性是指当前发生ADelta事件时的所有数据,Cumulative是指从触控操作开始一来发生的所有数据的更改)

->AStarting(触控终止,元素开始惯性运动时引发)

->ADelta(上面说过此事件可多次引发,所以再写一遍)

->ACompleted(触控完成,惯性完成,引发此操作)


好了,贴伪代码了:

下面中用到的CompositeTransform变换在我之前一篇博客中有提到,博客地址:动画与变换

XAML:

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock x:Name="textblock" HorizontalAlignment="Center" Text="结果" FontSize="35"/>
        <!--ManipulationMode必须指定,当然可以按需指定,如果嫌麻烦,直接指定为All即可-->
        <Image x:Name="image" Grid.Row="1" Source="DemoPic.png" Stretch="UniformToFill" Width="200" Height="200" 
               ManipulationMode="All"
               ManipulationStarting="image_ManipulationStarting" 
               ManipulationStarted="image_ManipulationStarted"
               ManipulationDelta="image_ManipulationDelta"
               ManipulationCompleted="image_ManipulationCompleted">
        </Image>
        <!--ManipulationMode必须指定,当然可以按需指定,如果嫌麻烦,直接指定为All即可-->
        <Image x:Name="image2" Grid.Row="2" Source="DemoPic2.png" Stretch="UniformToFill" Width="200" Height="200"
               RenderTransformOrigin="0.5,0.5"
               ManipulationMode="All"
               ManipulationStarting="image2_ManipulationStarting"
               ManipulationStarted="image2_ManipulationStarted"
               ManipulationDelta="image2_ManipulationDelta"
               ManipulationCompleted="image2_ManipulationCompleted">
            <Image.RenderTransform>
                <!--组合变换,但是变换之间是独立的-->
                <CompositeTransform x:Name="compositeTransform"/>
                
            </Image.RenderTransform>
        </Image>
    </Grid>

.cs:

public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            this.NavigationCacheMode = NavigationCacheMode.Required;
        }

        /// <summary>
        /// 在此页将要在 Frame 中显示时进行调用。
        /// </summary>
        /// <param name="e">描述如何访问此页的事件数据。
        /// 此参数通常用于配置页。</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            // TODO: 准备此处显示的页面。

            // TODO: 如果您的应用程序包含多个页面,请确保
            // 通过注册以下事件来处理硬件“后退”按钮:
            // Windows.Phone.UI.Input.HardwareButtons.BackPressed 事件。
            // 如果使用由某些模板提供的 NavigationHelper,
            // 则系统会为您处理该事件。
        }

        private void image_ManipulationStarting(object sender, ManipulationStartingRoutedEventArgs e)
        {
            textblock.Text = "ManipulationStarting";
        }

        private void image_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
        {
            textblock.Text = "ManipulationStarted";
        }

        private void image_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
        {
            textblock.Text = "ManipulationDelta";
        }

        private void image_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
        {
            textblock.Text = "ManipulationCompleted";
        }

        private void image2_ManipulationStarting(object sender, ManipulationStartingRoutedEventArgs e)
        {
            textblock.Text = "ManipulationStarting";
        }

        private void image2_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
        {
            textblock.Text = "ManipulationStarted";
        }

        private void image2_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
        {
            //这边采用的是在XAML页面中已经给UIElement定义好了RenderTransform变换模式的方法
            //当然你也可以不采用这种方法,直接在.cs中动态指定也是可以的,思想都是一样的。只不过XAML把需要在.cs中申明的做好了而已,这也是XAML封装的方便性
            //比如如下,当然下面的不能在ManipulationDelta申明,要在函数体外申明
            //CompositeTransform cpTransform = null;
            //image2.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY | ManipulationModes.ScaleX | ManipulationModes.ScaleY | ManipulationModes.Rotate;
            //cpTransform = new CompositeTransform();
            //cpTransform.TranslateX = cpTransform.TranslateY = 0;
            //cpTransform.Rotation = 0;
            //........
            //image2.RenderTransform = cpTransform;

            textblock.Text = "ManipulationDelta";

            //可以自由拖动图片,说白了就是对位置坐标的不断加减以使图片到拖动指定的位置上,注意这边是加法
            compositeTransform.TranslateX += e.Delta.Translation.X;
            compositeTransform.TranslateY += e.Delta.Translation.Y;

            //自由伸缩图片,这边缩放当然是等比例缩放,当然你也可以自己设置。注意这边是乘法,因为是放大倍数
            //甚至你可以不写Y轴上的缩放,看是什么效果
            compositeTransform.ScaleX *= e.Delta.Scale;
            compositeTransform.ScaleY *= e.Delta.Scale;
            //设置缩放最大最小比例,当图片放大时,如果放大倍数小于3,则放大到指定的程度;如果放大倍数大于3,则只放大到3倍,不能再放大了
            //缩小图片,同理
            compositeTransform.ScaleX = Math.Min(compositeTransform.ScaleX,3.0);
            compositeTransform.ScaleY = Math.Min(compositeTransform.ScaleY,3.0);
            compositeTransform.ScaleX = Math.Max(compositeTransform.ScaleX,0.5);
            compositeTransform.ScaleY = Math.Max(compositeTransform.ScaleY,0.5);

            //旋转图片,注意这边是加法
            compositeTransform.Rotation += e.Delta.Rotation * 180 / Math.PI;

        }

        private void image2_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
        {
            textblock.Text = "ManipulationCompleted";
        }
    }

这边就随便截了图,包含图片位置变化,缩放,旋转

          手势操作之后:


手势之后便是介绍指针了:

其实还是弄清楚执行的顺序即可(当然其中应该有Tap的一些相关事件和Manipulation相关事件)

PointerEntered -> PointerPressed -> PointerMoved(可多次) -> PointerReleased -> PointerExited

当然就Tab事件所触发了多少事件的话,写全了应该如下:

PointerEntered -> PointerPressed -> ManipulationStarting -> PointerMoved(可多次)

-> PointerReleased -> Tapped->PointerExited

具体所代表的意思可以查看上面给出的链接


直接给出利用Pointer实现画板功能的伪代码:

XAML:

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock x:Name="textblock" FontSize="35" Text="结果" HorizontalAlignment="Center"/>
        <Canvas Grid.Row="1" x:Name="canvas" Width="350" Height="350" HorizontalAlignment="Center" VerticalAlignment="Center" Background="Coral"
                 PointerEntered="ellipse_PointerEntered"
                 PointerPressed="ellipse_PointerPressed"
                 PointerMoved="ellipse_PointerMoved"
                 PointerReleased="ellipse_PointerReleased"
                 PointerExited="ellipse_PointerExited"/>
</Grid>

.cs:

//uint表示32位无符号的整数
        Dictionary<uint, PointerPoint> points = new Dictionary<uint, PointerPoint>();
        private void ellipse_PointerEntered(object sender, PointerRoutedEventArgs e)
        {
            textblock.Text = "PointerEntered";
        }

        private void ellipse_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            //判断是否与设备接触
            if(e.Pointer.IsInContact)
            {
                points.Add(e.Pointer.PointerId, e.GetCurrentPoint(canvas));
                textblock.Text = "PointerPressed";
            }
        }

        private void ellipse_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            if(e.Pointer.IsInContact)
            {
                var point = e.GetCurrentPoint(canvas);
                if(points.ContainsKey(e.Pointer.PointerId))
                {
                    var prev = points[e.Pointer.PointerId];
                    Line line = new Line();
                    line.X1 = prev.Position.X;
                    line.Y1 = prev.Position.Y;
                    line.X2 = point.Position.X;
                    line.Y2 = point.Position.Y;
                    line.Stroke = new SolidColorBrush(Colors.Blue);
                    line.StrokeThickness = 10.0;
                    line.StrokeStartLineCap = line.StrokeEndLineCap = PenLineCap.Round;
                    canvas.Children.Add(line);
                }
                points[e.Pointer.PointerId] = point;
                textblock.Text = "PointerMoved";
            }
        }

        private void ellipse_PointerReleased(object sender, PointerRoutedEventArgs e)
        {
            if (points.ContainsKey(e.Pointer.PointerId))
            {
                points.Remove(e.Pointer.PointerId);
                textblock.Text = "PointerReleased";
            }
        }

        private void ellipse_PointerExited(object sender, PointerRoutedEventArgs e)
        {
            if (points.ContainsKey(e.Pointer.PointerId))
            {
                points.Remove(e.Pointer.PointerId);
                textblock.Text = "PointerExited";
            }
        }

截图:注意当Pointer脱离所作用的元素时,触发PointerExited事件,如最后一张图所见



推荐链接:我看了之后又很大的帮助和收获,也希望帮到大家

zhangjunjian127的专栏:WP7 ——触控操作Manipulation 

编程小梦:windows phone 8.1开发:触控和指针事件1

          Windows Phone 8.1开发:触控和指针事件2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值