读书人

基于GridView LoadMoreAsync 设计的横

发布时间: 2013-10-18 20:53:13 作者: rapoo

基于GridView LoadMoreAsync 设计的横向双向瀑布流 组件 源码分享

原理:

实现了ISupportIncrementalLoading 接口

完成了增量加载,

针对于本地对象无法释放的情况 增加了 相关的Func

同时 通过VisualTree 拿到了GridView中 HorizontalBar 来对滚动条的位置进行捕捉 与计算

通过计算后 来执行 虚化操作

如需 转载请声明博客作者

以下是源码:

IncrementalLoadingCollection 对象:

 public class IncrementalLoadingCollection<T> : ObservableCollection<T>, ISupportIncrementalLoading    {        // 是否正在异步加载中        private bool _isBusy = false;        // 提供数据的 Func        // 第一个参数:增量加载的起始索引;第二个参数:需要获取的数据量;第三个参数:获取到的数据集合        private Func<int, int, List<T>> _funcGetData;        // 最大可显示的数据量        private uint _totalCount = 0;        private Func<T, T> _actDisposeData;        public int PageIndex = 0;        public uint perCount = 0;        /// <summary>        /// 构造函数        /// </summary>        /// <param name="totalCount">最大可显示的数据量</param>        /// <param name="getDataFunc">提供数据的 Func</param>        public IncrementalLoadingCollection(uint totalCount, Func<int, int, List<T>> getDataFunc, Func<T, T> actDisposeData)        {            _funcGetData = getDataFunc;            _totalCount = totalCount;            _actDisposeData = actDisposeData;        }        /// <summary>        /// 是否还有更多的数据        /// </summary>        public bool HasMoreItems        {            get { return this.Count < _totalCount; }        }        /// <summary>        /// 异步加载数据(增量加载)        /// </summary>        /// <param name="count">需要加载的数据量</param>        /// <returns></returns>        public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)        {            perCount = count;            if (_isBusy)            {                return AsyncInfo.Run((token) => Task.Run<LoadMoreItemsResult>(() =>                {                    return new LoadMoreItemsResult { Count = (uint)this.Count };                }, token));            }            _isBusy = true;            var dispatcher = Window.Current.Dispatcher;            return AsyncInfo.Run(                (token) =>                    Task.Run<LoadMoreItemsResult>(                       async () =>                       {                           try                           {                               //// 模拟长时任务                               await Task.Delay(100);                               // 增量加载的起始索引                               var startIndex = this.Count;                               await dispatcher.RunAsync(                                    CoreDispatcherPriority.Normal,                                    () =>                                    {                                        PageIndex++;                                        // 通过 Func 获取增量数据                                        var items = _funcGetData(startIndex, (int)count);                                        if (items != null)                                            foreach (var item in items)                                            {                                                this.Add(item);                                            }                                    });                               // Count - 实际已加载的数据量                               return new LoadMoreItemsResult { Count = (uint)this.Count };                           }                           finally                           {                               _isBusy = false;                           }                       },                       token));        }        public void DisposeItemByStartAndEnd(long start, long end)        {            for (long i = start; i < end; i++)            {                _actDisposeData(this.Items[(int)i]);            }        }        public void RemoveItemByRange(long start, long end)        {            for (long i = start; i < end; i++)            {                if (this.Items.Count > i)                {                    this.RemoveItem((int)i);                }            }        }        public void Reset()        {            this.OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Reset));        }    }


IncrementalLoadingGridView:

  public class IncrementalLoadingGridView : GridView    {        #region Member Variables        private ScrollBar _HorizontalScrollBar;        private Dictionary<int, int> _pageOffsetDict = new Dictionary<int, int>();        private Dictionary<int, bool> _pageVirtualizingDict = new Dictionary<int, bool>();        private dynamic _filelist;        #endregion        #region Constants        const string HORIZONTALSCROLLBAR_PARTNAME = "HorizontalScrollBar";        #endregion        public IncrementalLoadingGridView()        {            this.Loaded += ((sender, e) =>            {                this.OnApplyTemplate();            });            this.Unloaded += ((sender, e) =>            {                _HorizontalScrollBar = Pollute.FindVisualChildByName<ScrollBar>(this, HORIZONTALSCROLLBAR_PARTNAME);                if (null != _HorizontalScrollBar)                    _HorizontalScrollBar.ValueChanged -= ValueChanged;            });        }        protected override async void OnApplyTemplate()        {            base.OnApplyTemplate();            await this.WaitForLayoutUpdateAsync();            _HorizontalScrollBar = Pollute.FindVisualChildByName<ScrollBar>(this, HORIZONTALSCROLLBAR_PARTNAME);            if (null != _HorizontalScrollBar)            {                _HorizontalScrollBar.ValueChanged += ValueChanged;            }        }        protected override async void OnItemsChanged(object e)        {            if (null != this.ItemsSource)            {                InitPositionByValue();            }            base.OnItemsChanged(e);            if (null == this.ItemsSource)            {                await this.WaitForLoadedAsync();                InitPositionByValue();            }        }        private void InitPositionByValue()        {            _filelist = this.ItemsSource;            CompositionTarget.Rendering += ((obj, args) =>            {                var newValue = Convert.ToInt32(_HorizontalScrollBar.Value);                int currentPageIndex = _filelist.PageIndex - 2;                if (!_pageOffsetDict.ContainsKey(currentPageIndex))                {                    _pageOffsetDict[currentPageIndex] = newValue;                    _pageVirtualizingDict[currentPageIndex] = false;                }            });        }        private int preIndex = 0;        private int maxPageIndex = 0;        private int minOffset;        private async void ValueChanged(object sender, RangeBaseValueChangedEventArgs e)        {            if (null == this.ItemsSource) return;            var newValue = Convert.ToInt32(e.NewValue);            var oldValue = Convert.ToInt32(e.OldValue);            if (newValue == oldValue) return;            Debug.WriteLine("坐标:" + newValue);            int currentPageIndex;            if (null == _filelist) _filelist = this.ItemsSource;            if (e.NewValue < 1.0)            {                await Task.Run(async () =>                {                    string text = "滑到头了 开始释放 释放前个数:" + _filelist.Count.ToString();                    Debug.WriteLine(text);                    for (int i = 3; i < maxPageIndex; i++)                    {                        _pageVirtualizingDict[i] = true;                        var start = (i - 1) * _filelist.perCount;                        var end = i * _filelist.perCount - 1;                        await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>                        {                            if (_filelist.Count > end)                            {                                _filelist.DisposeItemByStartAndEnd(start, end);                                _filelist.RemoveItemByRange(start, end);                            }                            await Task.Delay(500);                            _filelist.Reset();                        });                    }                    _filelist.PageIndex = 2;                    text = "滑到头了 释放完毕 释放后个数:" + _filelist.Count;                    Debug.WriteLine(text);                });            }            else                await Task.Run(() =>                {                    if (newValue > oldValue)                    {                        //lock (_pageOffsetDict)                        //{                        var horiOffset = newValue - oldValue;                        if (minOffset > horiOffset) minOffset = horiOffset;                        currentPageIndex = _filelist.PageIndex - 2;                        //_pageOffsetDict[currentPageIndex] = newValue;                        maxPageIndex = Convert.ToInt32(_filelist.Count) / Convert.ToInt32(_filelist.perCount);                        //}                        if (preIndex != currentPageIndex && _pageOffsetDict.ContainsValue(newValue))                        {                            Debug.WriteLine("坐标:" + newValue + " 上一页:" + preIndex + " 当前页:" + currentPageIndex);                            if (_pageVirtualizingDict.ContainsKey(preIndex))                            {                                _pageVirtualizingDict[preIndex] = false;                                Debug.WriteLine("@@@@@@@@@@@@@@@@@@@@@@@@@需要向后虚化:" + preIndex + "@@@@@@@@@@@@@@@@@@@@@@" + _pageVirtualizingDict[preIndex]);                                if (!_pageVirtualizingDict[preIndex] && preIndex > 3)                                {                                    int i = preIndex;                                    while (i > 3)                                    {                                        //if (!_pageVirtualizingDict.ContainsKey(i))                                        //{                                        //    _pageVirtualizingDict[i] = false;                                        //    _pageOffsetDict[i] = oldValue -= minOffset;                                        //}                                        if (_pageVirtualizingDict.ContainsKey(i) && !_pageVirtualizingDict[i])                                        {                                            var start = (i - 3) * _filelist.perCount;                                            var end = (i - 2) * _filelist.perCount - 1;                                            _filelist.DisposeItemByStartAndEnd(start, end);                                            _pageVirtualizingDict[i] = true;                                            Debug.WriteLine("虚化完毕:" + i);                                        }                                        i--;                                    }                                }                            }                            preIndex = currentPageIndex;                        }                        _pageVirtualizingDict[currentPageIndex] = false;                    }                    else if (newValue < oldValue)                    {                        if (_pageOffsetDict.ContainsValue(newValue))                        {                            currentPageIndex = _pageOffsetDict.GetKey(newValue).FirstOrDefault();                            Debug.WriteLine("当前页:" + currentPageIndex + " 坐标:" + newValue);                            var offset = 3;                            if (preIndex - offset > currentPageIndex && currentPageIndex > 0)                            {                                _pageVirtualizingDict[preIndex] = false;                                if (!_pageVirtualizingDict[preIndex])                                {                                    Debug.WriteLine("@@@@@@@@@@@@@@@@@@@@@@@@@虚化 After页:" + preIndex + " 是否虚化" + _pageVirtualizingDict[preIndex]);                                    int i = preIndex - offset;                                    while (i <= maxPageIndex)                                    {                                        //if (!_pageVirtualizingDict.ContainsKey(i))                                        //{                                        //    _pageVirtualizingDict[i] = false;                                        //    _pageOffsetDict[i] = oldValue += minOffset;                                        //}                                        if (_pageVirtualizingDict.ContainsKey(i) && !_pageVirtualizingDict[i])                                        {                                            Debug.WriteLine("开始释放第:" + i + "页");                                            int count = _filelist.Count;                                            Debug.WriteLine("虚化前个数:" + count);                                            var start = (i - 1) * _filelist.perCount;                                            var end = i * _filelist.perCount - 1;                                            if (end < _filelist.Count)                                            {                                                _filelist.DisposeItemByStartAndEnd(start, end);                                            }                                            string writeLine = "虚化after完毕 虚化位Start:" + start + " End:" + end + " Page:" + i + " 虚化后个数:" + count;                                            Debug.WriteLine(writeLine);                                            _pageVirtualizingDict[i] = true;                                        }                                        i++;                                    }                                }                                _pageVirtualizingDict[currentPageIndex] = false;                                //_pageVirtualizingDict[currentPageIndex - 1] = false;                                //_pageVirtualizingDict[currentPageIndex - 2] = false;                                //_pageVirtualizingDict[currentPageIndex - 3] = false;                                preIndex = currentPageIndex;                            }                            _pageVirtualizingDict[currentPageIndex] = false;                        }                    }                });            if (e.NewValue == _HorizontalScrollBar.Maximum)            {                this.IsHitTestVisible = false;                await Task.Delay(500);                this.IsHitTestVisible = true;            }        }    }


使用方式:

    <toolkit:IncrementalLoadingGridView x:Name="gvMain"                      Visibility="{Binding IsLeafLevel, Converter={StaticResource BooleanToVisibilityConverter}}"                      Padding="140,40,0,0"                      SelectionMode="None"                      ItemsSource="{Binding ImageItemsSource, Mode=TwoWay}"                      IncrementalLoadingThreshold="0.5" DataFetchSize="0.5"                      IsItemClickEnabled="True"                      PointerMoved="gvMain_PointerMoved"                      ItemClick="gvMain_ItemClick"                     ItemTemplateSelector="{StaticResource imageDataTemplateSelector}">                <GridView.ItemsPanel>                    <ItemsPanelTemplate>                        <WrapGrid VirtualizingStackPanel.VirtualizationMode="Recycling"></WrapGrid>                    </ItemsPanelTemplate>                </GridView.ItemsPanel>            </toolkit:IncrementalLoadingGridView>


IncrementalLoadingThreshold="0.5" DataFetchSize="0.5" 这2个阀值设定的比较低 这样虚化起来效率会高一些

需要binding 的列表对象

Dispose方法 需要binding对象继承IDispose 释放本地流

_filelist = new IncrementalLoadingCollection<FileItem>((uint)_currentfiles.Count, (startIndex, Count) =>            {                SetLoadingState(true);                if (_currentfiles == null || _currentfiles.Count <= startIndex) return null;                List<FileItem> list = new List<FileItem>();                foreach (var file in _currentfiles.Skip(startIndex).Take(Count))                {                    FileItem item = new FileItem();                    item.File = file;                    item.Name = file.DisplayName;                    if (folder.Name.EndsWith("zhx")) item.Name = "zhx" + item.Name;                    list.Add(item);                }                SetLoadingState(false);                return list;            }, (o) =>            {                o.Dispose();                return o;            });


当增量滚动时 我会针对 触发增量加载事件同时 记录 需要虚化的offset 在回滚的同时 进行释放操作,以保证内存在较低的负载。

基于GridView LoadMoreAsync 设计的横向双向飞瀑流 组件 源码分享

在超宽屏幕上的使用效果, 载入了大量本地图片

1楼Menger86昨天 13:32
神作

读书人网 >移动开发

热点推荐