基于最新的Runtime框架,仿制Windows10的进度条
分类:计算机编程

一、其实有现成的

《深入理解Windows Phone 8.1 UI控件编程》本书基于最新的Windows Phone 8.1 Runtime SDK编写,全面深入地论述了最酷的UI编程技术:实现复杂炫酷的动画、掌握布局原理、列表虚拟化原理、高性能列表实现、图表编程、控件原理等。

  本篇将记录一下如何在WPF中绘画和设计动画,这方面一直都不是VS的强项,然而它有一套利器Blend;这方面也不是我的优势,幸好我有博客园,能记录一下学习的过程。在本记录中,为了更好的理解绘画与动画,多数的例子还是在VS里面敲出来的。好了,不废话了,现在开始。

前言

在前面一篇“新年快乐”的随笔中,我们介绍了WinRT中的简单动画实现。其实在使用Windows/Windows Phone时,我们都会看到一些动画,最简单的比如按下一个button时,该button的状态变化就是动画的一种。再比如弹出式窗口或菜单,也是一种动画。WinRT中的动画种类很多,但是分类有点儿让初学者摸不着头脑:主题过渡,主题动画,视觉转换,情节提要动画。这些我们就不说了,这里主要说说自定义动画,或者说是情节提要动画(Storyboard Animation),因为这种动画是我们要常用的。

但是在一个非游戏类的App中添加动画是有原则的:在UI状态之间进行快速流畅的过渡,但不会使用户分心;超出用户的预期,但是又不会让用户厌烦。当然最大的前提是你的App的基本功能比较完美。如果有两个App实现了相同的功能,一个有动画,一个没有,你会喜欢哪个呢?答案显而易见。况且在WinRT中,动画实现比较简单,效果又很好,所以just do it!

今天我们按实现方式介绍三类动画:单一动画,复合动画,关键帧动画。其中还分别介绍了用XAML/Code如何实现动画。

  先来看看Windows10进度条的两种模式:

 全书源代码免费下载:

一、WPF绘画

收藏页面中的动画 - 单一动画

图片 1

在这个页中,点击三个蓝色的收藏类别条(分类/博主/博文),都会触发两个动画:

1)类别条本身做360度的X轴旋转

2)对应的类别条下方的ListView做FadeIn/FadeOut的显示/隐藏过渡

 图片 2

1.1基本图形

  在WPF中可以绘制矢量图,不会随窗口或图型的放大或缩小出现锯齿或变形,除此之外,XAML绘制出来的图有个好处就是便于修改,当图不符合要求的时间,通常改某些属性就可以完成了。下面先记录一下几个基本的图形(他们都派生于Shape类)。

  • Line.aspx) 直线段
  • Rectangle.aspx) 矩形
  • Ellipse 椭圆
  • Polygon.aspx) 多边形
  • Polyline.aspx) 折线,不闭合
  • Path.aspx) 路径

用XAML定义动画

先说360度旋转的做法。我们定义一个Template Control,然后在该Control的Style中定义动画:

<Style TargetType="local:FavoriteGroupControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:FavoriteGroupControl">
                    <Grid x:Name="grid_Header" Height="60" Background="{ThemeResource CNBlogsThemeColor}">
                        <Grid.Projection>
                            <PlaneProjection/>
                        </Grid.Projection>
                        <Grid.Resources>
                            <Storyboard x:Name="sb_Roll">
                                <DoubleAnimation Storyboard.TargetName="grid_Header"                               Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)"
From="0" To="360" Duration="0:0:00.50"/>
                            </Storyboard>
                        </Grid.Resources>

……
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

我去掉了不重要的部分,只留下了要说明的部分,完整代码请看Windows Phone project中的Theme/Generic.xaml。

首先要定义<Grid.Projection>属性,<PlaneProjection/>表示该Grid需要做X/Y/Z轴的旋转,这个定义是必须的(如果不定义的话后面会出错)。其次,要在<Grid.Resources>中定义Storyboard,它包含有一个<DoubleAnimation>(在后面的动画中会在一个Storyboard中包含多个DoubleAnimation)。

再看DoubleAnimation的细节:

1) Storyboard.TargetName指明我们要对名字叫做gird_Header的控件下毒手

2)Storyboard.TargetPrpoerty指明了我们要玩弄那个控件的PlaneProjection.RotationX属性

3)From/To指明了要把该控件旋转一周即360度

4)Duraion指明在0.5秒内完成

好了,动画定义好了,如何触发呢?在MainPage.xaml中,你可以找到以下代码段:

<local:FavoriteGroupControl x:Name="fgc_Category" Tapped="sp_category_Tapped" Margin="0,10"/>

这里定义了一个sp_category_Tapped事件,顺藤摸瓜,我们在MainPage.xaml.cs中找到以下代码:

private void sp_category_Tapped(object sender, TappedRoutedEventArgs e)
{
            this.fg_Category.Tapped();
}

请注意!一个控件的内置动画只应该在其内部触发,而不是由外部控制。所以,这次摸的瓜是个傻瓜:) 真正的触发动画的Code应该在FavoriteGroupControl.cs中找:

protected override void OnTapped(TappedRoutedEventArgs e)
{
    Storyboard sb = this.GetTemplateChild("sb_Roll") as Storyboard;
    if (sb != null)
    {
        sb.Begin();
    }
}

它先根据名称“sb_Roll”获得Storyboard的实例sb,然后调用其Begin()方法使其开始旋转。在XAML中定义的Storyboard,都要通过事件处理代码调用Begin()来激活动画。

这里有两点要说明:

1)为什么用动画?因为凡是在用户点击屏幕时,我们都应该给予视觉上的响应,免得心急的用户狂点屏幕造成手指受伤,作为程序员的我们要有爱心

2)为什么用旋转动画?因为我喜欢,就让我任性一次吧,不容易啊。当然也可用别的动画,比如斜一下,或者陷下一点儿。

3)为什么在控件内部调用Begin()?因为你给人家提供一个控件,按下后旋转是该控件的预定行为,不要再让使用该控件的人再去管什么动画操作。当然,你也可以提供一个TemplateBinding属性来让使用该控件的人指定是否需要动画,然后在控件内部根据设置调用或不调用动画。

 

 

1.2笔刷

  常用的笔刷Brush类型有:

· SolidColorBrush.aspx):使用纯 Color 绘制区域。 

· LinearGradientBrush:使用线性渐变绘制区域。 其中有个GradientStop.aspx)属性,径向渐变也有可以查看msdn,我觉得上面说的还是比较清楚的。

· RadialGradientBrush:使用径向渐变绘制区域。 

· ImageBrush:使用图像(由 ImageSource 对象表示)绘制区域。

· DrawingBrush:使用 Drawing 绘制区域。 绘图可能包含向量和位图对象。

· VisualBrush:使用 Visual 对象绘制区域。 使用 VisualBrush 可以将内容从应用程序的一个部分复制到另一个区域,这在创建反射效果和放大局部屏幕时会非常有用。

接下来感受一下Shape类和Brush类的使用。

用Code定义动画

该部分第二个动画是显示或隐藏ListView,这次我们用另外一种方法实现的动画,用Code实现,而不是用XAML实现。看code:

