Xamarin Forms:如何检测滚动视图或扩展器末端或顶部到达?

问题描述

当滚动视图到达末尾或顶部时,我需要加载更多数据。我在这里找到了相同的 thread,但在那上面,我不明白什么是 MyScrollView

private void OnScrolled(object sender,ScrolledEventArgs e)
{
    MyScrollView scrollView = sender as MyScrollView;
    double scrollingSpace = scrollView.ContentSize.Height - scrollView.Height;

    if (scrollingSpace <= e.ScrollY)
    {
       //reached end
    }
}

我在 ListView 中使用 ItemAppearing 事件实现了相同的功能。但目前,我在 UI 上使用 Expander显示数据。扩展器到达终点和起点是否存在相同类型的事件?扩展器位于滚动视图内部,这就是我开始研究滚动视图结束事件的原因。

如何在滚动视图到达结束或开始时触发某种事件?

更新

我的滚动视图内容如下,它包含 BindableLayoutExpander

<ScrollView
    x:Name="MyScrollView"
    Scrolled="OnScrolled">
    <StackLayout BindableLayout.ItemsSource="{Binding AllItems,Mode=TwoWay}">
        <BindableLayout.ItemTemplate>
            <DataTemplate>
                <Expander>
                </Expander>
            </DataTemplate>
        </BindableLayout.ItemTemplate>
    </StackLayout>
</ScrollView>

解决方法

MyScrollView 可能是使用 ScrollView 的自定义视图。这是我使用 Listview 的示例代码。您也可以将其应用于您的自定义滚动视图。

CustomListview.cs

public class CustomListView : ListView
{
    public CustomListView()
    {
    }
}

iOS

[assembly: ExportRenderer(typeof(CustomListView),typeof(CustomListViewRenderer))]
namespace YOUR_PROJECT_NAME.iOS.Renderers
{
    public class CustomListViewRenderer : ListViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
        {
            base.OnElementChanged(e);
            if (Element != null)
            {
                Control.AlwaysBounceVertical = Element.IsPullToRefreshEnabled;
                Control.Bounces = Element.IsPullToRefreshEnabled;
            }
        }

        protected override void OnElementPropertyChanged(object sender,PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender,e);
            if (e.PropertyName == nameof(Element.IsPullToRefreshEnabled))
            {
                Control.AlwaysBounceVertical = Element.IsPullToRefreshEnabled;
                Control.Bounces = Element.IsPullToRefreshEnabled;
            }
        }
    }
}

安卓

No custom renderer is needed

XML

<Grid RowSpacing="0">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Grid Grid.Row="0"
              x:Name="DashboardBarGrid"
              BackgroundColor="#263A43"
              RowSpacing="0">

            <Grid.RowDefinitions>
                <RowDefinition Height="50" />
            </Grid.RowDefinitions>

            <Grid Grid.Row="0"
                  HorizontalOptions="CenterAndExpand" VerticalOptions="Center">
                <Label Text="Title" TextColor="White"/>
            </Grid>

        </Grid>
         <controls:CustomListView Grid.Row="1" ItemsSource="{Binding Items}" Scrolled="listView_Scrolled" IsPullToRefreshEnabled="False"/>
     </Grid>

页面.cs

...
private Animation _lastRowAnimation;
private readonly double _initialLastRowHeight;
private readonly RowDefinition _lastRowDefinition;
private bool _isScrolled;
private bool _isFirstRun;
public List<string> Items { get; set; } = new List<string>();
...

public MyPage()
{
   InitializeComponent();
   _lastRowDefinition = DashboardBarGrid.RowDefinitions[0];
   _initialLastRowHeight = _lastRowDefinition.Height.Value;
   _isFirstRun = true;
            
   Items.Add("A");
   Items.Add("B");
   Items.Add("C");
   ...
   Items.Add("Z");

   BindingContext = this;
}

...
private void listView_Scrolled(object sender,ScrolledEventArgs e)
{
   var listView = (ListView)sender;
   if (e.ScrollY > 50) _isFirstRun = false;
   if (_isScrolled && e.ScrollY == 0 && _lastRowDefinition?.Height.Value < _initialLastRowHeight){
      listView.IsEnabled = false;
      _lastRowAnimation = new Animation((d) => _lastRowDefinition.Height = new GridLength(d.Clamp(50,double.MaxValue)),_lastRowDefinition.Height.Value,_initialLastRowHeight,Easing.Linear,() => _lastRowAnimation = null);
      _lastRowAnimation.Commit(this,"Animation",16,500,(v,c) =>
      {
         _isScrolled = false;
         listView.IsEnabled = true;
      });
   }


   // Hide the rows
   else if (!_isScrolled && _lastRowDefinition?.Height.Value >= 
   _initialLastRowHeight && !_isFirstRun)
   {
      listView.IsEnabled = false;
      _lastRowAnimation = new Animation((d) => _lastRowDefinition.Height = new GridLength(d.Clamp(0,c) =>    {
         _isScrolled = true;
         listView.IsEnabled = true;
      });
   }
}
,

您的原始代码在正确的轨道上。前段时间我花了一些时间才让它工作,但这是我使用的代码。它比 MyScrollView 类简单得多。

private void TheScrollView_Scrolled(object sender,ScrolledEventArgs e)
{
    if (TheScrollView.ScrollY >= TheScrollView.ContentSize.Height - TheScrollView.Height)
    {
        //All the way to the bottom.
    }
    else if (TheScrollView.ScrollX <= 0)
    {
        //All the way to the top.
    }
    else
    {
        //Neither all the way to the top nor to the right.
    }
}
,

就像我在问题中提到的那样,我使用 ListView 事件在 ItemAppearing 中实现了相同的功能。但在这种情况下,我的数据是另一个列表中的项目列表。这就是我强制使用 Expander 组件在 UI 上显示数据的原因。

同时,当 Expander 底部到达时,我需要加载更多项目。但 Expander 不提供相关事件,因为它不是可滚动控件。所以我试图找出 ScrollView 组件的事件。但也没有运气。

最后,我决定将数据从另一个列表中的列表更改为单个列表。我已经使用一些 for 循环对其进行了转换,现在数据适用于 ListView。我已经使用 ItemAppearing 事件实现了加载更多功能。

谢谢

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...