闲逛了一会儿之后,我收集了一些想法,并完善了自己的无限滚动方式,该方式不会扩展ListView,但反过来,它仅使用VM:
public class UpcomingMoviesViewModel
{
private readonly int scollingThreshold = 15;
private int loadStartIndex;
private int page;
public ObservableCollection<Movie> Movies { get; private set; }
private IRestService service;
public string Title { get { return "Upcoming Movies"; } }
public UpcomingMoviesViewModel(IRestService service)
{
loadStartIndex = scollingThreshold;
page = 1;
Movies = new ObservableCollection<Movie>();
this.service = service;
}
public async Task<bool> LoadUpcomingMovies()
{
var upcoming = await service.GetUpcomingMovies(page);
var allGenres = await service.GetGenres();
if (upcoming == null)
return false;
ConvertToBindableData(upcoming, allGenres);
return true;
}
public async Task<bool> LoadMoreMovies(Movie e)
{
if (e.Position != loadStartIndex)
return true;
loadStartIndex = scollingThreshold + Movies.Count;
page++;
return await LoadUpcomingMovies();
}
private void ConvertToBindableData(List<Movie> upcoming, List<Genre> allGenres)
{
int totalPagesInResponse = upcoming.Count;
for (int i = 0; i < totalPagesInResponse; i++)
{
var movie = upcoming[i];
//...
movie.Position = page == 1 ? i : i + totalPagesInResponse * (page - 1);
Movies.Add(movie);
}
}
}
API响应发送的页面各包含20个项目,如果我想要整个列表,则必须在递增page
的同时迭代所有页面。请注意,上面的scrollingThreshold
设置为15。这意味着在滚动了15项而不是20项后,将对API进行新的调用。
这是我的模特
public class Movie
{
public int Id { get; set; }
public int Position { get; set; }
public string Title { get; set; }
public string PosterPath { get; set; }
public string Overview { get; set; }
public string ReleaseDate { get; set; }
public List<int> GenreIds { get; set; }
public string DisplayGenre { get; set; }
}
这就是事实。我首先在代码背后的LoadUpcomingMovies()
事件中调用OnAppearing
方法:
protected async override void OnAppearing()
{
base.OnAppearing();
var resultOk = await ((UpcomingMoviesViewModel)BindingContext).LoadUpcomingMovies();
if (!resultOk)
await DisplayAlert("Communication Error",
"An error has occurred while trying to communicate with the REST API", "OK");
}
那就像一种魅力。然后,我从LoadMoreMovies
处理程序中调用ListView_ItemAppearing
方法,它将获取所有数据,但不会加载图像:
private async void ListView_ItemAppearing(object sender, ItemVisibilityEventArgs e)
{
var resultOk = await ((UpcomingMoviesViewModel)BindingContext).LoadMoreMovies((Movie)e.Item);
if (!resultOk)
await DisplayAlert("Communication Error",
"An error has occurred while trying to communicate with the REST API", "OK");
}
XAML文件:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MovieDbApp.View.UpcomingMoviesPage"
Title="{Binding Title}">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListView x:Name="listView" Grid.Row="0" HasUnevenRows="True" SeparatorColor="DarkBlue" ItemsSource="{Binding Movies}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout HorizontalOptions="StartAndExpand" Padding="5,5,5,5">
<Label Text="{Binding Title}"
TextColor="MidnightBlue"
FontSize="Medium"/>
<Image Source="{Binding PosterPath}" HorizontalOptions="Start" />
<Label Text="{Binding ReleaseDate}"
TextColor="DimGray"
FontSize="Micro"/>
<Label Text="{Binding DisplayGenre}"
TextColor="MediumSlateBlue"
FontSize="Micro"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage.Content>
</ContentPage>
我研究了xamarin中的异步图像加载,它们都指向特定于平台的第三方库,但是找不到适合我需要的第三方库(我只是试图将其全部塞入PCL中,以便现在)。因此,我决定自己处理问题,并尝试创建一个异步图像加载器:
public class AsyncImageService
{
private bool isLoading;
public async Task<ImageSource> LoadImage(string imageUrl, ImageSource target)
{
Stream stream;
ImageSource imageSource = null;
if (!isLoading)
{
isLoading = true;
if (!string.IsNullOrEmpty(imageUrl))
{
HttpClient client = new HttpClient();
var uri = new Uri(imageUrl);
stream = await client.GetStreamAsync(uri);
imageSource = ImageSource.FromStream(() => stream);
}
}
isLoading = false;
return imageSource;
}
}
但是由于经验不足,我不知道如何将其绑定到ListView单元格图像源。
我认为这里有各种各样的可能的解决方案,任何都会受到赞赏