New UWP Community Toolkit - RotatorTile
概述
UWP Community Toolkit 中有一个为图片或磁贴提供轮播效果的控件 - RotatorTile,本篇我们结合代码详细讲解 RotatorTile 的实现。
RotatorTile 提供了一种类似 Windows 10 磁贴的轮播方式,可以轮流播放开发者设置的内容序列,支持设置轮播方向,包括上下左右四个方向;接下来看看官方示例的截图:
Doc: https://docs.microsoft.com/zh-cn/windows/uwpcommunitytoolkit/controls/rotatortile
Namespace: Microsoft.Toolkit.Uwp.UI.Controls; Nuget: Microsoft.Toolkit.Uwp.UI.Controls;
开发过程
代码分析
RotatorTile 控件包括 RotatorTile.cs 和 RotatorTile.xaml,分别是控件的定义处理类和样式文件,分别来看一下:
1. RotatorTile.xaml
RotatorTile.xaml 是 RotatorTile 控件的样式文件,我们看 Template 部分,轮播效果的实现主要是靠 StackPanel 中排列的两个 Content,分别代表 current 和 next 内容,根据设置的轮播方向,设置 StackPanel 的排列方向;轮播时,使用 TranslateTransform 来实现轮播的元素切换动画;
<Style TargetType="controls:RotatorTile"> <Setter Property="IsTabStop" Value="False"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="controls:RotatorTile"> <Grid Background="{TemplateBinding Background}"> <Canvas x:Name="Scroller" DataContext="{x:Null}"> <StackPanel x:Name="Stack"> <StackPanel.RenderTransform> <TranslateTransform x:Name="Translate" Y="0" /> </StackPanel.RenderTransform> <ContentPresenter x:Name="Current" Content="{Binding}" ContentTemplate="{TemplateBinding ItemTemplate}" DataContext="{x:Null}" /> <ContentPresenter x:Name="Next" Content="{Binding}" ContentTemplate="{TemplateBinding ItemTemplate}" DataContext="{x:Null}" /> </StackPanel> </Canvas> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="RotationDelay" Value="0:0:5" /> <Setter Property="ExtraRandomDuration" Value="0:0:5" /> </Style>
2. RotatorTile.cs
RotatorTile 控件的定义和主要处理类,来看看类的结构:
首先看一下 OnApplyTemplate() 方法,他会获取控件的模板,根据当前轮播方向处理 StackPanel 容器,初始化并开始轮播动画;这也是 RotatorTile 控件的主要流程:使用 Timer,根据设置的间隔时间和轮播的方向,在 Tick 事件中不断按照某个方向去做平移动画,动画中不断更新当前显示元素为下一个元素,并不断相应中途的显示元素集合变化事件;
同时控件会响应 RotatorTile_SizeChanged 事件,根据新的尺寸去修改显示元素和容器的尺寸;响应 RotatorTile_Loaded 和 RotatorTile_Unloaded,处理 Timer 的开始和结束处理;
RotatorTile.cs 继承自 Control 类,先看一下它定义了哪些依赖属性:
- ExtraRandomDuration - 一个随机时间区间的上限,轮播时一个 0~ExtraRandomDuration 的随机值会被作为轮播间隔使用;
- RotationDelay - 轮播的间隔,时间修改时会触发 OnRotationDelayInSecondsPropertyChanged 事件;
- ItemsSource - 轮播内容集合的数据源,变化时触发 OnItemsSourcePropertyChanged 事件;
- ItemTemplate - 轮播内容的内容模板;
- RotateDirection - 轮播的方向,分别上 下 左 右四个方向;
- CurrentItem - 轮播时当前的 Item,变化时触发 OnCurrentItemPropertyChanged 事件;
首先来看 OnItemsSourcePropertyChanged 事件,它的主要逻辑在方法 Incc_CollectionChanged(s, e) 中:
- 首先 action 处理会被分为 5 种:Remove,Add,Replace,Move 和 Reset;
- 对 Remove action,根据删除后的开始索引与当前索引,结束索引之间的关系,去更新下一个元素,或设置当前索引,或更新上下文;
- 对 Add action,根据添加后的开始索引与当前索引的关系,以及当前索引与 0 的关系,去开始轮播,或设置当前索引,或更新上下文;
- 对 Replace action,如果当前索引介于新的开始索引和结束索引之间,则更新下一个元素;
- 对 Move action,如果当前索引介于新的开始索引和结束索引之间,获取它的新索引;
- 对 Reset action,重新开始轮播;
private void Incc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Remove) { ) { int endIndex = e.OldStartingIndex + e.OldItems.Count; if (_currentIndex >= e.NewStartingIndex && _currentIndex < endIndex) { // Current item was removed. Replace with the next one UpdateNextItem(); } else if (_currentIndex > endIndex) { // Items were removed before the current item. Just update the changed index _currentIndex -= (endIndex - e.NewStartingIndex) - ; } ) { // Upcoming item was changed, so update the datacontext _nextElement.DataContext = GetNext(); } } } else if (e.Action == NotifyCollectionChangedAction.Add) { int endIndex = e.NewStartingIndex + e.NewItems.Count; ) { ) { // First item loaded. Start the rotator Start(); } else if (_currentIndex >= e.NewStartingIndex) { // Items were inserted before the current item. Update the index _currentIndex += e.NewItems.Count; } == e.NewStartingIndex) { // Upcoming item was changed, so update the datacontext _nextElement.DataContext = GetNext(); } } } else if (e.Action == NotifyCollectionChangedAction.Replace) { int endIndex = e.OldStartingIndex + e.OldItems.Count; ) { // Current item was removed. Replace with the next one UpdateNextItem(); } } else if (e.Action == NotifyCollectionChangedAction.Move) { int endIndex = e.OldStartingIndex + e.OldItems.Count; if (_currentIndex >= e.OldStartingIndex && _currentIndex < endIndex) { // The current item was moved. Get its new location _currentIndex = GetIndexOf(CurrentItem); } } else if (e.Action == NotifyCollectionChangedAction.Reset) { // Significant change or clear. Restart. Start(); } }
接着来看 OnCurrentItemPropertyChanged(d, e) 方法的处理,主要处理逻辑在 RotateToNextItem() 中:
- 首先判断是否有两个或者更多的元素,如果没有则退出处理;
- 定义 Storyboard,动画时间是 500ms,方向和轮播的目标属性根据当前轮播的方向去计算;
- 在动画结束时,开始准备下一个显示的元素;
private void RotateToNextItem() { // Check if there's more than one item. if not, don't start animation bool hasTwoOrMoreItems = false; ... if (!hasTwoOrMoreItems) { return;} var sb = new Storyboard(); if (_translate != null) { var anim = new DoubleAnimation { Duration = )), From = }; if (Direction == RotateDirection.Up) { anim.To = -ActualHeight; } else if (Direction == RotateDirection.Down) {...} else if (Direction == RotateDirection.Right) {...} else if (Direction == RotateDirection.Left) {...} anim.FillBehavior = FillBehavior.HoldEnd; anim.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut }; Storyboard.SetTarget(anim, _translate); if (Direction == RotateDirection.Up || Direction == RotateDirection.Down) { Storyboard.SetTargetProperty(anim, "Y"); } else { Storyboard.SetTargetProperty(anim, "X"); } sb.Children.Add(anim); } sb.Completed += async (a, b) => { if (_currentElement != null) { _currentElement.DataContext = _nextElement.DataContext; } // make sure DataContext on _currentElement has had a chance to update the binding // avoids flicker on rotation ); // Reset back and swap images, getting the next image ready sb.Stop(); if (_translate != null) { UpdateTranslateXY(); } if (_nextElement != null) { _nextElement.DataContext = GetNext(); // Preload the next tile } }; sb.Begin(); }
我们看到有两个方法中都调用了 UpdateTranslateXY() 方法,来更新平移时的 X 或 Y:
对于 Left 和 Up,只需要充值 X 或 Y 为 0;对于 Right 和 Down,需要把对应的 X 或 Y 设置为 -1 × 对应的高度或宽度,让动画从负一倍尺寸平移到 0;
private void UpdateTranslateXY() { if (Direction == RotateDirection.Left || Direction == RotateDirection.Up) { _translate.X = _translate.Y = ; } else if (Direction == RotateDirection.Right) { _translate.X = - * ActualWidth; } else if (Direction == RotateDirection.Down) { _translate.Y = - * ActualHeight; } }
调用示例
我们定义了一个 RotatorTile,动画间隔 1s,方向向上,来看一下 gif 图显示的运行结果:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <controls:RotatorTile x:Name="Tile1" Height="200" Background="LightGray" RotationDelay="0:0:1" ExtraRandomDuration="0:0:1" Direction="Up" ItemTemplate="{StaticResource PhotoTemplate}" /> </Grid>
总结
到这里我们就把 UWP Community Toolkit 中的 RotatorTile 控件的源代码实现过程和简单的调用示例讲解完成了,希望能对大家更好的理解和使用这个控件有所帮助。欢迎大家多多交流,谢谢!
最后,再跟大家安利一下 UWPCommunityToolkit 的官方微博:https://weibo.com/u/6506046490, 大家可以通过微博关注最新动态。
衷心感谢 UWPCommunityToolkit 的作者们杰出的工作,Thank you so much, UWPCommunityToolkit authors!!!
New UWP Community Toolkit - RotatorTile的更多相关文章
- New UWP Community Toolkit
概述 UWP Community Toolkit 是一个 UWP App 自定义控件.应用服务和帮助方法的集合,能够很大程度的简化和指引开发者的开发工作,相信广大 UWPer 并不陌生. 下面是截取自 ...
- New UWP Community Toolkit - XAML Brushes
概述 上一篇 New UWP Community Toolkit 文章中,我们对 V2.2.0 版本的重要更新做了简单回顾.接下来会针对每个重要更新,结合 SDK 源代码和调用代码详细讲解. 本篇我们 ...
- New UWP Community Toolkit - Markdown
概述 前面 New UWP Community Toolkit 文章中,我们对 V2.2.0 版本的重要更新做了简单回顾,其中简单介绍了 MarkdownTextBlock 和 MarkdownDoc ...
- New UWP Community Toolkit - Staggered panel
概述 前面 New UWP Community Toolkit 文章中,我们对 2.2.0 版本的重要更新做了简单回顾,其中简单介绍了 Staggered panel,本篇我们结合代码详细讲解 St ...
- New UWP Community Toolkit - Carousel
概述 New UWP Community Toolkit V2.2.0 的版本发布日志中提到了 Carousel 的调整,本篇我们结合代码详细讲解 Carousel 的实现. Carousel 是 ...
- New UWP Community Toolkit - RadialProgressBar
概述 UWP Community Toolkit 中有一个圆形的进度条控件 - RadialProgressBar,本篇我们结合代码详细讲解 RadialProgressBar 的实现. Radi ...
- New UWP Community Toolkit - RadialGauge
概述 New UWP Community Toolkit V2.2.0 的版本发布日志中提到了 RadialGauge 的调整,本篇我们结合代码详细讲解 RadialGauge 的实现. Radi ...
- New UWP Community Toolkit - RangeSelector
概述 前面 New UWP Community Toolkit 文章中,我们对 V2.2.0 版本的重要更新做了简单回顾,其中简单介绍了 RangeSelector,本篇我们结合代码详细讲解一下 Ra ...
- New UWP Community Toolkit - ImageEx
概述 UWP Community Toolkit 中有一个图片的扩展控件 - ImageEx,本篇我们结合代码详细讲解 ImageEx 的实现. ImageEx 是一个图片的扩展控件,包括 Ima ...
随机推荐
- Java .classpath文件Classpath entry org.maven.eclipse.MAVEN2_CLASSPATH_CONTAINER will not be exported or published异常解决办法
在实际用Maven构建Java Web项目开发过程中,有时候会出现上述情况的警告,如果不解决这个警告,就会在启动Web服务器的时候抛出无法加载Maven管理的第三方jar包的异常. 所以,要解决上面的 ...
- Myeclipse配置tomcat,以及简单的Myeclipse的配置
第一次总结技术相关的东西,那就坚持下去每天写一点.[有错欢迎指正,本人小白,轻虐] 首先,(以我的为例子吧)需要准备原材料,当然了也需要根据自己的机器底层架构选择了---Myeclipse(2015) ...
- Matcher类的简单使用
今天工作时遇到一个问题, 用正则处理html标签时不知该如何下手.还好有Matcher帮助解决了问题. 需求如下: 例如有如下html文章内容: <p><a href="w ...
- Cucumber 相关资源
Cucumber support: https://cucumber.io/support yan1234abcd的专栏:http://blog.csdn.net/yan1234abcd/articl ...
- Tomcat5.5.9+JSP经典配置实例
一.开发环境配置 第一步:下载j2sdk和tomcat:到sun官方站(http://java.sun.com/j2se/1.5.0/download.jsp)下载j2sdk,注意下载版本为Windo ...
- Java 缩写总结
1.JVM:Java Virtual Machine(Java虚拟机)的缩写. 它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的. Java语言的一个非常重要的特点就是与 ...
- springMVC正确使用GET POST PUT和DELETE方法,如何传递参数
1. 向服务器请求数据:GET 这是标准的http的GET最擅长的, 应该使用GET请求,但是在使用时候我们会需要传递一个或多个参数给服务器, 这些出参数可能是基本数据类型页可能是对象,get方法可以 ...
- 笔记:I/O流-内存映射文件
内存映射文件时利用虚拟内存实现来将一个文件或者文件的一部分映射到内存中,然后整个文件就可以当作数组一样的访问,这个比传统的文件操作要快得多,Java 使用内存映射文件首先需要从文件中获取一个chann ...
- 利用NPOI将EXCEL转换成HTML的C#实现
领导说想做一个网页打印功能,而且模板可以自定义,我考虑了三个方案,一是打印插件,二是在线 html 编辑器,三是 excel 模板,领导建议用的是打印插件的形式,我研究了一下,一个是需要下载安装,二个 ...
- POJ1331 Multiply(strtol函数练习)
题目链接:http://poj.org/problem?id=1331 主要介绍strtol函数: long int strtol(const char *nptr,char **endptr,int ...