编程问答
uwp composition api -凯发ag旗舰厅登录网址下载
需求:
光看标题大家肯定不知道是什么东西,先上效果图:
这不就是listview的group效果吗?? 看上去是的。但是请听完需求.
1.group中的集合需要支持增量加载isupportincrementalloading
2.支持ui virtualization
oh,no。listview 自带的group都不支持这2个需求。好吧,只有靠自己撸code了。。
实现前思考:
仔细想了下,其实要解决的主要问题有2个
数据源的处理 和 groupheader的ui的处理
1.数据源的处理
因为之前在写 uwp virtualizedvariablesizedgridview 支持可虚拟化可变大小item的view的时候已经做过这种处理源的工作了,所以方案出来的比较快。
不管有几个group,其实当第1个hasmore等false的时候,我们就可以加载第2个group里面的集合。
我为此写了一个类groupobservablecollection
而igroupcollection是个接口。
public interface igroupcollection: isupportincrementalloading{listigroupheader 是用来描述group header的,你可以继承它,添加一些绑定groupheader的属性(注意请给firstindex和lastindex赋值-1的初始值)
比如:在效果图中,如果只有全部评论,没有精彩评论,那么后面的导航的按钮是应该不现实的,所以我加了gotobuttonvisibility属性来控制。
public class mygroupheader : igroupheader, inotifypropertychanged{public string name { get; set; }public int firstindex { get; set; }public int lastindex { get; set; }public double height { get; set; }public string goto { get; set; }private visibility _gotobuttonvisibility = visibility.collapsed;public visibility gotobuttonvisibility{get { return _gotobuttonvisibility; }set{_gotobuttonvisibility = value;onpropertychanged("gotobuttonvisibility");}}public mygroupheader(){firstindex = -1;lastindex = -1;}public event propertychangedeventhandler propertychanged;void onpropertychanged(string propertyname){if (propertychanged != null){propertychanged(this, new propertychangedeventargs(propertyname));}}}数据源的处理还是比较简单的。
2.groupheader的ui的处理
首先我想到的是加一个grid,然后这些groupheader放在里面,通过scrollviewer的viewchanged来处理它们。
比较了下listview的group效果,scrollbar是会挡住groupheader的,所以我把这个grid放进了scrollviewer的模板里面。
grouplistview的模板,这里大家可以看到我加入了个progressring,这个是后面做导航功能需要的,后面再讲。
scrollviewer的模板
下面就是实现对groupheader显示的控制了。
很快代码写好了。。运行起来效果还可以。。但是童鞋们说。。你这个跟composition api 一毛钱关系都没有啊。。
大家别急。。听我说。。模拟器里面运行还行,拿实体机器上运行的时候,当我快速向上或者向下滑动的时候,groupheader会出现顿一顿的感觉,卡一下,不会有惯性的感觉。
看到这个,我立马明白了。。不管是viewchanging或者viewchanged事件,它们跟manipulation都不是同步的。
看了上一盘 uwp composition api - pulltorefresh的童鞋会说,好吧,隐藏的真深。
那我们还是用composition api来建立groupheader和scrollviewer之间的关系。
1.首先我想的是,当进入viewport再用composition api来建立关系,但是很快被我否决了。还是因为viewchanged这个事件是有惯性的原因,这样没法让创建groupheader和scrollviewer之间的关系的初始数据完全准确。
就是说groupheader因为初始数据不正确的情况会造成没放在我想要的位置,只有当惯性停止的时候获取的位置信息才是准确的。
在preparecontainerforitemoverride中判断是否groupheader 的那个item已经准备添加到itemspanel里面。
protected override void preparecontainerforitemoverride(dependencyobject element, object item){base.preparecontainerforitemoverride(element, item);listviewitem listviewitem = element as listviewitem;listviewitem.sizechanged -= listviewitem_sizechanged;if (listviewitem.tag == null){defaultlistviewitemmargin = listviewitem.margin;}if (groupcollection != null){var index = indexfromcontainer(element);var group = groupcollection.groupheaders.firstordefault(x => x.firstindex == index || x.lastindex == index);if (group != null){if (!groupdic.containskey(group)){contentcontrol groupheader = creategroupheader(group);contentcontrol tempgroupheader = creategroupheader(group);expressionanimationitem expressionanimationitem = new expressionanimationitem();expressionanimationitem.visualelement = groupheader;expressionanimationitem.tempelement = tempgroupheader;groupdic[group] = expressionanimationitem;var temp = new dictionary在updategroupheader方法里面去设置header的状态
internal void updategroupheaders(bool isintermediate = true){var firstvisibleitemindex = this.getfirstvisibleindex();foreach (var item in groupdic){//top headerif (item.key.firstindex <= firstvisibleitemindex && (firstvisibleitemindex <= item.key.lastindex || item.key.lastindex == -1)){currenttopgroupheader.visibility = visibility.visible;currenttopgroupheader.margin = new thickness(0);currenttopgroupheader.clip = null;currenttopgroupheader.datacontext = item.key;if (item.key.firstindex == firstvisibleitemindex){if (item.value.scrollviewer == null){item.value.scrollviewer = scrollviewer;}var isactive = item.value.isactive;item.value.stopanimation();item.value.visualelement.clip = null;item.value.visualelement.visibility = visibility.collapsed;if (!isactive){if (!isintermediate){item.value.visualelement.margin = new thickness(0);item.value.startanimation(true);}}else{item.value.startanimation(false);}}cleartempelement(item);}//moving headerelse{handlegroupheader(isintermediate, item);}}} view code这里我简单说下几种状态:
1. 在itemspanel里面
1)全部在viewport里面
动画开启,clip设置为null
2)部分在viewport里面
动画开启,并且设置clip
3)没有在viewport里面
动画开启,visible 设置为collapsed
2. 没有在itemspanel里面
动画停止。
关于groupheader初始状态的设置,这里是最坑的,遇到很多问题。
public void startanimation(bool update = false){if (update || expression == null || visual == null){visual = elementcompositionpreview.getelementvisual(visualelement);//if (0 <= visualelement.margin.top && visualelement.margin.top <= scrollviewer.actualheight)//{// min = (float)-visualelement.margin.top;// max = (float)scrollviewer.actualheight min;//}//else if (visualelement.margin.top < 0)//{//}//else if (visualelement.margin.top > scrollviewer.actualheight)//{//}if (scrollviewermanipprops == null){scrollviewermanipprops = elementcompositionpreview.getscrollviewermanipulationpropertyset(scrollviewer);}compositor compositor = scrollviewermanipprops.compositor;// create the expression//expression = compositor.createexpressionanimation("min(max((scrollviewermanipprops.translation.y verticaloffset), minvalue), maxvalue)");////expression = compositor.createexpressionanimation("scrollviewermanipprops.translation.y verticaloffset");//expression.setscalarparameter("minvalue", min);//expression.setscalarparameter("maxvalue", max);//expression.setscalarparameter("verticaloffset", (float)scrollviewer.verticaloffset); expression = compositor.createexpressionanimation("scrollviewermanipprops.translation.y verticaloffset");////expression = compositor.createexpressionanimation("scrollviewermanipprops.translation.y verticaloffset");//expression.setscalarparameter("minvalue", min);//expression.setscalarparameter("maxvalue", max);verticaloffset = scrollviewer.verticaloffset;expression.setscalarparameter("verticaloffset", (float)scrollviewer.verticaloffset);// set "dynamic" reference parameter that will be used to evaluate the current position of the scrollbar every frameexpression.setreferenceparameter("scrollviewermanipprops", scrollviewermanipprops);}visual.startanimation("offset.y", expression);isactive = true;//windows.ui.xaml.media.compositiontarget.rendering -= oncompositiontargetrendering;//windows.ui.xaml.media.compositiontarget.rendering = oncompositiontargetrendering;}注释掉了的代码是处理:
当groupheader进入viewport的时候才启动动画,离开之后就关闭动画,表达式就是一个限制,这个就不讲了。
expression = compositor.createexpressionanimation("scrollviewermanipprops.translation.y verticaloffset");可以看到我给表达式加了一个vericaloffset。。嗯。其实visual的offset是表示 visual 相对于其父 visual 的位置偏移量。
举2个例子,整个viewport的高度是500,现在滚动条的vericaloffset是100。
1.如果我想把header(header高度为50)放到viewport的最下面(header刚好全部进入viewport),那么初始的参数应该是哪些呢?
header.margin = new thickness(450);
header.clip=null;
expression = compositor.createexpressionanimation("scrollviewermanipprops.translation.y 100");
这样向上滚scrollviewermanipprops.translation.y(-450),header 就会滚viewport的顶部。
2.如果我想把header(header高度为50)放到viewport的最下面(header刚好一半全部进入viewport),那么初始的参数应该是哪些呢?
header.margin = new thickness(475);
header.clip=new rectanglegeometry() { rect = new rect(0, 0, this.actualwidth, 25) };
expression = compositor.createexpressionanimation("scrollviewermanipprops.translation.y 100");
当向上或者向下滚动的时候,记得更新clip值就可以了。
说到为什么要加clip,因为如果你的控件不是整个page大小的时候,这个header会显示到控件外部去,大家应该都是懂得。
这里说下这个里面碰到一个问题。当groupheader viewport之外的时候(在grid之外的,margin大于grid的高度)创建动画,会发现你怎么修改header属性都是没有效果的。
最终结果的是不会在屏幕上显示任何东西。
实验了下用canvas发现就可以了,但是grid却不行,是不是可以认为visual在创建的时候如果对象不在它父容器的size范围之内,创建出来都是看不见的??
这个希望懂得童鞋能留言告诉一下。
把scrollviewer模板里面的grid换成canvas就好了。。
剩下的都是一些计算,计算位置,计算大小变化。
最后就是gotogroup方法,当跳转的group没有load出来的时候(也就是firstindex还没有值得时候),我们就load,load,load,直到
它有值,这个可能是个长的时间过程,所以加了progressring,找到index,最后用listview的api来跳转就好了。
public async task gotogroupasync(int groupindex, scrollintoviewalignment scrollintoviewalignment = scrollintoviewalignment.leading){if (groupcollection != null){var gc = groupcollection;if (groupindex < gc.groupheaders.count && groupindex >= 0 && !isgotogrouping){isgotogrouping = true;//load more so that scrollintoviewalignment.leading can go to topvar loadcount = this.getvisibleitemscount() 1;progressring.isactive = true;progressring.visibility = visibility.visible;//make sure user don't do any other thing at the time.this.ishittestvisible = false;//await task.delay(3000);while (gc.groupheaders[groupindex].firstindex == -1){if (gc.hasmoreitems){await gc.loadmoreitemsasync(loadcount);}else{break;}}if (gc.groupheaders[groupindex].firstindex != -1){//make sure there are enought items to go scrollintoviewalignment.leading//this.count > (firstindex loadcount)if (scrollintoviewalignment == scrollintoviewalignment.leading){var more = this.items.count - (gc.groupheaders[groupindex].firstindex loadcount);if (gc.hasmoreitems && more < 0){await gc.loadmoreitemsasync((uint)math.abs(more));}}progressring.isactive = false;progressring.visibility = visibility.collapsed;var groupfirstindex = gc.groupheaders[groupindex].firstindex;scrollintoview(this.items[groupfirstindex], scrollintoviewalignment);//already in viewport, maybe it will not change view if (groupdic.containskey(gc.groupheaders[groupindex]) && groupdic[gc.groupheaders[groupindex]].visibility == visibility.visible){this.ishittestvisible = true;isgotogrouping = false;}}else{this.ishittestvisible = true;isgotogrouping = false;progressring.isactive = false;progressring.visibility = visibility.collapsed;}}}}总结:
这个控件做下来,基本上都是在计算计算计算。。当然也知道了一些composition api的东西。
其实vistual的属性还有很多,在做这个控件的时候没有用到,以后用到了会继续分享的。 开源有益,源码github地址。
visual 元素有些基本的呈现相关属性,这些属性都能使用 composition api 的动画 api 来演示动画。
-
opacity
表示 visual 的透明度。 -
offset
表示 visual 相对于其父 visual 的位置偏移量。 -
clip
表示 visual 裁剪区域。 -
centerpoint
表示 visual 的中心点。 -
transformmatrix
表示 visual 的变换矩阵。 -
size
表示 visual 的尺寸大小。 -
scale
表示 visual 的缩放大小。 -
rotationaxis
表示 visual 的旋转轴。 -
rotationangle
表示 visual 的旋转角度。
有 4 个类派生自 visual,他们分别对应了不同种类的 visual,分别是:
-
containervisual
表示容器 visual,可能有子节点的 visual,大部分的 xaml 可视元素基本都是该 visual,其他的 visual 都也是派生自该类。 -
effectvisual
表示通过特效来呈现内容的 visual,可以通过配合 win2d 的支持 composition 的 effects 来呈现丰富多彩的内容。 -
imagevisual
表示通过图片来呈现内容的 visual,可以用于呈现图片。 -
solidcolorvisual
表示一个纯色矩形的 visual 元素
-
转载于:https://www.cnblogs.com/fadekongjian/p/5629715.html
总结
以上是凯发ag旗舰厅登录网址下载为你收集整理的uwp composition api - grouplistview(一)的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得凯发ag旗舰厅登录网址下载网站内容还不错,欢迎将凯发ag旗舰厅登录网址下载推荐给好友。
- 上一篇: php 源码解析--count
- 下一篇: webform 转 mvc 飞一般的感觉