class FavoriteGroup
    {
        bool ShowListView = true;
        ListView lvDetail;
        Storyboard sbShow, sbHide;

        public FavoriteGroup(ListView lv)
        {
            this.lvDetail = lv;
            CreateStoryboard();
            this.sbHide.Completed  = sbHide_Completed;
        }

        private void sbHide_Completed(object sender, object e)
        {
            this.lvDetail.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
        }

        public void Tapped()
        {
            this.ShowListView = !this.ShowListView;
            if (this.ShowListView)
            {
                this.lvDetail.Opacity = 0;
                this.lvDetail.Visibility = Windows.UI.Xaml.Visibility.Visible;
                this.sbShow.Begin();
            }
            else
            {
                this.sbHide.Begin();
            }
        }

        private void CreateStoryboard()
        {
            // show listview in 1 second
            DoubleAnimation daShow = new DoubleAnimation();
            daShow.From = 0;
            daShow.To = 1;
            daShow.Duration = new Windows.UI.Xaml.Duration(TimeSpan.FromSeconds(1));

            this.sbShow = new Storyboard();
            sbShow.Children.Add(daShow);
            Storyboard.SetTarget(daShow, this.lvDetail);
            Storyboard.SetTargetProperty(daShow, "Opacity");

            // hide listview in 1 second
            DoubleAnimation daHide = new DoubleAnimation();
            daHide.From = 1;
            daHide.To = 0;
            daHide.Duration = new Windows.UI.Xaml.Duration(TimeSpan.FromSeconds(1));

            this.sbHide = new Storyboard();
            sbHide.Children.Add(daHide);
            Storyboard.SetTarget(daHide, this.lvDetail);
            Storyboard.SetTargetProperty(daHide, "Opacity");
        }

    }

在构造函数中,调用了CreateStoryboard()方法,首先定义了两个Storyboard,在每个Storyboard中定义了一个DoubleAnimation,一个是用1秒时间把ListView的Opacity值从0变到1(显示),另一个是用1秒时间把Opacity从1变到0(隐藏)。上面的写法等价于这个XAML:

<Storyboard x:Name="sbShow">
    <DoubleAnimation Storyboard.TargetName="lvDetail"
                                  Storyboard.TargetProperty="Opacity"
                                  From="0" To="1" Duraion="0:0:1"/>
</Storyboard>
<Storyboard x:Name="sbHide">
    <DoubleAnimation Storyboard.TargetName="lvDetail"
                                  Storyboard.TargetProperty="Opacity"
                                  From="1" To="0" Duraion="0:0:1"/>
</Storyboard>

为什么在这里不用XAML写法而用Code直接定义呢?是为了显示技巧吗?你猜对啦!因为在MainPage.xaml中,有三个ListView,分别为lv_category, lv_author, lv_blog,如果要用XAML定义动画,要对这个三个ListView各写一遍,重复了三次,只是ListView的名字不同,太难看啦!注意素质!于是搞了一个FavoriteGroup类(可能名字不太好,叫刺杀金xx怎么样?),里面用code包了一下,把ListView作为参数传入,就可以复用code啦。哎,纯属刁民小技,让各位看官见笑了。

  网上有不少介绍仿制Windows10进度条的文章,也都实现了不错的效果。而我再开一文的原因是觉得如果在这基础上添加一些功能,比如圆点的数量,圆点的大小等等,效果可能会更好一些。接触过UWP的朋友应该知道,其框架中自带了进度条控件,以 ProgressRing 为例,通过Blend,我们可以获取到控件的XAML,以下是部分截图:

试读章节会在博客园持续更新:

1.3 直线段

  在平面上,两点确定一条直线段。同样在Line类中也具有两点的属性(X1,Y1) ( X2,Y2),同时还个属性Stroke——笔触,它是Brush类型的。也就是可以用上面的笔刷赋值。由于其简单性,在此不作过多的说明,可以画出下面的直线段如图1:

图片 3

图1

下面是对应的代码,在Blend敲的话,对应的属性值提示会更加完整些,但是VS下看着比较清晰,各有优略了。

图片 4图片 5XAML

<Window x:Class="Chapter_10.LineTest"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="LineTest" Height="300" Width="300">
    <Grid>
        <Line X1="10" Y1="20" X2="260" Y2="20" Stroke="Red" StrokeThickness="10"></Line>
        <Line X1="10" Y1="40" X2="260" Y2="40" Stroke="Orange" StrokeThickness="6"/>
        <Line X1="10" Y1="60" X2="260" Y2="60" Stroke="Green" StrokeThickness="3"/>
        <Line X1="10" Y1="80" X2="260" Y2="80" Stroke="Purple" StrokeThickness="2"/>
        <Line X1="10" Y1="100" X2="260" Y2="100" Stroke="Black" StrokeThickness="1"/>
        <Line X1="10" Y1="120" X2="260" Y2="120" StrokeDashArray="3" Stroke="Black" StrokeThickness="1"/>
        <Line X1="10" Y1="140" X2="260" Y2="140" StrokeDashArray="5" Stroke="Black" StrokeThickness="1"/>
        <Line X1="10" Y1="160" X2="260" Y2="160" Stroke="Black" StrokeEndLineCap="Flat" StrokeThickness="6"/>
        <Line X1="10" Y1="180" X2="260" Y2="180" Stroke="Black" StrokeEndLineCap="Triangle" StrokeThickness="8"/>
        <Line X1="10" Y1="200" X2="260" Y2="200" StrokeEndLineCap="Round" StrokeThickness="10">
            <Line.Stroke>
                <LinearGradientBrush EndPoint="0,0.5" StartPoint="1,0.5">
                    <GradientStop Color="Blue"/>
                    <GradientStop Offset="1"/>
                </LinearGradientBrush>
            </Line.Stroke>
        </Line>
    </Grid>
</Window>

Setting页面中About的动画 - 复合动画

图片 6

我们再看看稍微复杂些的动画:在一个Storyboard中包含多个DoubleAnimatoin。

<Storyboard x:Name="sb_LogoMoveUp">
            <DoubleAnimation Duration="0:0:0.8"
                             From="200"
                             Storyboard.TargetName="image_Logo"
                             Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"
                             To="0" />
            <DoubleAnimation Duration="0:0:0.8"
                             From="360"
                             Storyboard.TargetName="image_Logo"
                             Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationZ)"
                             To="0" />
            <DoubleAnimation Duration="0:0:0.8"
                             From="0"
                             Storyboard.TargetName="image_Logo"
                             Storyboard.TargetProperty="Opacity"
                             To="1" />
        </Storyboard>

在SettingsPage.xaml中,我们在sb_LogoMoveUp的Storyboard中定义了三个动画:

1)把image_Logo上移200个像素

2)让image_Logo旋转360度

3)让image_Logo透明度从0变成1

以上三个动画同时进行,都是在0.8秒内完成,于是我们看到了那个图片从下方“滚动”(不是滑动)到上方,并逐渐清晰,整个过程很是优雅大方,毕竟滚动摩擦比滑动摩擦小很多(扯远了),不拖泥带水,很有节操的。

要说明几点:

1)用复合动画,可以对一个控件的不同属性进行同时操作,以形成单一动画无法完成的复杂效果。比如我们是对image_Logo的三个属性同时进行操作。当然也可以不同时,用BeginTime属性来设置一下启动时间即可。

2)在这里为什么要用动画?因为我喜欢超出用户的预期,给他们以动态视觉享受,而不是干巴巴的看着一个图片发呆。用户一高兴,没准儿就给个好评了。

 图片 7

[WP8.1UI控件编程]Windows Phone XAML页面的编译

1.4矩形

  矩形最突出的属性是长和宽,除此之外还有(Stroke)笔触、填充(Fill)属性等属性。下面看一下能画出的图形如图2已经代码:

图片 8

图2

代码如下:

图片 9图片 10XAML

