UWP Composition API - PullToRefresh

UWP Composition API - PullToRefresh
原文:UWP Composition API - PullToRefresh

背景:

之前用ScrollViewer 来做过 PullToRefresh的控件,在项目一些特殊的条件下总有一些问题,比如ScrollViewer不会及时到达指定位置。
于是便有了使用Composition API来重新实现PullToRefresh控件。本控件的难点不是实现,而是对Composition API的一些探索。

本文的一些观点或者说结论不一定是全对的,都是通过实验得到的,Composition API 可用的资料实在是太少了。

成品效果图:

 

资料:

Composition API 资料

1.官方Sample

2. 原作者 Nick Waggoner 供职于微软 native Windows UI platform 链接是对文章的翻译 VALID VOID 

3.比较旧的资料


在网上查阅了些资料,看到网上有大神已经实现过了。了解了大概的实现过程,因为自己想做的效果还是跟大神的有差距的,所以

还是自己动手封装成控件。

实现原理:

这里引用 VALID VOID里面的话

输入驱动动画

 自大约五年前触摸渐成主流起,创造低延迟体验成为了一种普遍需求。使用手指或笔在屏幕上操作,使得人眼获得了更直观的参照点来辨识操作的延迟和流畅性。为使操作流畅,主流操作系统公司均将更多的操作移交至系统和 GPU (如 ChromeIE)执行。在 Windows 上,这由 DirectManipulation 这一或多或少是针对于触摸构建的动画引擎实现的。它解决了关键的延迟挑战,也就是如何自然地以展示从输入驱动到事件驱动过渡的动效。但另一方面,它也几乎没有提供对定制惯性观感的支持,就像福特 T 型车那样——“只要车是黑色的,你可以把它涂成任意你喜欢的颜色”。2

 

ElementCompositionPreview.GetScrollViewerManipulationPropertySet 是让你能够把玩输入驱动动效的第一步。虽然它仍然没给你任何对内容滚动时观感进行控制的额外能力,但它确实允许你对次级内容应用表达式动画。例如,我们终于能完成我们的基础视差滚动代码:

 

// 创建驱动视差滚动的表达式动画。
ExpressionAnimation parallaxAnimation = compositor.CreateExpressionAnimation("MyForeground.Translation.Y / MyParallaxRatio");// 设置对前景对象的引用。
CompositionPropertySet MyPropertySet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(MyScrollViewer);  
parallaxAnimation.SetReferenceParameter("MyForeground", MyPropertySet);// 设置背景对象视差滚动的速度。 parallaxAnimation.SetScalarParameter("MyParallaxRatio", 0.5f); // 对背景对象开始视差动画。 backgroundVisual.StartAnimation("Offset.Y", parallaxAnimation); 

 

使用这一技巧,你能够实现多种优秀的效果:视差滚动、粘性表头、自定义滚动条等等。唯一缺失的就是定制操作本身的观感……

我讲一下我的理解:使用过ScrollViewer 和Manipulation相关事件的童鞋都知道,想要得到一些ScrollViewer 触摸的详情太难了,

DirectManipulationStarted和DirectManipulationCompleted得到信息太少了,而Manipulation其他的事件又需要设置ManipulationMode,这样全部的情况都要你自己来处理。当看到

ElementCompositionPreview.GetScrollViewerManipulationPropertySet(MyScrollViewer); 

的时候,你是不是感觉有点亲切。看上去你拿到了ScrollViewer 的一些Manipulation 的信息。。话说这里是最最坑爹了,

MyForeground 其实是GetScrollViewerManipulationPropertySet返回的东东,

但MyForeground.Translation.Y这是什么鬼东西。。Manipulation 的Translation??? 后来我在网上搜索了一下,

但我查了下CompositionPropertySet 并没发现有相关Translation的属性啊? 

难道跟Manipulation事件里面的参数里面的Translation 是一样的吗??这是我的推测。

网上都是这样用的,但是没有文档。。除了Translation不知道还有其他属性能使用不。

暂时没有寻找到答案,希望知道的童鞋可以留言,万分感激。。。

上面这段的代码的意思就是说把Manipulation.Translation.Y 映射到backgroundVisual的Offset.Y上面。

