• 《Windows Fun 7》 二:Windows Phone怎样实现一个水平切换Page的Transition


    前言

         重置过Windows Phone 7手机的朋友,一定记得,重置之后第一件要做的事是设置语言,时区,时间,Live ID等信息。我们也一定记得那是一种水平切换的动画。当我们点击下一步时,页面从右向左滑动。有点类似iphone主页菜单的左右滑动效果。

         前段时间QQ群有人在问这种效果。这样的效果有时候会用到,比如街旁WP7客户端开始的引导用户使用的几个页面,类似教程,就可以做成这种效果。还有,如果需要在客户端注册,也可以把几个注册过程做成这样的效果,还有比如翻书的效果,也可以这样平移……

         我们可以看下一下效果,它是从右向左连贯的水平滑动,本篇就分析这样的思路以及实现原理,并给出源代码下载:

    page

    1.你的思路

         如果我猜的没错,你已经有了大概的思路:设置一个Canvas,按每个页面实际的位置布局,然后每次修改每个元素的Canvas.Left……

         确实,以前我也这么干过!

         这样虽然可行,但是有点显得我们不够专业,而且所有页面需要一次实例化,如果页面过多,那会影响第一次加载时间。

         那怎么办呢?不急,我们一步一步来慢慢分析。

    2.Silverlight中的简单导航

         切换,切换,切换,该怎么切换呢?

         想想,在Silverlight中,我们要由一个UI页面切换到另一个UI页面,在不借助Frame的时候,会怎么办呢。

         最简单的,就是将容器的Content给替换成另一个对象,没错就是这样。

         所以,页面切换的最简单模型就是,最外层放置一个ContentControl,第一次显示的时候给他的Content赋值一个页面实例,在用户点击导航按钮之后,用另一个页面实例替换。如下的模型:

    aaa

    3.Frame+Page

         其实,没错啦,页面切换的基本模型就是这么简单。

         Silverlight提供了一个Frame+Page的导航模型,它其实仍然是这样的原理。我们首先看下Frame的定义:

       1:  public class Frame : ContentControl, INavigate
       2:  {
       3:     public bool CanGoBack { get; internal set; }
       4:     public bool CanGoForward { get; internal set; }
       5:     public Uri CurrentSource { get; internal set; }
       6:     public Uri Source { get; set; }
       7:     public UriMapperBase UriMapper { get; set; }
       8:     public event FragmentNavigationEventHandler FragmentNavigation;
       9:     public event NavigatedEventHandler Navigated;
      10:     public event NavigatingCancelEventHandler Navigating;
      11:     public event NavigationFailedEventHandler NavigationFailed;
      12:     public event NavigationStoppedEventHandler NavigationStopped;
      13:     public void GoBack();
      14:     public void GoForward();
      15:     public bool Navigate(Uri source);
      16:     public void StopLoading();
      17:  }

         Frame首先是一个ContentControl,那么每个Page就是一个页面了。不同的是不需要我们去替换Content,我们只需要指定一个XAML文件的地址,Frame会根据路径找到这个页面,然后实例化,然后自己替换它的Content。

         相比我们前面得出的最简单的模型,它仅仅是提供了一些导航事件,使得我们可以做出一些处理。

         我们看到,Frame模型简化了我们开发中的页面导航。还是很有用的。

    4.Windows Phone 7中的Transitions

         Nice!但是页面切换太突兀了,一下子就换过来,一点都不符合Silverlight的风格,Silverlight可是做动画的啊,Silverlight可是替换Flash的啊,为什么要像HTML一样导航呢。

         Windows Phone 7操作系统出来后,Metro横空出世,Metro强调流畅,强调平滑,强调每户的每个操作和响应都能通过一些视觉变换反馈给用户。因此,给Page之间的过度加入了Transition。

         不过默认的PhoneApplicationFrame并没有加入Transition的功能,我们需要去分析Toolkit里面的TransitionFrame代码。

         对TransitionFrame的分析以及将本实例转化为Frame的方式将会在下一篇分析。

    5.本示例代码分析

         本实例实际上来源于PDC大会上介绍Expression Blend 4时官方演示的源代码示例。其中我把导航的部分抽取出来。

         和Frame+Page的模型类似,本实例也由两部分组成:TransitionContentControl和SlideShow。其中TransitionContentControl类似于Frame的功能,而SlideShow类似于Page,不同的是SlideShow是继承于ItemsControl,它首先就会取得所有页面并实例化,这在页面多得时候不是很可取,后面我会将它进行改造。

       1:  public class TransitionContentControl : Control 
       2:  { 
       3:     private ContentControl currentWrappedContent = null; 
       4:     private Grid grid = null; 
       5:   
       6:     public Style TransitionStyle { get; set;} 
       7:     public object Content{ get; set;} 
       8:   
       9:     static void ContentPropertyChanged(object o, DependencyPropertyChangedEventArgs args); 
      10:     static void TransitionStylePropertyChanged(object o, DependencyPropertyChangedEventArgs args); }
     

         和前面的Frame没有太大区别,神奇之处在于多了一个TransitionStyle依赖属性,这个Style也没什么区别,神奇之处在于多了一个LayoutStates

       1:  <Style x:Key="SlideTemplate" TargetType="ContentControl">
       2:      <Setter Property="Template">
       3:          <Setter.Value>
       4:             <ControlTemplate TargetType="ContentControl">
       5:                <Grid x:Name="grid" Background="{TemplateBinding Background}">
       6:                   <VisualStateManager.VisualStateGroups>
       7:                      <VisualStateGroup x:Name="LayoutStates">
       8:                         <VisualState x:Name="BeforeLoaded">
       9:                         <VisualState x:Name="AfterLoaded"/>
      10:                         <VisualState x:Name="BeforeUnloaded">
      11:                      </VisualStateManager.VisualStateGroups>
      12:                      <Grid.RenderTransform>
      13:                         <CompositeTransform/>
      14:                      </Grid.RenderTransform>
      15:                      <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding                                    ContentTemplate}" Content="{TemplateBinding Content}"}"/>
      16:               </Grid>
      17:            </ControlTemplate>
      18:         </Setter.Value>
      19:     </Setter>
      20:  </Style>
     

         关于LayoutStates,我之前的一篇文章有介绍:http://www.cnblogs.com/hielvis/archive/2010/10/09/1846046.html

         其实也很简单,它定义了三个状态BeforeLoaded,AfterLoaded,BeforeUnloaded。

         BeforeLoaded就是一个页面加载之前的状态,如果页面是从右向左滑动,那么它应该是在屏幕右边看不见:

       1:  <VisualState x:Name="BeforeLoaded"> 
       2:     <Storyboard> 
       3:        <DoubleAnimation Duration="0" To="1000" 
       4:               Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)" 
       5:               Storyboard.TargetName="grid"/> 
       6:        <DoubleAnimation Duration="0" To="0" 
       7:               Storyboard.TargetProperty="(UIElement.Opacity)" 
       8:               Storyboard.TargetName="grid"/> 
       9:     </Storyboard> 
      10:  </VisualState> 
     

         我们看到这个状态,其CompositeTransform.TranslateX被设置到屏幕后边的位置,并且透明度为0,不可见。

         与BeforeLoaded状态相反,BeforeUnloaded如下:

       1:  <VisualState x:Name="BeforeUnloaded">   
       2:     <Storyboard>   
       3:       <DoubleAnimation Duration="0" To="-1000"   
       4:                   Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)"                       
       5:                   Storyboard.TargetName="grid"/>    
       6:        <DoubleAnimation Duration="0" To="0"                         
       7:                  Storyboard.TargetProperty="(UIElement.Opacity)"   
       8:                  Storyboard.TargetName="grid"/>   
       9:     </Storyboard>  
      10:  </VisualState> 

         而AfterLoaded就是一个页面正常显示的状态,那为什么它是空的没有设置呢,那是因为它和正常状态一样,没有修改任何元位置,下面就是正常状态:    
       1:  <Grid.RenderTransform> 
       2:      <CompositeTransform/> 
       3:  </Grid.RenderTransform> 
     

         好了,由此我们不难明白,由BeforeLoaded状态过度到AfterLoaded状态,就是新页面由右向左滑入的动画;而由AfterLoaded状态过度到BeforeUnloaded状态,就实现了页面向左淡出的动画效果。

         那么你会问,这两个状态过度如何同时进行的。实际上,这两个动画不是同时作用在一个元素上,通过前面简单的模型,我们知道Frame实际上控制着两个页面实例,在切换的时候,它将两个不同的状态过度分别作用的两个页面上,具体的代码在OnContentChanged方法中:

       1:  protected virtual void OnContentChanged(object oldContent, object newContent)
       2:  {
       3:      ContentControl oldWrapper = this.currentWrappedContent;   if (oldWrapper != null)
       4:      {
       5:          VisualStateGroup layoutStatesGroup = FindNameInWrapper(oldWrapper, "LayoutStates") as VisualStateGroup;
       6:          if (layoutStatesGroup == null)
       7:          {
       8:               this.grid.Children.Remove(oldWrapper);
       9:               SetContent(oldWrapper, null);
      10:          }
      11:          else
      12:          {
      13:             layoutStatesGroup.CurrentStateChanged += delegate(object sender, VisualStateChangedEventArgs args)
      14:              {
      15:                   this.grid.Children.Remove(oldWrapper);
      16:                   SetContent(oldWrapper, null);
      17:               };
      18:             VisualStateManager.GoToState(oldWrapper, this.Reversed ? "BeforeLoaded" : "BeforeUnloaded", true);
      19:           }
      20:        }
      21:   
      22:        ContentControl newWrapper = new ContentControl();
      23:        newWrapper.Style = this.TransitionStyle;
      24:        newWrapper.HorizontalContentAlignment = HorizontalAlignment.Stretch;
      25:        newWrapper.VerticalContentAlignment = VerticalAlignment.Stretch;
      26:   
      27:        this.grid.Children.Add(newWrapper);
      28:        newWrapper.ApplyTemplate();
      29:        if (this.TransitionStyle != null)
      30:        {
      31:           SetContent(newWrapper, newContent);
      32:           if (oldWrapper != null)
      33:           {
      34:               VisualStateManager.GoToState(newWrapper, this.Reversed ? "BeforeUnloaded" : "BeforeLoaded", false);
      35:               VisualStateManager.GoToState(newWrapper, "AfterLoaded", true);
      36:           }
      37:       }
      38:       this.currentWrappedContent = newWrapper;}
     
    这个方法实际上是本示例最核心的部分,它分别将两种状态切换作用在两个页面实例上,然后两个动画同时作用,就形成了页面由右向左滑入的效果。
    这里也提前指出TransitionFrame的一个缺点,它不能像这样两个动画同时作用。我后面将要改造的Frame会加上LayoutStates的支持。
     
    此示例其实代码非常精简,思路也很清晰。以上我分析了核心部分。
     

    6.下一篇内容

         在此基础上,我相信你已经大概知道TransitionFrame的实现思路了,是的,至少思想是类似的。

         但是TransitionFrame要复杂得多,我将在下一篇分析。

         本实例实际上有很多缺陷:

    1. 所有页面第一次全部实例化,不能像Frame可以在导航的时候加载页面
    2. 不能结合PhoneApplicationPage响应回退键,需要响应后退事件还需要手工做一些事情

         后续,我将把次功能整合进TransitionFrame或者自定义一个Frame,那是就会分析一下Frame,这样就可以把这种效果与Back事件集成。

    7.源代码下载

         源代码:https://files.cnblogs.com/hielvis/TecHappy.WindowsPhone.Toolkit.rar

         Xap包:https://files.cnblogs.com/hielvis/TecHappy.WindowsPhone.Sample.xap

    8.Windows Phone 7技术沙龙

         9月17号,我们将在微软亚太研发集团举办Windows Phone 7的技术沙龙,将有一些大家熟悉的一线开发人员分享实战经验。

         微博报名:http://event.weibo.com/199979

         我们官方宣传页:http://www.techappy.net/

    特邀嘉宾

    ray

  • 相关阅读:
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第三节 梯度下降法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第二节 线性回归算法 (下)实操篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第二节 线性回归算法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第一节 KNN算法 (下)实操篇
    萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第一节 KNN算法 (上)理解篇
    萌新向Python数据分析及数据挖掘 第二章 pandas 第五节 Getting Started with pandas
    Oracle数据库安装和授权
    c# 如何获取JSON文件以及如何获取Config文件(framework 和 net .Core)
    C#Core查询数据库存储EXCEL文件
    如何在WINDOW系统下编译P12证书制作
  • 原文地址:https://www.cnblogs.com/hielvis/p/2173294.html
Copyright © 2020-2023  润新知