XAML in Xamarin.Forms 基礎篇 電子書

XAML in Xamarin.Forms 基礎篇 電子書
XAML in Xamarin.Forms 基礎篇 電子書

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書

2017/10/25

Xamarin.Forms 的 ListView 之延遲載入(Lazy Loading) 之設計方法

有些時候,當使用者要查詢後端資料庫的資料時候,因為所設定的查詢條件過於寬鬆,將會導致會從後端資料一次性的接收到成千上萬筆的資料,若您讓手機應用程式來接收這麼多的資料,將會有嚴重的效能上的問題出現,例如,要從網路一次讀取到這麼多的資料,會需要花點時間;要把這麼多的紀錄加入到 ListView 中,也有可能造成記憶體不足或者ListView操作延緩的現象產生。
在這個時候,我們就可以使用延遲載入 Lazy Loading 這樣的設計方法來解決此一問題。他的處理方式為,每次呼叫 Web API 的時候,後端 Web API 僅僅會回傳一定數量的紀錄(例如:最多 100 筆),當使用者捲動 ListView 的清單到最後一筆紀錄的時候,您的 Xamarin.Forms 程式,就會再度呼叫 Web API 服務,請求回傳接下來一定數量的紀錄(例如:101~200筆),並且把這些新讀取到的紀錄,顯示到 ListView 中。
想要做到這樣的功能,ListView 有提供兩個事件,ItemAppearing / ItemDisappearing 這兩個事件,可以供我們做出這樣的效果;在我們接下來的範例中,將會展示出這樣的設計方法。
我們透過了 XAML 的行為 Behavior (這裡的行為擴充,我們將會使用 Prism 所提供的擴充行為功能),設定當 ItemAppearing 事件發生的時候,會執行所對應的指定的 ViewModel 內的命令 ItemAppearingCommand,而我們在 ViewModel 內,將會設計,當這個命令 ItemAppearingCommand 被觸發的時候,並且現在所顯示的紀錄是全部紀錄的最後一筆,將會進行載入更多的紀錄到 ListView 內(這裡我們將會呼叫 Reload(fooLast.ID+1) 方法來達成)。
因此,在這個範例中,也充分展示出,如何不透過後置碼 Code Behind 的設計技術,也可以在 ViewModel 內,處理相關事件發生要處理的需求。
當然,為了簡化這個範例專案的複雜程度,我們要讀取的集合資料,並不會實際從網路呼叫 Web API 來取得,而是直接產生靜態的集合資料(若您要設計每次都從網路讀取資料,請記得要設計相關的 UX,告知使用者,現在這樣更新資料中,要不然,當在讀取更多資料的時候,手機應用程式,當時會卡卡的)。
底下是我們這個測試頁面 View 的 XAML 標記語言
我們首先宣告了一個 Prism Behavior 的命名空間 xmlns:behavior="clr-namespace:Prism.Behaviors; 接著在 ListView 內,加入一個行為設計,EventToCommandBehavior,並且指定當 ItemAppearing 事件發生的時候,就會需要執行 ViewModel 內的 ItemAppearingCommand 命令;另外,在這裡,我們使用了 EventArgsParameterPath 這個屬性,設定當觸發這個 ViewModel 內的命令時候,需要將是事件中的這個參數,傳入到命令中,這樣,我們才可以在該命令中,知道當時正在顯示的紀錄是哪一筆。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
             prism:ViewModelLocator.AutowireViewModel="True"
             xmlns:behavior="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
             x:Class="XFListViewLazy.Views.MainPage"
             Title="MainPage">
    <Grid
      >
        <ListView
            ItemsSource="{Binding MyDatas}"
            SelectedItem="{Binding SelectedMyData}"
            HasUnevenRows="True"
            >
            <ListView.Behaviors>
                <behavior:EventToCommandBehavior
                    EventName="ItemAppearing"
                    Command="{Binding ItemAppearingCommand}"
                    EventArgsParameterPath="Item"/>
            </ListView.Behaviors>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout>
                            <Label Text="{Binding ID}"/>
                            <Label Text="{Binding Name}" FontSize="30"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</ContentPage>
底下是上面 View 的相對應 ViewModel
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Xamarin.Forms;
using XFListViewLazy.Models;
using XFListViewLazy.Repositories;

namespace XFListViewLazy.ViewModels
{

    public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;


        public ObservableCollection<MyModel> MyDatas { get; set; } = new ObservableCollection<MyModel>();
        public MyModel SelectedMyData { get; set; }

        private readonly INavigationService _navigationService;
        public MyRepository _myRepository { get; set; }

        public DelegateCommand<MyModel> ItemAppearingCommand { get; set; }

        public MainPageViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            _myRepository = MyRepository.GetInstance();

            ItemAppearingCommand = new DelegateCommand<MyModel>((x) =>
            {
                var fooLast = MyDatas.Last();
                if (x.ID == fooLast.ID)
                {
                    Reload(fooLast.ID+1);
                }
            });
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatingTo(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {
            Reload(0);
        }

        public void Reload(int last)
        {
            var foo = _myRepository.GetNext(last);
            foreach (var item in foo)
            {
                MyDatas.Add(item);
            }
        }

    }

}

沒有留言:

張貼留言