<Window x:Class="Chapter_10.RectangleTest"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="RectangleTest" Height="390" Width="600">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="160"/>
            <RowDefinition Height="10"/>
            <RowDefinition Height="160"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="180"/>
            <ColumnDefinition Width="10"/>
            <ColumnDefinition Width="180"/>
            <ColumnDefinition Width="10"/>
            <ColumnDefinition Width="180"/>
        </Grid.ColumnDefinitions>
        <Rectangle Grid.Column="0" Grid.Row="0" Stroke="Black" Fill="LightBlue"/>
        <Rectangle Grid.Column="2" Grid.Row="0">
            <Rectangle.Fill>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
                    <GradientStop Color="#FFB6f8f1" Offset="0.1"/>
                    <GradientStop Color="#FF0083bd" Offset="0.239"/>
                    <GradientStop Color="#ddffee" Offset="0.661"/>
                    <GradientStop Color="#eeaacc" Offset="1"/>
                    <GradientStop Color="#FF3DA5CA" Offset="0.422"/>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <Rectangle Grid.Column="4" Grid.Row="0">
            <Rectangle.Fill>
                <RadialGradientBrush >
                    <GradientStop Color="AntiqueWhite" Offset="0"/>
                    <GradientStop Color="Brown" Offset="0.25"/>
                    <GradientStop Color="green" Offset="0.75"/>
                    <GradientStop Color="red" Offset="0.75"/>
                </RadialGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <Rectangle Grid.Column="0" Grid.Row="2">
            <Rectangle.Fill>
                <ImageBrush ImageSource=".logo.png" Viewport="0,0,0.3,0.15" TileMode="Tile"/>
            </Rectangle.Fill>
        </Rectangle>
        <Rectangle Grid.Column="2" Grid.Row="2">
            <Rectangle.Fill>
                <DrawingBrush Viewport="0,0,0.2,0.2" TileMode="Tile">
                    <DrawingBrush.Drawing>
                            <GeometryDrawing Brush="LightBlue">
                                <GeometryDrawing.Geometry>
                                    <EllipseGeometry RadiusX="10" RadiusY="10"/>
                                </GeometryDrawing.Geometry>
                        </GeometryDrawing>
                    </DrawingBrush.Drawing>
                </DrawingBrush>
            </Rectangle.Fill>
        </Rectangle>
        <Rectangle Grid.Column="4" Grid.Row="2" StrokeThickness="10">
            <Rectangle.Stroke>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
                    <GradientStop Color="White" Offset="0.3"/>
                    <GradientStop Color="Blue" Offset="1"/>
                </LinearGradientBrush>
            </Rectangle.Stroke>
        </Rectangle>        
    </Grid>
</Window>

  以上的的效果不做过多的说明,具体的可以参照msdn中矩形的的属性,链接已经给出。下面给出一个关于VisualBrush的例子来体会一下,是怎么回事。在VisualBrush类中,有个构造函数:public VisualBrush(Visual visual);其实就是构造一个和Visual元素一样的实例,另外FrameworkElement也是继承于Visual类,那么所有的控件都可以用VisualBrush来模拟了。下面看一个简单的例子,其他的可以灵活掌握。通过点击中间的按钮,然左边的按钮的形状"放到"右边,例子的效果如图3:最下面的是通过透明度来控制的。

图片 11

图3

下面给出主要代码:

图片 12图片 13XAML

<Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="160"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="160"/>
        </Grid.ColumnDefinitions>
        <StackPanel x:Name="stackPanelLeft" Background="White">
            <Button x:Name="realButton" Content="OK" Height="40"/>
        </StackPanel>
        <Button Content=">>>" Grid.Column="1" Margin="5,0" Click="CloneVisual"/>
        <StackPanel x:Name="stackPanelRight" Background="White" Grid.Column="2"/>
    </Grid>

图片 14图片 15cs

        //定义透明度
        double o = 1.0;
        private void CloneVisual(object sender, RoutedEventArgs e)
        {
            //定义VisualBrush笔刷
            VisualBrush vBrush = new VisualBrush(this.realButton);

            //定义一个矩形,并使其宽高和按钮的一样,让他的填充笔刷为VisualBrush,透明度慢慢的减弱
            Rectangle rect = new Rectangle();
            rect.Width = realButton.ActualWidth;
            rect.Height = realButton.ActualHeight;
            rect.Fill = vBrush;
            rect.Opacity = o;
            o -= 0.2;
            this.stackPanelRight.Children.Add(rect);
        }

  这样的话上提到的可以做反射,或者是倒影的功能是不是有些思路了,设置透明度,然后旋转就可以了,至于放大镜的实例用到了VisualBrush的ViewBox属性,详情网上查询,如果有时间我会把这个例子补出来。

Windows 8.1版本中的PostControl动画 - 关键帧动画

大家可以查看Windows 8.1 project的Theme/Generic.xaml看完整代码。

在这个Control中,左边那个图,点击右侧箭头,将会向左滑动,成为右边那个样子。

图片 16

这个滑动过程不是线性的,因此要用到关键帧,意思是说:在某个时间点,做这件事;到下一个时间点,再做那件事。看下面的XAML代码:

<Storyboard x:Name="sb_Button_out">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SecondViewTrans"
    Storyboard.TargetProperty="X" BeginTime="0:0:0">
    <SplineDoubleKeyFrame  KeyTime="00:00:00.00" Value="480"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.10" Value="460"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.20" Value="400"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.30" Value="300"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.40" Value="170"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.50" Value="0"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.54" Value="32"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.58" Value="60"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.62" Value="80"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.66" Value="92"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.70" Value="96"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.74" Value="92"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.78" Value="80"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.82" Value="60"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.86" Value="32"/>
    <SplineDoubleKeyFrame  KeyTime="00:00:00.90" Value="0"/>
</DoubleAnimationUsingKeyFrames>

</Storyboard>

其中的那个<SplineDoubleKeyFram>就是关键帧的定义,在每个时间点,都定义了目标控件的X位置。可以看到第6个关键帧,X值已经是0了,为什么又从0变大了呢?这样就产生了触底反弹的效果,让目标控件弹回到最大96的位置,最后再回到0。

需要注意的是,关键帧只能对某个控件的唯一的一个属性操作,不能同时操作多个属性。而在上一节的复合动画中,是对某个控件的多个属性同时操作,但是不能对某个属性定义两次DoubleAnimation。这个要牢记。

  粗略一看,只要稍作修改便能用到WPF中——我们几乎可以什么都不做!

[WP8.1UI控件编程]Windows Phone自定义布局规则

1.5椭圆

  椭圆中比较常见的是长半轴a和短半轴b,如果a=b,那么就是一个圆形。在WPF中用Width和Height表示a,b其他的用法和矩形一致,下面给出一个球形的例子如图4:

图片 17

图4

关于折线和多边形不做过多说明了,下面直接记录路径(Path)。

小结

哦,办公室已经自动关灯了,看样子该给公家省电了,拍屁股回家吧。但是大家要记住哟,动画不能乱用,不能让用户讨厌,不能人为影响系统流畅度,不能影响系统性能。

比如在博客园UAP的WP版本中,我们在很多小地方使用了动画,比如热门页中下拉ListView时右上角的数字变化,博主页中下拉ListView时页面标题的变化,等等。这些动画都是和当前的操作密切相关的,但它们又不会强烈吸引用户注意。

在“新年快乐”页中,是故意要展示一下一些东西,所以做了很多动画。另外,在“新年快乐”页中,还用到了不使用Storyboard/DoubleAnimation/KeyFrame等技术,而是用纯code操作XAML元素的位置来制作的动画(游戏开发的基本功),我们后面再聊!

 

分享代码,改变世界!

Windows Phone Store App link:

http://www.windowsphone.com/zh-cn/store/app/博客园-uap/500f08f0-5be8-4723-aff9-a397beee52fc

Windows Store App link:

GitHub open source link:

MSDN Sample Code:

 

MS-UAP

2015/1/9

 

[WP8.1UI控件编程]Windows Phone理解和运用ItemTemplate、ContentTemplate和DataTemplate

 1.6路径

   路径在绘图中是属于比较重要的一个类,他可以替换上面的几个图形工具,而且还可以画出更复杂的图像。路径不仅有Stroke,StrokeThickness等属性,还有个关键的属性——Data,其类型为Geometry(几何图形),我们就是通过这个属性来替代其他绘图类的。下面先看一组图(图5):