也就是说你现在已经可以找到ScrollViewer滚动的时候一些有用的数值了。。值得说的是,用鼠标滚动的时候 映射依然能生效。

这种映射是实时的,是会有惯性效果的。

实现过程:

因为要用到ScrollViewer,所以我做了2种,一种是刷新内容第一元素是ScrollViewer的,一种不是ScrollViewer的。

如果刷新内容第一元素是ScrollViewer,模板如下:

  <ControlTemplate TargetType="local:PullToRefreshGrid1"><Grid><ContentControl x:Name="Header" Opacity="0" VerticalAlignment="Top" ContentTemplate="{TemplateBinding HeaderTemplate}" HorizontalContentAlignment="Center" VerticalContentAlignment="Bottom" /><ContentPresenter x:Name="Content" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/></Grid></ControlTemplate>

如果刷新内容第一元素不是ScrollViewer,那么我为它添加了一个ScrollViewer:

  <ControlTemplate TargetType="local:PullToRefreshGrid"><Grid><ContentControl x:Name="Header" Opacity="0" VerticalAlignment="Top" ContentTemplate="{TemplateBinding HeaderTemplate}" HorizontalContentAlignment="Center" VerticalContentAlignment="Bottom" /><ScrollViewer x:Name="ScrollViewer" VerticalSnapPointsType="MandatorySingle"  VerticalSnapPointsAlignment="Near"VerticalScrollMode="Enabled" VerticalScrollBarVisibility="Hidden" VerticalContentAlignment="Stretch" VerticalAlignment="Stretch"><ContentPresenter x:Name="Content" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/></ScrollViewer></Grid></ControlTemplate>

当然,其实第2种也是统用的,我只是想减少控件的Child,如果你不知道刷新内容里面有没有ScrollViewer,那么用第2种就好了。有人会说为啥控件的名字这么奇怪。。那是因为我之前也写过PullToRefresh控件(PullToRefreshControl,PullToRefreshPanel),我把名字都想完了。。实在想不出更好的了。。大家体谅下。。( ╯□╰ )

好了,重点就是拿到这个ScrollViewer 然后跟Header,产生某种关系(你懂的)。。。

            if (RefreshThreshold == 0.0){RefreshThreshold = headerHeight;}ratio = RefreshThreshold / headerHeight;_offsetAnimation = _compositor.CreateExpressionAnimation("(min(max(0, ScrollManipulation.Translation.Y * ratio) / Divider, 1)) * MaxOffsetY");_offsetAnimation.SetScalarParameter("Divider", (float)RefreshThreshold);_offsetAnimation.SetScalarParameter("MaxOffsetY", (float)RefreshThreshold * 5 / 4);_offsetAnimation.SetScalarParameter("ratio", (float)ratio);_offsetAnimation.SetReferenceParameter("ScrollManipulation", _scrollerViewerManipulation);_opacityAnimation = _compositor.CreateExpressionAnimation("min((max(0, ScrollManipulation.Translation.Y * ratio) / Divider), 1)");_opacityAnimation.SetScalarParameter("Divider", (float)headerHeight);_opacityAnimation.SetScalarParameter("ratio", (float)1);_opacityAnimation.SetReferenceParameter("ScrollManipulation", _scrollerViewerManipulation);_headerVisual = ElementCompositionPreview.GetElementVisual(_header);_contentVisual = ElementCompositionPreview.GetElementVisual(_scrollViewerBorder);_headerVisual.StartAnimation("Offset.Y", _offsetAnimation);_headerVisual.StartAnimation("Opacity", _opacityAnimation);_contentVisual.StartAnimation("Offset.Y", _offsetAnimation);

RefreshThreshold 是到达Release to refresh的一个点。。可以由用户设定,默认是header的高度。

 

MaxOffsetY 是当到达RefreshThreshold之后我还能拖动的最大值,这里设置为RefreshThreshold 的5/4。

(min(max(0, ScrollManipulation.Translation.Y * ratio) / Divider, 1)) * MaxOffsetY

我再来讲讲这个表达式的意思,ScrollManipulation.Translation.Y 大家都已经知道了。是ScrollViewer进行Manipulation的Y值,向下是正值,向上时负值,初始为0.

