无限滚动Flipview在翻页时更新内容
本文关键字:更新 新内容 滚动 Flipview 无限 | 更新日期: 2023-09-27 18:28:07
我正在尝试实现一种日历风格(基于日期)的无限FlipView
,用户可以在其中触摸滚动。我正在将FlipView
的ItemTemplate
与自定义ObservableCollection
绑定。一切都显示得很好,我正在操作ObservableCollection
以提供所需的行为:当选定的索引发生更改时,将新元素添加到顶部并从底部移除。
private void OnIndexChanged(object sender, SelectionChangedEventArgs e)
{
//Works great on slow swiping with pauses, no offset artifacts
DataGroup.OnIndexChanged(GroupFlip.SelectedIndex);
}
问题是,FlipView
只会在用户停止滚动时触发SelectedIndex
更改事件。对于小刷,这是可以的,但用户可以很容易地到达收藏的末尾,并在收藏更新之前走到死胡同。
我已经成功订阅了FlipView
内部的ScrollViewer
的[Viewchanged][1]
,正如这里所建议的那样,并且能够看到并使用HorizontalOffset
来计算新的索引偏移量并操作集合。
问题是,在此事件中操作集合时,FlipView
会以各种方式跳转,具体取决于用户和集合的操作类型。
InnerFlipper.ViewChanged += OnSoftScroll;//InnerFlipper is caught from the `VisualHelperTree`
private void OnSoftScroll(object sender, ScrollViewerViewChangedEventArgs e)
{
(...)
double UpperMargin = ScrollableSize - Offset;//Value is reliable
if (UpperMargin < ViewObservableSize)
{
DataGroup.OnIndexDelta(1);
}
(...)
}
我尝试了许多组合,忽略一些事件以避免双重触发,强制新的HorizontalOffset
为基于索引变化和当前偏移量等计算的值。没有一个能给出透明的结果,这是一个无缝的无限滚动。
有什么想法可以避免工件,处理这个事件,甚至用其他方法来实现期望的结果吗?
最终通过彻底重建FlipView的工作方式解决了这个问题。如果FlipView是用一个非常大的"虚拟"集(即没有内容)初始化的,那么滚动时我所要做的就是更新内容,而不是干扰FlipView的索引或项目计数。
希望它能帮助其他人。
编辑:
我从实现中制作了一个代码片段。然而,回过头来看,它只是要求使用一个可回收的模式,以防止在大量滚动时出现大量GC。更新的大型虚拟列表的概念仍然存在。我使用通用对象是因为我的视图切换了每个页面的自定义控件类型(周页面、月页面等)。希望它能帮助你们,快乐的编码。
在控制端,我们有一个只订阅了Loaded事件的FlipView。
protected ScrollViewer InnerScroller;
private void OnFlipViewerLoaded(object sender, RoutedEventArgs e)
{
InnerFlipper = (ScrollViewer)FindChildControl<ScrollViewer>(sender);
InnerFlipper.ViewChanged += OnPageScroll;
}
/// <summary>
/// Our custom pseudo-infinite collection
/// </summary>
ModelCollection ItemsCollection = new ModelCollection();
private void OnPageScroll(object sender, ScrollViewerViewChangedEventArgs e)
{
InnerFlipper.ViewChanged -= OnPageScroll;//Temporarily stop handling this event, to prevent double triggers and let the CPU breath for a little
int FlipViewerRealIndex = GetFlipViewIndex(sender);
ItemsCollection.UpdatePages(FlipViewerRealIndex);
InnerFlipper.ViewChanged += OnPageScroll;//Start getting this event again, ready for the next iteration
}
/// <summary>
/// No idea why, FlipView's inner offset starts at 2. Fuck it, subtract 2 and it works fine.
/// </summary>
/// <param name="sender"></param>
/// <returns></returns>
public static int GetFlipViewIndex(object sender)
{
double CorrectedScrollOffset= ((ScrollViewer)sender).HorizontalOffset - 2;
int NewIndex = (int)Math.Round(CorrectedScrollOffset);//Round instead of simple cast, otherwise there is a bias in the direction
return NewIndex;
}
在我们的模型集合设置上。
private const int VirtualListRadius = 1000;
/// <summary>
/// The collection constructor, runs the first step of the data filling.
/// </summary>
public ModelCollection()
{
//Fill in the virtual list with the default (mostly null) custom control.
for (int i = 1; i <= VirtualListRadius; i++)
{
object LeftMostPage = NewPageControl(args1);
object RightMostPage = NewPageControl(args2);
Items.Insert(0, LeftMostPage);
Items.Add(RightMostPage);
}
}
/// <summary>
/// The FlipViewer's items list, with all the virtual content and real content (where applicable)
/// </summary>
public ObservableCollection<Object> Items
{
get { return _items; }
set { SetProperty(ref _items, value); }
}
public ObservableCollection<Object> _items = new ObservableCollection<Object>();
更新页面的代码:
/// <summary>
/// How many pages of content should be buffered in each direction
/// </summary>
private const int ObservableListRadius = 3;
/// <summary>
/// The main update function that replaces placeholder-virtual content with actual content, while freeing up content that's no longe necessary
/// </summary>
/// <param name="scrollIndex">The new index absolute index that should be extracted from the Flipview's inner scroller</param>
public void UpdatePages(int scrollIndex)
{
if (scrollIndex < 0 || scrollIndex > Items.Count - 1)
{
//If the scroll has move beyond the virtual list, then we're in trouble
throw new Exception("The scroll has move beyond the virtual list");
}
int MinIndex = Math.Max(scrollIndex - ObservableListRadius, 0);
int MaxIndex = Math.Min(scrollIndex + ObservableListRadius, Items.Count() - 1);
//Update index content
(Items.ElementAt(scrollIndex) as ModelPage).UpdatePage(args1);
Status = Enumerators.CollectionStatusType.FirstPageLoaded;
//Update increasing radius indexes
for (int radius = 1; radius <= Constants.ObservableListRadius; radius++)
{
if (scrollIndex + radius <= MaxIndex && scrollIndex + radius > MinIndex)
{
(Items.ElementAt(scrollIndex + radius) as ModelPage).UpdatePage(args2);
}
if (scrollIndex - radius >= MinIndex && scrollIndex - radius <= MaxIndex)
{
(Items.ElementAt(scrollIndex - radius) as ModelPage).UpdatePage(args3);
}
}
}