图片 18

 图5

   如果用我们上面的直线,矩形,椭圆,多边形类,可以画出上面的图。那么让我们用路径类来替代前面的几个类吧。下面给出代码:

图片 19图片 20XAML

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="Chapter_10.PathTest"
    x:Name="Window"
    Title="PathTest"
    Width="340" Height="350">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="160"/>
        <RowDefinition Height="160"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="160"/>
        <ColumnDefinition Width="160"/>
    </Grid.ColumnDefinitions>
    <Path Stroke="Blue" StrokeThickness="2" Grid.Row="0" Grid.Column="0">
        <Path.Data>
            <LineGeometry StartPoint="20,20" EndPoint="140,140"/>
        </Path.Data>
    </Path>

    <Path Stroke="Orange" Fill="Yellow" Grid.Column="1" Grid.Row="0">
        <Path.Data>
            <RectangleGeometry Rect="20,20,120,120" RadiusX="10" RadiusY="10"/>
        </Path.Data>
    </Path>

    <Path Stroke="Green" Fill="LawnGreen" Grid.Row="1" Grid.Column="0">
        <Path.Data>
            <EllipseGeometry Center="80,80" RadiusX="60" RadiusY="40"/>
        </Path.Data>
    </Path>

    <Path Stroke="Green" Fill="LawnGreen" Grid.Row="1" Grid.Column="1">
        <Path.Data>
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigure StartPoint="25,140" IsClosed="True">

                            <!--以上一条的终点为起点-->
                            <LineSegment Point="20,40"/>
                            <LineSegment Point="40,110"/>
                            <LineSegment Point="50,20"/>
                            <LineSegment Point="80,110"/>
                            <LineSegment Point="110,20"/>
                            <LineSegment Point="120,110"/>
                            <LineSegment Point="140,40"/>
                            <LineSegment Point="135,140"/>

                    </PathFigure>
                </PathGeometry.Figures>
            </PathGeometry>
        </Path.Data>
    </Path>
</Grid>
</Window>

先解释一下上面的代码,由于Geometry为一个抽象类,有以下几个子类:

  • LineGeometry:直线段几何图形
  • RectangleGeometry:矩形几何图形
  • EllipseGeometry:椭圆几何图形
  • PathGeometry:路径几何图形
  • StreamGeometry
  • CombinedGeometry
  • GeometryGroup

   上面的例子中主要用到前四种类型的几何图形类,从代码可以看出前三个和它们对应的Shape类有相似,同样可以设置属性,来改变图形的形状。第四个类,有点不大一样,主要是通过多个LineSegment(线段)组成PathFigure(图,由于图是默认属性,可以省略PathFigure标签),多个PathFigure组成PathGeometry(几何图形)。和我们平时接触的几何有点相似,几何是由图组成,图是由多个段围成的,除此之外还有一个要注意的是每个段都是上一个段的终点作为起点的。除了LineSegment,还有几个比较重要的线段ArcSegment,BezierSegment(三次贝塞尔曲线),QuadraticBezierSegment(二次贝塞尔曲线段)等,如果想了解更多线段,请点击这里。特别是贝塞尔曲线,与数学和图形联系非常紧密,在此不作说明,有机会的话,写一篇这方面的文章。

  上面的这种多级标签式写法看起来比较清楚,但是一个路径可能是会很多行,为了方便,由于路径的特殊性(起点->绘图->闭合图形)下面还有一种简单的写法,直接用一个属性Data来表示路径。下面新看一下常用路径标记语法图6:

图片 21

图6

  下面举个例子说明一下(图7):

图片 22

图7

  上图中,以0,0坐标开始,有三段线段,终点坐标分别为(50,30)(60,40)(70,80)最后以一个Z命令闭合。如果要组成更复杂的路径,可以参考上面的表,当然需要一些几何基础。 关于绘画的类,暂时就记录到这里吧!

二、添加功能

[WP8.1UI控件编程]Windows Phone动画方案的选择

 二、图形的效果与滤镜

   有玩过Ps的就知道在里面有很多滤镜,使用起来方面,快捷。同样在WPF中,除了提供矢量图外,也有滤镜的功能。对于UIElement类的成员有两个属性BitmapEffect和Effect,前者由于其是占用CPU来计算渲染图片的,后者是显卡在计算运算能力站主导,这样Effect就为cpu省下了资源,所以现在很多情况都是用的Effect。由于美工方面比较差劲,在此仅给出其用法,具体的根据msdn和需求来调整。

  先记录一下BitmapEffect,在msdn上面看到属性已经过时了,但是4.0,4.5还在可以用,下面给出其派生类:

  • BevelBitmapEffect:斜角效果。
  • BitmapEffectGroup:符合效果。
  • BlurBitmapEffect:模糊效果。
  • DropShadowBitmapEffect:投影效果。
  • EmbossBitmapEffect:浮雕效果。
  • OuterGlowBitmapEffect: 外发光效果。

其用法比较简单,但是使用起来就要写美工基础了下面看一个例子。标签式写法如下:

<!--BlurBitmapEffect 浮雕效果-->
        <Image Source="美女.png" Grid.Column="0" Grid.Row="1"> 
            <Image.BitmapEffect>
                <BlurBitmapEffect Radius="10"/>
            </Image.BitmapEffect>
        </Image>
        <!--DropShadowBitmapEffect 投影效果-->
        <Button Width="100" Height="40" Content="哈哈" Grid.Column="0" Grid.Row="2"> 
            <Button.BitmapEffect>
                <DropShadowBitmapEffect Color="red" Direction="150" />
            </Button.BitmapEffect>
        </Button>

效果如图8:

图片 23

图8

 其他的用法都差不多,可以试着去玩一下。下面记录一下Effect。同样Effect也是UIElement的属性,其中Effect类有三个属性:

  • BlurEffect 模糊效果
  • DropShadowEffect 投影效果
  • ShaderEffect 着色器效果(抽象类)

  看了之后,有什么感想呢,怎么比BitmapEffect还少呢,但是有个抽象类,抽象类就是用来继承的,可以自己去写。想写多少种写多少种,关于前两种的效果使用方法和BitmapEffect的一样,主要说明一下抽象类,网上有很多写好的着色器的继承类,可以供我们使用。我在网上下载了一个WPFShaderEffectLibrary,在项目中先添加现有项,然后添加引用,之后我们就可以像模糊效果,投影效果一样的使用里面有重写的类了(本记录的练习代码我会在文章的最后提供下载),有个地方要注意的是,使用的时间要下加命名空间xmlns:selid="clr-namespace:ShaderEffectLibrary;assembly=ShaderEffectLibrary"。

        <Image Source="美女.png" Margin="15" Grid.Column="2">
            <Image.Effect>
                <selid:ZoomBlurEffect Center="0.5,0.5" BlurAmount="0.2"/>
            </Image.Effect>
        </Image>
        <Image Source="美女.png" Margin="15" Grid.Column="1">
            <Image.Effect>
                <selid:LightStreakEffect Attenuation="10" BrightThreshold="1" Scale="2"/>
            </Image.Effect>
        </Image>

看一下效果如图9:

图片 24

 图9

  怎么样呢?激动了吧!O(∩_∩)O~。赶紧去下载源码,悄悄她长得怎么样。好了,关键是记住使用的格式记住,其他的就要靠需求来使用滤镜了,好了,关于绘图的记录这个就到这里吧!下面进入图形的变形与动画。

  如果要更改圆点的数量,圆点的大小或者圆点的移动速度,我们该如何实现呢?继承章节一中的XAML,并根据所需调整模板就显得太麻烦了,这会让我们的样式文件显得臃肿不堪,所以采用纯粹的C#代码来实现它或许比较明智。不过之前的XAML也不是一无是处,至少它给出了环形进度条的关键帧动画的构成,这些信息对我们来说很重要,免去了我们自己去分析的步骤。