综合起来就是最大值为MaxOffsetY最小值为0.。这个速率是根据RefreshThreshold/headerHeight来的,因为你会发现,你向下drag scrollViewer的时候是有一定最大值的,当RefreshThreshold比较大的时候,你很难ScrollManipulation.Translation.Y值达到RefreshThreshold。

min((max(0, ScrollManipulation.Translation.Y * ratio) / Divider), 1)

这个也比较简单,是给header的Opaicty做了一个动画。

            _headerVisual.StartAnimation("Offset.Y", _offsetAnimation);_headerVisual.StartAnimation("Opacity", _opacityAnimation);_contentVisual.StartAnimation("Offset.Y", _offsetAnimation);

完成之后我们将动画开始就好了。这样在向下拖scrollviewer的时候,scrollviewer和header就都会向下移动。

接下来我们需要监听 offset。

        private void ScrollViewer_DirectManipulationStarted(object sender, object e){Windows.UI.Xaml.Media.CompositionTarget.Rendering += OnCompositionTargetRendering;_refresh = false;_header.Opacity = 1;}

在开始manipulat的时候,注册CompositionTarget.Rendering事件。在这个事件里面我们就可以实时获得offset的变化了。

         private void OnCompositionTargetRendering(object sender, object e){_headerVisual.StopAnimation("Offset.Y");var offsetY = _headerVisual.Offset.Y;IsReachThreshold = offsetY >= RefreshThreshold;_scrollViewerBorder.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, _content.Width, _content.Height - offsetY) };Debug.WriteLine(IsReachThreshold + "," + _headerVisual.Offset.Y + "," + RefreshThreshold);_headerVisual.StartAnimation("Offset.Y", _offsetAnimation);if (!_refresh){_refresh = IsReachThreshold;}if (_refresh){_pulledDownTime = DateTime.Now;}if (_refresh && offsetY <= 1){_releaseTime = DateTime.Now;}}

这里有点坑爹的是,发现如果不StopAnimation,那么Offset.Y永远都是0.。。很囧啊。。

最后我们在ScrollViewer_DirectManipulationCompleted事件里面处理是否要 触发PullToRefresh事件就ok了。

         private void ScrollViewer_DirectManipulationCompleted(object sender, object e){Windows.UI.Xaml.Media.CompositionTarget.Rendering -= OnCompositionTargetRendering;var cancelled = (_releaseTime - _pulledDownTime) > TimeSpan.FromMilliseconds(250);if (_refresh){_refresh = false;if (cancelled){Debug.WriteLine("Refresh cancelled...");}else{Debug.WriteLine("Refresh now!!!");if (PullToRefresh != null){_headerVisual.StopAnimation("Offset.Y");LastRefreshTime = DateTime.Now;_headerVisual.StartAnimation("Offset.Y", _offsetAnimation);PullToRefresh(this, null);}}}}

最后说下Header模板(HeaderTemplate)是可以定义的。。它的DataContext是绑定到这个控件上的,有用的属性有(LastRefreshTime,IsReachThreshold等),你可以用它们创造你属于你喜欢的Header样式。

通过本控件,初步了解Composition API 的一些用法。下一篇,我会讲讲一些更多的探索。。

开源有益,源码GitHub地址。

问题:

1.Visual 是继承IDisposable,我们需要在什么时候Dispose 掉它呢? 还是它自己管理的?

我试过在unload的时候去dispose它。但是会出了Win32的异常。。官方sample里面对这个也讲的不清楚。

希望知道的童鞋能留言告知,万分感谢。另外希望有对这个比较了解的童鞋能提供一些sample,资料,再次感谢。

补充:
使用条件:

    // Windows build 10240 and later. if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 1)){...}// Windows build10586 and later.if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 2)){...}// Windows build14332 and later.if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 3)){...}
调试了下
1. Windows build14332 and later: 1,2,3都为true。 
2. Windows build10586 and later: 1,2为true。
3. Windows build 10240 and later: 1为true。

因为10586之前的版本是不支持Composition API的。所以使用的时候记得判断:

    // Windows build10586 and later.if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 2)){...}

 
 
posted on 2018-03-12 13:05 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/8548729.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/486686.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从城市治理到城市“智”理,AI 不仅是城市管理的“眼睛”

