zoukankan      html  css  js  c++  java
  • 《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

  • 相关阅读:
    [MAC]如何抹掉 Mac 磁盘
    [MAC]出售、赠送或折抵 Mac 前该怎么做
    转载 软件项目计划如何编写举例
    GIT
    AWR实战分析之----direct path read temp
    ASM 磁盘组的的scrip
    巨杉db
    High waits on control file sequential read
    如何减小SQL 的物理读,。
    block size大小
  • 原文地址:https://www.cnblogs.com/hielvis/p/2173294.html
Copyright © 2011-2022 走看看