[WP8.1UI控件编程]SemanticZoom控件实现分组列表

三、图形的变形

  与其说是变形,不如说是变化,因为在WPF中的变形,不仅包括拉长,挤扁、放大、缩小等,还包括尺寸、位置、坐标比例、旋转角度等的变化。控制变形的属性有两个:

  1. RenderTransform:呈现变形,定义在UIElement类中。
  2. LayoutTransform:布局变形,定义在FrameworkElement类中。

  由于FrameworkElement类派生于UIelement类,而控件的基类Control类又派生于FrameworkElement类,所以说FrameworkElement类有两个属性。除此之外,还要知道上面的两个属性都是依赖属性,他们的类型为Transform 抽象类,此抽象类派生的类型有下面几个:

  • MatrixTransform:矩阵变形
  • RotateTransform:旋转变形
  • ScaleTransform:坐标变形
  • SkewTransform:拉伸变形
  • TranslateTransform:偏移变形
  • TransformGroup:变形组

下面来对比一下RenderTransform和LayoutTransform的区别。RenderTransform是不牵扯到布局的改变,只涉及到窗体的重绘。如果不理解的话,我们就从一个例子看一下。我在一个Grid上面,把Grid分为两列,其中第一列为自适应高度,后面的一列为剩余的部分,然后在第一列中放一个TextBlock,分别用两种变形来实现。 代码已经给出,如下:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid x:Name="titleBar" Background="LightBlue" Grid.Column="0">
        <TextBlock Text="Hello Tranformer!" FontSize="24" HorizontalAlignment="Left" VerticalAlignment="Bottom">
            <!--<TextBlock.RenderTransform>
                <RotateTransform Angle="-90"/>
            </TextBlock.RenderTransform>-->
            <TextBlock.LayoutTransform>
                <RotateTransform Angle="-90"/>
            </TextBlock.LayoutTransform>         
        </TextBlock>
    </Grid>
</Grid>

我们看一下其效果如图10:

图片 25

图10

   布局变形,真的是会布局会发生改变。呈现变形,只负责本身的形状,不管布局。所以如果是动画制作的话,如涉及到变形的话,应该使用RenderTransform。本记录重点是动画,所以还是看看呈现变形在动画里面是怎么表现的。

  现在我们的主要工作就是让写死的关键帧能够通过属性灵活配置,所以我们可能需要先编码一份进度条的基类( LoadingBase ),以提取两种类型进度条的共性。基类中定义8个属性,分别是 IsRunning 、 DotCount 、 DotInterval 、 DotBorderBrush 、 DotBorderThickness 、 DotDiameter 、 DotSpeed 、 DotDelayTime ,它们的含义已经是自注释的,不必赘述。而在环形进度条中,还有另外两个属性: DotOffSet 和 NeedHidden ,分别表示圆点整体的位置偏移和在运动中是否需要隐藏圆点。

[WP8.1UI控件编程]Windows Phone VirtualizingStackPanel、ItemsStackPanel和ItemsWrapGrid虚拟化排列布局控件

 四、动画

 

[WP8.1UI控件编程]Windows Phone大数据量网络图片列表的异步加载和内存优化

4.1 认识动画

   看到动画两个字,我们应该很快想到了动画片,动画片是一个或多个对象,在特定的时间段里,作出不同的变化。同样在WPF的动画中,原理和动画片的相似,只不过我们现在成了动画片的制作者,作为制作者,我们要思考某个对象做哪些动作,想好了之后,要思考哪些对象在哪些时间开始组合....最终就形成了“动画片”。简单的动画,由一个元素就可以完成了,WPF中的简单的动画称为AnimationTimeline,复杂的动画就需要多个元素相互协同完成,就像一段话剧,我们称之为Storyborad。我们可以通过转到定义,发现他们都继承自Timeline类。故事再好,都少不了一个载体,不是舞台,是时间。这也让我想起一句话,人生像一场戏,好坏全靠演技。所以说,故事就是时间的累积。还有一个要强调的是,WPF规定,可以用来制作动画的属性必须是依赖属性。好了,还是分别看一下WPF中的故事吧!

三、关键帧动画

 

4.2 简单动画

在介绍简单动画之前还要看一下AnimationTimeline的派生类:

  •           System.Windows.Media.Animation.BooleanAnimationBase
  •               System.Windows.Media.Animation.ByteAnimationBase
  •               System.Windows.Media.Animation.CharAnimationBase
  •               System.Windows.Media.Animation.ColorAnimationBase
  •               System.Windows.Media.Animation.DecimalAnimationBase
  •               System.Windows.Media.Animation.DoubleAnimationBase
  •               System.Windows.Media.Animation.Int16AnimationBase
  •               System.Windows.Media.Animation.Int32AnimationBase
  •               System.Windows.Media.Animation.Int64AnimationBase
  •               System.Windows.Media.Animation.MatrixAnimationBase
  •               System.Windows.Media.Animation.ObjectAnimationBase
  •               System.Windows.Media.Animation.Point3DAnimationBase
  •               System.Windows.Media.Animation.PointAnimationBase
  •               System.Windows.Media.Animation.QuaternionAnimationBase
  •               System.Windows.Media.Animation.RectAnimationBase
  •               System.Windows.Media.Animation.Rotation3DAnimationBase
  •               System.Windows.Media.Animation.SingleAnimationBase
  •               System.Windows.Media.Animation.SizeAnimationBase
  •               System.Windows.Media.Animation.StringAnimationBase
  •               System.Windows.Media.Animation.ThicknessAnimationBase
  •               System.Windows.Media.Animation.Vector3DAnimationBase
  •               System.Windows.Media.Animation.VectorAnimationBase

   由***Base看出都是基类,下面的一层才是具体的动画。为了保持和书中例子一致,我们就以DoubleAnimationBase为基类展开,其他的再慢慢去了解和摸索。一种就是点到点的的动画DoubleAnimation,一种是可以分为帧的动画DoubleAnimationUsingKeyFrames,还有一种是按照路径来执行的DoubleAnimationUsingPath的动画。简单动作由以下几个部分构成:变化起点(From属性),变化终点(To属性),变化幅度(By属性),变化时间(Duration属性)。如果指定的有终点那么幅度就被忽略了,如果没有起点,就以当前元素所在位置为起点。还是看个例子来的更易理解。下面演示一个按钮如果被点击了,在0.3s里,按钮朝着x,y轴上300个单位随机移动。下面给出代码

<Grid>
    <Button x:Name="btn" Content="Move!" HorizontalAlignment="Left" VerticalAlignment="top" Width="60" Height="60" Click="Button_Click">
            <Button.RenderTransform>
                <TranslateTransform x:Name="tt" X="0" Y="0"/>
            </Button.RenderTransform>
    </Button>
</Grid>

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //定义简单动画的实例
              DoubleAnimation daX = new DoubleAnimation();
            DoubleAnimation daY = new DoubleAnimation();

            //指定起点
             daX.From = 0D;
            daY.From = 0D;

            //指定终点
            Random r = new Random();
            daX.To = r.NextDouble() * 300;
            daY.To = r.NextDouble() * 300;

            //daX.By = 100D;
            //daY.By = 100D;
            //指定时长300ms
            Duration duration=new Duration(TimeSpan.FromMilliseconds(300));
            daY.Duration = duration;
            daX.Duration = duration;

            //将动画添加到偏移变形的实例上面 和Binding的格式有点像
            //this.textBox.SetBinding(TextBox.TextProperty,binding)

            //让按钮发生改变作为动画
            //btn.BeginAnimation(Button.WidthProperty, daX);
            //btn.BeginAnimation(Button.HeightProperty, daY);

            //让 位置发生改变作为动画
            this.tt.BeginAnimation(TranslateTransform.XProperty, daX);
            this.tt.BeginAnimation(TranslateTransform.YProperty, daY);
        }

   这个过程还真有点难表述,建议下载源代码看效果了,上面注意一点就是发生动画的是TranslateTransform,不是按钮的大小,可以把按钮的注释去掉查看效果。在上面代码中,就是我们拍好的片子,等到按钮点击就是播放了。除了直线运动,还可以设置高级的运动,源码上面也有个例子(AdvancedAnimation.xaml文件),其他属性参考msdn。

  最后一步就是用C#代码实现关键帧动画,不过得先有米才能做饭,故而需要先创建圆点:

目录如下:

4.3 关键帧动画   

   先理解一下帧的概念,帧也就每次属性改变都会产生一个新画面,新画面就是一个帧。帧的连续播放产生了动画。DoubleAnimationUsingKeyFrames的实例中通常是含有多个DoubleKeyFrame类的帧,具体的有下面四种:

  • LinearDoubleKeyFrame,线性帧,目标属性值的变化是直线型的,匀速的。
  • DiscreteDoubleKeyFrame,不连续变化的帧,目标属性值是跳跃的。
  • SplineDoubleKeyFrame, 样条函数变化帧,目标属性值的速率是一条贝赛尔曲线。
  • EasingDoubleKeyFrame,缓冲式帧,目标属性值以某种缓冲形式变化。

 LinearDoubleKeyFrame类的帧是时间点和值,DoubleAnimationUsingKeyFrames依赖于LinearDoubleKeyFrame的时间和值。下面看一个让按钮做“z”字型运动的构思:

  //定义两个DoubleAnimationUsingKeyFrames类型的实例,来控制呈现变形的横纵坐标
            DoubleAnimationUsingKeyFrames dakX = new DoubleAnimationUsingKeyFrames();
            DoubleAnimationUsingKeyFrames dakY = new DoubleAnimationUsingKeyFrames();

            //指定时长
            dakX.Duration = new Duration(TimeSpan.FromMilliseconds(900));
            dakY.Duration = new Duration(TimeSpan.FromMilliseconds(900));

            //纵坐标====================================================
            //动画分成三段,所以有三个线性关键帧
            LinearDoubleKeyFrame x_kf_1 = new LinearDoubleKeyFrame();
            LinearDoubleKeyFrame x_kf_2 = new LinearDoubleKeyFrame();
            LinearDoubleKeyFrame x_kf_3 = new LinearDoubleKeyFrame();

            //为三段关键帧赋值(时间和属性的值),并添加到动画中
            x_kf_1.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300));
            x_kf_1.Value = 200;
            x_kf_2.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(600));
            x_kf_2.Value = 0;
            x_kf_3.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(900));
            x_kf_3.Value = 200;

            dakX.KeyFrames.Add(x_kf_1);
            dakX.KeyFrames.Add(x_kf_2);
            dakX.KeyFrames.Add(x_kf_3);
            //纵坐标====================================================
            LinearDoubleKeyFrame y_kf_1 = new LinearDoubleKeyFrame();
            LinearDoubleKeyFrame y_kf_2 = new LinearDoubleKeyFrame();
            LinearDoubleKeyFrame y_kf_3 = new LinearDoubleKeyFrame();

            y_kf_1.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300));
            y_kf_1.Value = 0;
            y_kf_2.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(600));
            y_kf_2.Value = 180;
            y_kf_3.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(900));
            y_kf_3.Value = 180;

            dakY.KeyFrames.Add(y_kf_1);
            dakY.KeyFrames.Add(y_kf_2);
            dakY.KeyFrames.Add(y_kf_3);

            //把动画寄托在呈现变形中
            this.tt.BeginAnimation(TranslateTransform.XProperty, dakX);
            this.tt.BeginAnimation(TranslateTransform.YProperty, dakY);

   上面代码中横纵坐标有三次变化(0,0)->(200,0)->(0,180)->(200,180).关于贝塞尔的例子(在源码中有个SplineDoubleKeyFrame.xaml)可以参考一下。

 1 protected Ellipse CreateEllipse(int index)
 2         {
 3             var ellipse = new Ellipse();
 4             ellipse.SetBinding(WidthProperty, new Binding("DotDiameter") {Source = this});
 5             ellipse.SetBinding(HeightProperty, new Binding("DotDiameter") {Source = this});
 6             ellipse.SetBinding(Shape.FillProperty, new Binding("Foreground") {Source = this});
 7             ellipse.SetBinding(Shape.StrokeThicknessProperty, new Binding("DotBorderThickness") {Source = this});
 8             ellipse.SetBinding(Shape.StrokeProperty, new Binding("DotBorderBrush") {Source = this});
 9             return ellipse;
10         }

《深入理解Windows Phone 8 .1 UI控件编程》目录

 4.4 路径动画

   前面已经介绍了路径绘图时的强大,那么我们能不能让我的动画按照我们制定的路径去表演呢,答案是可以的。这就是我们要记录的DoubleAnimationUsingPath类。注意它有三个属性很关键,其中Duration是每个动画必须有的,另外两个是Source属性和PathGeometry分别用来指定向那个方向移动和路径。下面给出一个按钮沿路径移动的动画,构思如下:

    <Window.Resources>
        <PathGeometry x:Key="movingPath" Figures="M 40,110 A 50,50 0 1 1 100,60 A110,95 0 0 1 200,60 A 50,50 0 1 1 250 100 A 110,95 0 1 1 55,100 Z"/>
    </Window.Resources>
    <Grid x:Name="grid" HorizontalAlignment="Left" VerticalAlignment="Top">
        <Path x:Name="movingPath" Data="M 40,110 A 50,50 0 1 1 100,60 A110,95 0 0 1 200,60 A 50,50 0 1 1 250 100 A 110,95 0 1 1 55,100 Z" Stroke="Red"
           StrokeThickness="2" Visibility="Visible"/>
        <Button x:Name="btn" Height="30" Width="80" Content="路径动画" Click="btn_Click" Margin="0,0,219,210">
            <Button.RenderTransform>
                <TranslateTransform x:Name="tt" X="0" Y="0"/>
            </Button.RenderTransform>
            <Button.Effect>
                <DropShadowEffect BlurRadius="45" Color="Red" />
            </Button.Effect>
        </Button>
    </Grid>

            PathGeometry pg =this.FindResource("movingPath") as PathGeometry;
            Duration duration = new Duration(TimeSpan.FromMilliseconds(600));

            DoubleAnimationUsingPath dakX = new DoubleAnimationUsingPath();
            dakX.PathGeometry = pg;
            dakX.Source = PathAnimationSource.X;
            dakX.Duration = duration;


            DoubleAnimationUsingPath dakY = new DoubleAnimationUsingPath();
            dakY.PathGeometry = pg;
            dakY.Source = PathAnimationSource.Y;
            dakY.Duration = duration;

            this.tt.BeginAnimation(TranslateTransform.XProperty, dakX);
            this.tt.BeginAnimation(TranslateTransform.YProperty, dakY);

上面的代码不是非常完善,仅作为认识路径动画的一个途径。

  上面的方法在进度条基类中实现,仅仅是用相关的属性初始化了我们的原材料:圆点。由于环形进度条在X、Y轴方向都有移动,所以为了方便,我们可以考虑在圆点外面再包一层 Border 作为看不见的壳,我们将圆点与壳底部对齐,现在只要让壳绕中心旋转就基本实现了目标,下面是环形进度条1个点到5个点带壳的示意图:

第1章 深入解析程序界面

4.5 场景(Storyborad)

  关键帧动画是串在一起的,让一个完整的TimeLine分为多个帧,场景强调的是并发执行,把多个动画同时进行。

图片 26

