Xamarin Forms:如何检测滚动视图(或扩展器)结束或到达顶部?
Xamarin Forms: How to detect the scrollview(or expander) end or top reached?
我需要在滚动视图到达末尾或顶部时加载更多数据。我在这里找到了相同的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
来显示数据。扩展器到达终点和起点是否有相同类型的事件?扩展器位于滚动视图内部,这就是我开始研究滚动视图结束事件的原因。
如何在滚动视图结束或开始时触发某种事件?
更新
我的 scrollview 内容如下,它包含 BindableLayout
和 Expander
。
<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;
}
}
}
}
Android
No custom renderer is needed
Xaml
<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>
Page.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, Easing.Linear, (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, double.MaxValue)), _initialLastRowHeight, 0, Easing.Linear, () => _lastRowAnimation = null);
_lastRowAnimation.Commit(this, "Animation", 16, 500, Easing.Linear, (v, c) => {
_isScrolled = true;
listView.IsEnabled = true;
});
}
}
您的原始代码在正确的轨道上。前段时间我花了一些时间才让它工作,但这是我使用的代码。比那个MyScrollView简单多了class.
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
事件实现了加载更多功能。
谢谢
我需要在滚动视图到达末尾或顶部时加载更多数据。我在这里找到了相同的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
来显示数据。扩展器到达终点和起点是否有相同类型的事件?扩展器位于滚动视图内部,这就是我开始研究滚动视图结束事件的原因。
如何在滚动视图结束或开始时触发某种事件?
更新
我的 scrollview 内容如下,它包含 BindableLayout
和 Expander
。
<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;
}
}
}
}
Android
No custom renderer is needed
Xaml
<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>
Page.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, Easing.Linear, (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, double.MaxValue)), _initialLastRowHeight, 0, Easing.Linear, () => _lastRowAnimation = null);
_lastRowAnimation.Commit(this, "Animation", 16, 500, Easing.Linear, (v, c) => {
_isScrolled = true;
listView.IsEnabled = true;
});
}
}
您的原始代码在正确的轨道上。前段时间我花了一些时间才让它工作,但这是我使用的代码。比那个MyScrollView简单多了class.
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
事件实现了加载更多功能。
谢谢