来源&#xff1a;帮尼资讯部分参考来源&#xff1a;中国安防行业网&#xff0c;图片来源网络近年来&#xff0c;随着计算机视觉技术的长足进步&#xff0c;AI在城市管理领域广泛部署。其中&#xff0c;AI视频分析识别技术成为城市场景中规模最大、数量最多、落地最广泛的应用。…

python3 应用 nose_parameterized 实现unittest 参数化

一、读取变量的值&#xff0c;实现unittest 参数化 import nose_parameterized,unittestdef calc(a:int,b:int):return ab case_data [[10,20,30],[12,21,33],[15,21,36] ] class MyClass(unittest.TestCase):nose_parameterized.parameterized.expand(case_data)def test_comp…

vue data数据修改_VUE的数据响应式

什么是数据响应式&#xff1f;const vm newVUE({data:{n:0}})上面的代码中&#xff0c;如果修改vm.n&#xff0c;那么UI中的n就会通过变化来响应我&#xff0c;这就是数据响应式。VUE对data做了什么&#xff1f;当给一个vue实例传入data的时候&#xff0c;vue内部会对传入的dat…

hutool的定时任务不支持依赖注入怎么办_设计一个任务调度算法,时间轮算法,比优先队列更高效...

当年我还是个学生的时候&#xff0c;有一次去参加欢聚时代的一个面试&#xff0c;有一道面试题记忆尤新&#xff0c;让你来实现一个定时任务&#xff0c;你会怎么做&#xff1f;为了简化问题&#xff0c;我们只用考虑内存方案&#xff0c;不用考虑数据持久化。数组法最简单的&a…

蜂鸟开发板 linux,蜂鸟E203系列——Linux下运行hello world例程

创建程序在 &#xff5e;/hbird-e-sdk-master/software 路径下创建一个“helloworld”中文件夹在 &#xff5e;/hbird-e-sdk-master/software/helloworld 路径下创建文件“helloworld.c”内容如下&#xff1a;#includeint main(void){printf("hello world!");printf(…

全景解密量子信息技术:高层集中学习,国家战略,三大领域一文看懂

来源&#xff1a;智东西 内参来源&#xff1a;中国信通院 IPRdaily中文网10月16日下午&#xff0c;高层就量子科技研究相关前景举行了一次会议&#xff0c;强调当今世界正经历百年未有之大变局&#xff0c;科技创新是其中一个关键变量。要充分认识推动量子科技发展的重要性&am…

mac怎么查看gitlab的注册邮箱_163电子邮箱怎么注册申请?手机号注册电子邮箱的小技巧...

电子邮箱帮助我们实现了无纸化&#xff0c;无需手写信件&#xff0c;通过电脑、手机输入&#xff0c;即可与收件人在网络上进行联系。电子邮箱的兴起&#xff0c;对于人与人之间的沟通和交流&#xff0c;增加了便捷性&#xff0c;促进了社会的发展与进步。目前的邮箱中&#xf…

c语言 switch案例,c语言switch case语句使用例子

c语言switch case语句使用例子发布时间&#xff1a;2020-04-23 11:48:53来源&#xff1a;亿速云阅读&#xff1a;421作者&#xff1a;小新这篇文章主要为大家详细介绍了c语言switch case语句使用例子&#xff0c;文中示例代码介绍的非常详细&#xff0c;具有一定的参考价值&…

深度学习未来的三种方式

来源&#xff1a;海豚数据科学实验室深度学习的未来在于这三种学习模式&#xff0c;而且它们彼此之间密切相关&#xff1a;混合学习——现代深度学习方法如何跨越监督学习和非监督学习之间的边界&#xff0c;以适应大量未使用的无标签数据&#xff1f;复合学习——如何以创造性…

android ocr识别源码_身份证识别OCR解决手动输入繁琐问题

随着互联网金融的的发展&#xff0c;越来越多的互联网金融公司都推出了自己的金融APP&#xff0c;这些APP都涉及到个人身份证信息的输入认证&#xff0c;如果手动去输入身份证号码和姓名&#xff0c;速度非常慢&#xff0c;且用户体验非常差。为了提高在手机移动终端上输入身份…

什么是内卷?华为内部这篇文章读懂

来源&#xff1a;互联网坊间八卦&#xff08;ID:kekesil&#xff09;内卷的意思是明明已经靠近边界有个天花板&#xff0c;但却又不断自我激发&#xff0c;繁复化、精致化。概念的含糊其辞是无效讨论和跌入焦虑自我再生产困境的原因之一。判断内卷还是良性竞争的前置问题是回答…