图11

  下面看一个例子:布局图如上图(图11),点击按钮时,三个小球向目标前进,其中一个小球的XAML代码:

        <Border BorderBrush="Gray" BorderThickness="1" Grid.Row="1">
            <Ellipse x:Name="ballG" Height="80" Width="80" Fill="Green" HorizontalAlignment="Left">
                <Ellipse.RenderTransform>
                    <TranslateTransform x:Name="ttG"/>
                </Ellipse.RenderTransform>
            </Ellipse>
        </Border>

对应的cs代码,注释已经给出:

            //定义动画要执行的时长
            Duration duation = new Duration(TimeSpan.FromMilliseconds(600));

            //定义一个简单的移动——匀速直线运动
            DoubleAnimation daRx = new DoubleAnimation();
            daRx.Duration = duation;
            daRx.To = 400;

            //定义一个关键帧的移动,目标属性值的速率是一条贝赛尔曲线函数
            DoubleAnimationUsingKeyFrames dakGx = new DoubleAnimationUsingKeyFrames();
            dakGx.Duration = duation;
            SplineDoubleKeyFrame kfG = new SplineDoubleKeyFrame(400, KeyTime.FromPercent(1));
            kfG.KeySpline = new KeySpline(1, 0, 0, 1);
            dakGx.KeyFrames.Add(kfG);

            //定义一个关键帧的移动,目标属性值的速率是一条贝赛尔曲线函数
            DoubleAnimationUsingKeyFrames dakBx = new DoubleAnimationUsingKeyFrames();
            dakBx.Duration = duation;
            SplineDoubleKeyFrame kfB = new SplineDoubleKeyFrame(400, KeyTime.FromPercent(1));
            kfB.KeySpline = new KeySpline(0, 1, 1, 0);
            dakBx.KeyFrames.Add(kfB);

            Storyboard storyboard = new Storyboard();

            //使指定的动画的UI载体
            Storyboard.SetTargetName(daRx, "ttR");
            Storyboard.SetTargetName(dakGx, "ttG");
            Storyboard.SetTargetName(dakBx, "ttB");

            //使动画与UI载体的属性相关联
            Storyboard.SetTargetProperty(daRx,new PropertyPath(TranslateTransform.XProperty));
            Storyboard.SetTargetProperty(dakGx, new PropertyPath(TranslateTransform.XProperty));
            Storyboard.SetTargetProperty(dakBx, new PropertyPath(TranslateTransform.XProperty));

            //指定场景的时间,并把各个对像的动画添加到场景里面
            storyboard.Duration = duation;
            storyboard.Children.Add(daRx);
            storyboard.Children.Add(dakGx);
            storyboard.Children.Add(dakBx);

            storyboard.Begin(this);

  通过本例子应该对场景有个印象,但是离运用应该还有一段的差距,先就到这里吧!有时间好好的研究一下!

图片 27

1.1 XAML的原理

五、总结

  本篇记录了关于WPF中的绘画类和与动画有关的几个类,使我对其有了初步的认识,关于这方面的知识,还需要深入去理解。下面把源码附上:源码。欢迎交流!下一篇,我将把本系列的源码和目录整理一下,顺便把电子书一并上传。供大家参考学习。

  想一想,如果没有这层壳,我们又有什么替代方法,这些方法是否都是极为方便的?可能没有这层壳,就需要去琢磨怎么改变圆点的 RenderTransformOrigin ,好让它们看起来都是围绕一个点旋转的,即使改变了进度条整体的尺寸。套壳的代码如下:

    1.1.1 XAML的概念

 1 private Border CreateBorder(int index)
 2         {
 3             var ellipse = CreateEllipse(index);
 4             ellipse.HorizontalAlignment = HorizontalAlignment.Center;
 5             ellipse.VerticalAlignment = VerticalAlignment.Bottom;
 6             var rt = new RotateTransform
 7             {
 8                 Angle = -DotInterval * index
 9             };
10             var myTransGroup = new TransformGroup();
11             myTransGroup.Children.Add(rt);
12             var border = new Border
13             {
14                 RenderTransformOrigin = new Point(0.5, 0.5),
15                 RenderTransform = myTransGroup,
16                 Child = ellipse,
17                 Visibility = NeedHidden ? Visibility.Collapsed : Visibility.Visible
18             };
19             border.SetBinding(WidthProperty, new Binding("Width") { Source = this });
20             border.SetBinding(HeightProperty, new Binding("Height") { Source = this });
21 
22             return border;
23         }

    1.1.2 XAML页面的编译

  套壳代码除了套壳和相关的初始化,最重要的是19和20行的宽高绑定,这是让圆点旋转中心始终唯一的关键。有了以上的准备,我们终于可以开始for循环了:

    1.1.3 动态加载XAML

 1 //定义动画
 2 Storyboard = new Storyboard
 3 {
 4     RepeatBehavior = RepeatBehavior.Forever
 5 };
 6 
 7 for (var i = 0; i < DotCount; i  )
 8 {
 9     //在这里创建圆点  
10 }

1.2 XAML的树结构

  下面就是最核心的关键帧动画,通过之前用Blend提取出来的XAML,我们可以看到它使用了 SplineDoubleKeyFrame ,这会涉及三次贝塞尔曲线的控制点,考虑到易用性,我们会用 LinearDoubleKeyFrame 和 EasingDoubleKeyFrame 代替。在XAML中我们最关心的关键字应该是角度,在时间片的哪部分,圆点应该在哪儿,而又在什么时候,圆点应该会消失,我们只要随意截取两个点的关键帧就能获得以上所有信息:

    1.2.1 可视化树

图片 28

    1.2.2 VisualTreeHelper类

图片 29

    1.2.3 遍历可视化树

 

    1.2.4 可视化树应用示例:实现ListBox控件分页加载

  上面两张分别是圆点1和2透明度和位置的关键帧截图,通过两个点我们完全可以推断所有点。出于个人喜好,我将透明度替换成了 Visibility 的切换,所以还会引入 DiscreteObjectKeyFrame 。篇幅原因,我们直接总结分析结果:

1.3 路由事件

  • 一开始所有点都是显示的,但是位置不同,从点1的-110度开始,角度逐个减6;
  • 点1开始运动后,0.167秒(1/6秒)后点2开始运动,所以各点动画延迟时间为1/6秒(这里不太能确定是否和圆点数量有关);
  • 以点1为例,旋转角度随时间变化图如下:

    1.3.1 Windows Phone事件

图片 30图片 31图片 32图片 33图片 34图片 35图片 36

    1.3.2 路由事件的概念

  从上面7张图中可以看出,在一次循环中点1是这样运动的:减速、匀速、加速、减速、匀速、加速,而且与之对应的角度位置也给出了,最后水到渠成,环形进度条就完成了。

    1.3.4 路由事件原理

 

    1.3.5 路由事件的作用和演示

四、截图

1.4框架和页面

  通过设置不同的属性,可以实现不同的效果:

    1.4.1 框架页面结构

  图片 37

    1.4.2 页面导航

 

    1.4.3 框架的应用示例:自定义弹出窗口

五、源码

1.5 UI线程

  本文所讨论的进度条源码已经在github开源:

第2章 样式和模板

2.1 样式

    2.1.1 创建样式

    2.1.2 样式继承

    2.1.3 以编程方式设置样式

    2.1.4 样式文件

    2.1.5 系统主题

    2.1.6 主题资源

    2.1.7 自定义主题

2.2 模板

    2.2.1 控件模板(ControlTemplate)         

    2.2.2 ContentControl和ContentPresenter

    2.2.3 视觉状态管理(VisualStatesManager)

    2.2.4 数据模板(DataTemplate)

    2.2.5 ItemTemplate、ContentTemplate和DataTemplate

    2.2.6 数据模板的使用

    2.2.7 读取和更换数据模板

第3章 布局原理

3.1 布局原理

    3.1.1 布局的意义

    3.1.2 系统的布局面板

    3.1.3 布局系统

    3.1.4 布局系统的重要方法和属性

    3.1.5 测量和排列的过程

    3.1.6 多分辨率的适配布局

