本文共 12703 字,大约阅读时间需要 42 分钟。
上篇 讲到该控件的需要和设计过程。
这篇讲讲开发过程中一些重要问题解决。
1.支持ISupportIncrementalLoading,实现HasMoreItems属性和LoadMoreItemsAsync方法
因为我们上篇里面讲过,需要把源数据分成一个一个的Group作为GirdView的源,
所以LoadMoreItemsAsync方法里面我做了以下的实现:
public IAsyncOperationLoadMoreItemsAsync(uint count) { IAsyncOperation result = rowAdapter.LoadMoreItemsAsync(count); if (rowAdapter.Count > 0) { for (int i = this.Count; i < rowAdapter.Count; i++) { if (rowAdapter.SourceList.Count / rowAdapter.rowItemsCount > i) { var item = this.ElementAtOrDefault(i); if (item == null) { this.Insert(i, rowAdapter[i]); } } } } return result; }
应该还是比较清楚的,当源数据里面的个数超过了RowItemsCount的时候,我们才插入Item。
也就是说。当满15个Item的时候我们才插入第1个Row Item,当满30个Item的时候我们插入第2个Row Item......
可能有人会说,如果源数据不是15个整数,那怎么办呢??嗯,我也遇到了这个问题,当然,HasMoreItems属性也需要作相应的实现
public bool HasMoreItems { get { var hasMoreItems = rowAdapter.HasMoreItems; if (!hasMoreItems) { if (rowAdapter.Count > 0 && this.Count < rowAdapter.Count) { for (int i = this.Count; i < rowAdapter.Count; i++) { //sometime it will miss some indexs in LoadMoreItemsAsync method, //if hasMoreItems is false, that means not more items, //so at that monment we should add the missed items. //if (rowAdapter.SourceList.Count / rowAdapter.rowItemsCount <= i) { var item = this.ElementAtOrDefault(i); if (item == null) { this.Insert(i, rowAdapter[i]); } } } } } return hasMoreItems; } }
当HasMoreItems为false的时候,就是说这个源不会有更多的数据了,所以这时候我们应该把剩余的Item都加入到下一个Row Item里面去。
2.当Window size 改变的时候,实现不同的可变大小结构。
public class ResizeableItems : List<ResizeableItem>
每一种结构,我用一个ResizeableItem来表示。
比如window 最小的时候这种结构。
list = new List(); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); var c1 = new ResizeableItem() { Columns = 2, Items = list, Min = windowMinwidth + +1, Max = windowMinwidth + rangwidth * 1 }; _resizeableItems.Add(c1);
设置GirdView里面的每个Item的宽高比2:1,设置ResizeableItem的最小和最大值,这个意思就是当window 到达MIn和Max的这个区间的时候就使用这个结构。
然后也设置出其它的结构样式。(PS:我这里还没找到Get 最小window 宽度的办法,好像只能设置,如果有办法的朋友请留言一下)
_resizeableItems = new ResizeableItems(); //ApplicationView.GetForCurrentView().SetPreferredMinSize(new Windows.Foundation.Size(200, 200)); double windowMinwidth = 500; double windowMaxwidth = DeviceInfo.DeviceScreenSize.Width; double rangwidth = (windowMaxwidth - windowMinwidth) / 4.0; #region 4 var list = new List(); list.Add(new Resizable() { Width = 2, Height = 2 }); list.Add(new Resizable() { Width = 1, Height = 2 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 2 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); var c4 = new ResizeableItem() { Columns = 4, Items = list, Min = windowMinwidth + rangwidth * 3 + 1, Max = double.PositiveInfinity }; _resizeableItems.Add(c4); #endregion #region 3 list = new List (); list.Add(new Resizable() { Width = 2, Height = 2 }); list.Add(new Resizable() { Width = 1, Height = 2 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 2 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); var c3 = new ResizeableItem() { Columns = 3, Items = list, Min = windowMinwidth + rangwidth * 2 + 1, Max = windowMinwidth + rangwidth * 3 }; _resizeableItems.Add(c3); #endregion #region 2 list = new List (); list.Add(new Resizable() { Width = 2, Height = 2 }); list.Add(new Resizable() { Width = 1, Height = 2 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 2 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); list.Add(new Resizable() { Width = 1, Height = 1 }); var c2 = new ResizeableItem() { Columns = 2, Items = list, Min = windowMinwidth + rangwidth * 1 + 1, Max = windowMinwidth + rangwidth * 2 }; _resizeableItems.Add(c2); #endregion #region 1 list = new List (); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); list.Add(new Resizable() { Width = 2, Height = 1 }); var c1 = new ResizeableItem() { Columns = 2, Items = list, Min = windowMinwidth + +1, Max = windowMinwidth + rangwidth * 1 }; _resizeableItems.Add(c1); #endregion
这样我们就设置好了,到达多少size的时候使用什么结构模板了。。
最后我们只需要在控件的MeasureOverrid中去设置,GridView的Item的样式就好了.
protected override Size MeasureOverride(Size availableSize) { if (!PlatformIndependent.IsWindowsPhoneDevice) { OnMeasureOverride(availableSize); } return base.MeasureOverride(availableSize); } private void OnMeasureOverride(Size availableSize) { if (ItemsSource != null && ItemsSource is IResizeableItems && availableSize != Size.Empty) { var resizeableItem = ResizeableItems.GetItem(availableSize.Width); if (resizeableItem != null) { resizeableItem.ItemWidth = (int)(availableSize.Width / resizeableItem.Columns - 7); foreach (var item in this.Items) { var gridviewItem = this.ContainerFromItem(item) as ListViewItem; //not null, it's in viewport, so it need to update. if (gridviewItem != null && gridviewItem.ContentTemplateRoot != null) { var gridview = gridviewItem.ContentTemplateRoot as VariableSizedGridView; gridview.ResizeableItem = null; gridview.ResizeableItem = resizeableItem; } } } } }
3.ListViewItem默认模板的修改
在使用这个的时候因为其实是个ListView,所以当你点击到GridView外面的时候就点击到了ListLViewItem上面,会有些你不想要的效果,比如PointerDown。第一次用VS拿到ListViewItem的模板的时候发现没地方可能修改这个动画呢???
再查了下MSDN,发现原来,ListViewItem是有2套模板的,
When the 's is an (the default) or , this template is used to show the data items. This template uses a instead of a tree to improve grid performance.
这个模板跟我从VS里面拿到的是一样的。
When the 's is not an (the default) or , this template is used to show the data items. This template uses a tree and visual states instead of a .
这个模板我就不贴了,比较多,
总结:
虽然说这个控件已经满足了Boss的需求,但是我还是觉得有一些需要改进的。
1.UI Virtualized
如果说GridView里面的Item个数被用户设置的很多,这个必定是很占用内存的。
实际Debug,也发现,Live Visual Tree里面有3个ListViewItem.
感觉内存中不需要这么多个ListViewItem 来循环回收利用,我的猜想是微软做了上下各一个Item的缓存来提高Scrolling的流畅。
但对于这个控件来说,内存里面的UI Item 会有3*15个,因为我们知道VariableSizedWrapGrid是不支持UI虚拟化的。
2.没有对Insert,Delete进行处理。
这个控件还有一个限制,就是源是一个固定个数或者是ISupportIncrementalLoading,如果在过程中add,insert,delete的话,暂时没有进行处理。
只对Reset这种情况进行了处理。
private void ObservableRowAapter_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: break; case NotifyCollectionChangedAction.Move: break; case NotifyCollectionChangedAction.Remove: break; case NotifyCollectionChangedAction.Replace: break; case NotifyCollectionChangedAction.Reset: if (this.Count>0) { this.Clear(); } break; default: break; } }
希望有好想法的童鞋能留言,大家讨论共同进步。。
为了部落!
开源有益,
转载地址:http://mgdja.baihongyu.com/