锁屏界面显示某些设置已隐藏_iOS 14 隐藏功能,只要轻点手机背面就能截屏

关于 iOS 14 系统的一些功能我也为大家介绍了一些&#xff0c;iOS 14 已发布&#xff0c;界面大更新&#xff01;其实除了之外&#xff0c;iOS 14 系统还有许多隐藏的功能。那么今天我就为大家介绍 iOS 14 系统的隐藏功能之一&#xff1a;轻点背面。话不多说&#xff0c;我们先…

分享丨强化学习是针对优化数据的监督学习?

来源&#xff1a;AI科技大本营作者 | Ben Eysenbach、Aviral Kumar、Abhishek Gupta 编译 | 凯隐出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;强化学习&#xff08;RL&#xff09;可以从两个不同的视角来看待&#xff1a;优化和动态规划。其中&#xff0c;诸如REI…

stm32l0的停止模式怎么唤醒_手把手教你怎么利用旧电脑搭建NAS组建自己的黑群晖...

手把手教你怎么利用旧电脑搭建NAS组建自己的黑群晖Synology群晖科技&#xff08;Synology &#xff09;创立于 2000 年&#xff0c;自始便专注于打造高效能、可靠、功能丰富且绿色环保的 NAS 服务器&#xff0c;是全球少数几家以单纯的提供网络存储解决方案获得世界认同的华人企…

有人说 GPT3 是“暴力美学”的结晶,它的工作原理你知道吗?| 动图详解

来源&#xff1a;CSDN如今&#xff0c;在科技领域掀起了一股GPT3的热潮。大规模语言模型&#xff08;比如GPT3&#xff09;的潜力惊艳了我们。虽然这些模型还没有成熟到大多数企业将之直接面对消费者&#xff0c;但却展示出一些智慧的火花&#xff0c;并让人坚信其将会加速自动…

git 生成ssh key_ubuntu git生成ssh key (公钥私钥)配置github或者码云

Git是分布式的代码管理工具&#xff0c;远程的代码管理是基于SSH的&#xff0c;所以要使用远程的Git则需要SSH的配置。github的SSH配置如下&#xff1a;设置Git的user name和email&#xff1a;git config --global user.name "xx"git config --global user.email &qu…

android圆形头像 demo,Android图像处理之绘制圆形头像

在Android中&#xff0c;绘制圆形和绘制图片都是很容易的事情&#xff0c;但是绘制圆形图片就有点难倒人了。以前为了偷懒就直接去github上找一个开源项目&#xff0c;后来才发现绘制圆形图片其实也是很简单的事。绘制圆形图片也需要两个步骤&#xff1a;绘制圆形和绘制图片&am…

芯片项目烂尾怎么办?国家发改委回应了!

来源&#xff1a;北京日报客户端芯片项目烂尾的报道近日引发关注。对此&#xff0c;国家发改委新闻发言人孟玮在今日&#xff08;20日&#xff09;上午举行的例行发布会上回应&#xff0c;将会同有关部门强化顶层设计&#xff0c;狠抓产业规划布局&#xff0c;努力维护产业发展…

idea中二级包为什么创建不了_IDEA通过Maven打包JavaFX工程(OpenJFX11)

1 概述 最近研究JFX&#xff0c;写出来了但是打包不了&#xff0c;这。。。尴尬。。。 IDEA的文档说只支持Java8打成jar包&#xff1a; 尝试过直接使用Maven插件的package&#xff0c;不行&#xff0c;也尝试过Build Artifacts&#xff0c;也不行&#xff0c;各种奇奇怪怪的问题…

鸿蒙系统发布会16号几点,华为鸿蒙手机系统正式定档发布,12月16日于我们见面...

#华为鸿蒙系统#大家都知道&#xff0c;华为手机已经无法预装谷歌GMS服务了&#xff0c;对于大部分海外用户来说&#xff0c;GMS服务还是相当重要的&#xff0c;谷歌全家桶内置了地图、搜索、视频娱乐等功能&#xff0c;但凡是搭载了安卓系统的智能手机&#xff0c;那么就无法离…