3.2 自定义布局规则

    3.2.1 创建布局类

    3.2.2 实现测量过程

    3.2.3 实现排列过程

    3.2.4 应用布局规则

第4章 图形绘图

4.1 图形原理

    4.1.1 图形中常用的结构

    4.1.2 画图相关的类

    4.1.3 基础的图形形状

4.2 Path图形 

    4.2.1 两种Path图形的创建方法

    4.2.2 使用简单的几何图形来创建Path     

    4.2.3 使用PathGeometry来创建Path        

    4.2.4 使用路径标记语法创建Path     

    4.2.5 使用Path实现自定义图形

    4.2.6 利用Expression Blend工具创建Path图形        

4.3 画刷

    4.3.1 SolidColorBrush画刷    

    4.3.2 LinearGradientBrush画刷    

    4.3.3 ImageBrush画刷  

4.4 图形裁剪

    4.4.1 使用几何图形进行剪裁      

    4.4.2 对布局区域进行剪裁 

第5章 图表编程

5.1动态生成折线图和区域图

    5.1.1折线图和区域图原理

    5.1.2 生成图形逻辑封装

5.2 实现饼图控件

    5.2.1 自定义饼图片形形状

    5.2.2 封装饼图控件

5.3 线性报表

    5.3.1 实现图形表格和坐标轴

    5.3.2 定义线性数据图形类

    5.3.3 实现图例

    5.3.4 实现线性报表

5.4 QuickCharts图表控件库解析

    5.4.1 QuickCharts项目结构分析

    5.4.2 饼图图表PieChart的实现逻辑

    5.4.3 连续图形图表SerialChart的实现逻辑

第6章 变换特效和三维特效

6.1 变换特效

    6.1.1 变换的原理二维变换矩阵

    6.1.2 平移变换TranslateTransform

    6.1.3 旋转变换RotateTransform

    6.1.4 缩放变换ScaleTransform

    6.1.5 扭曲变换SkewTransform

    6.1.6 组合变换TransformGroup

    6.1.7 矩阵变换MatrixTransform 

6.2 三维特效

    6.2.1 三维坐标体系

    6.2.2 三维旋转

    6.2.3 三维平移

    6.2.4 用矩阵实现三维特效

第7章 动画编程基础

7.1 动画原理

    7.1.1 理解动画

    7.1.2 动画的目标属性

    7.1.3 动画的类型

7.2 线性插值动画

    7.2.1 动画的基本语法

    7.2.2 线性动画的基本语法

    7.2.3 DoubleAnimation实现变换动画

    7.2.4 ColorAnimation实现颜色渐变动画

    7.2.5 PointAnimation实现Path图形动画

7.3 关键帧动画

    7.3.1 关键帧动画概述

    7.3.2 线性关键帧

    7.3.3 样条关键帧

    7.3.4 离散关键帧

7.4 缓动函数动画

    7.4.1 缓动函数动画概述

    7 .4.2 BackEase动画

    7.4.3 BounceEase动画

    7.4.4 CircleEase动画

    7.4.5 CubicEase动画

    7.4.6 ElasticEase动画

    7.4.7 ExponentialEase动画

    7.4.8 PowerEase/QuadraticEase/QuarticEase/QuinticEase动画

    7.4.9 SineEase动画

7.5 基于帧动画

    7.5.1 基于帧动画的原理

    7.5.2 基于帧动画的应用场景

    7.5.3 基于帧动画的实现

第8章 动画进阶演练

8.1 动画方案的选择

    8.1.1 帧速率

    8.1.2 UI线程和构图线程

    8.1.3 选择最优的动画方案

8.2 列表动画

    8.2.1 实现的思路

    8.2.2 使用附加属性控制动画对象

    8.2.3 列表切换缓动动画实现

    8.2.4 退出页面的三维动画实现

    8.2.5 列表动画的演示

8.3 模拟实现微信的彩蛋动画

    8.3.1 实现的思路

    8.3.2 星星创建工厂

    8.3.3 实现单个星星的动画轨迹

    8.3.4 封装批量星星飘落的逻辑

    8.3.5 星星飘落动画演示

8.4 决斗游戏动画

    8.4.1 实现的思路

    8.4.2 初始页面的布局

    8.4.3 人物走路动画

    8.4.4 决斗开枪动画

第9章 深入解析控件编程

9.1 系统控件原理解析

    9.1.1 系统控件分类

    9.1.2 系统控件的默认样式

    9.1.3 深度改造系统控件

9.2 UserControl自定义控件——水印输入框控件

    9.2.1 UserControl自定义控件的原理

    9.2.2 创建水印输入框控件

    9.2.3 添加水印输入框控件属性和事件的处理

    9.2.4 使用水印输入框控件

9.3 从控件基类派生实现自定义控件——全屏进度条控件

    9.3.1 创建控件样式

    9.3.2 加载样式

    9.3.3 全屏进度条的打开和关闭

    9.3.4 处理物理返回事件

    9.3.5 全屏进度条控件的使用

第10章 Expression Blend工具的使用

10.1 Expression Blend概述

    10.1.1 视图

    10.1.2 工作区

10.2 主要的面板

    10.2.1美工板

    10.2.2 资产面板

    10.2.3 工具面板

    10.2.4 对象和时间线面板

    10.2.5 属性面板

10.3 Expression Blend for Windows Phone的特色功能

    10.3.1 选择设备的效果

    10.3.2 预览 Windows Phone 样式

    10.3.3 定义应用程序菜单栏

10.4 Expression Blend绘图

    10.4.1 绘图基础

    10.4.2 使用“笔”绘制路径

    10.4.3 合并路径

    10.4.4 实例演练——绘制一个表情图形

10.5 Expression Blend制作动画

    10.5.1 情节提要

    10.5.2 时间线

    10.5.3 Expression Blend的关键帧

    10.5.4 实例演练——制作小球掉落反弹动画

第11章 列表编程

11.1 列表控件的使用

    11.1.1 ItemsControl实现最简洁的列表

    11.1.2 ListBox实现下拉点击刷新列表         

    11.1.3 ListView实现下拉自动刷新列表

    11.1.4 GridView实现网格列表

    11.1.5 SemanticZoom实现分组列表

11.2 虚拟化技术

    11.2.1 列表的虚拟化

    11.2.2 VirtualizingStackPanel、ItemsStackPanel和ItemsWrapGrid虚拟化排列布局控件

    11.2.3 实现横向虚拟化布局

    11.2.4 大数据量网络图片列表的异步加载和内存优化

第12章 Toolkit控件库的技术原理分析

12.1 Toolkit控件库项目简介

12.2 CustomMessageBox控件原理解析

    12.2.1 CustomMessageBox的调用逻辑

    12.2.2 CustomMessageBox的样式和弱引用的使用

12.3 PhoneTextBox控件原理解析         

    12.3.1 PhoneTextBox的调用逻辑

    12.3.2 PhoneTextBox的封装逻辑

12.4 ToggleSwitch控件原理解析

    12.4.1 ToggleSwitch的调用逻辑

    12.4.2 ToggleSwitch和ToggleSwitchButton的样式

    12.4.3 ToggleSwitch对拖拽手势的判断

12.5 ListPicker控件原理解析

    12.5.1 ListPicker的调用逻辑

    12.5.2 ListPicker控件主要逻辑的分析

12.6 WrapPanel控件原理解析

    12.6.1 WrapPanel控件的调用逻辑

    12.6.2 WrapPanel布局控件的测量排列逻辑

 

京东购买地址:

图片 38

本文由pc28.am发布于计算机编程,转载请注明出处:基于最新的Runtime框架,仿制Windows10的进度条

上一篇:VS二零零六编写翻译器下ACE的布局,CPP代码审计 下一篇:没有了
猜你喜欢
热门排行
精彩图文