LoginSignup
6
2

More than 5 years have passed since last update.

[Xamarin.Forms] CachingStrategyでListViewを高速化しよう

Posted at

この記事は、[初心者さん・学生さん大歓迎!] Xamarin その1 Advent Calendar 2017の23日目の記事です。

高速化の方法

この記事ではListViewのCaching Strategyを指定することで、ListViewを高速化します。詳しく知りたい方はこちらを参照してください。https://developer.xamarin.com/guides/xamarin-forms/user-interface/listview/performance/

ViewCell(MyCell)

テキスト1つと画像4つで構成されたViewCellです。大きい3枚の画像は常に同じ画像の順で描画し、左上の小さい画像は3枚の画像からランダムに選んだものを描画しています。(画像はFLAT ICON DESIGNからお借りしました)

XAML

MyCell.xaml
<ViewCell 
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Class="ViewCellTest.MyCell">
    <ViewCell.View>
        <StackLayout Orientation="Horizontal">
            <Image
                x:Name="iconImage"
                VerticalOptions="StartAndExpand"
                WidthRequest="50"
                HeightRequest="50"/>
            <StackLayout Orientation="Vertical">
                <Label
                    x:Name="titleLabel"
                    FontSize="Large"/>
                <StackLayout Orientation="Horizontal">
                    <!-- 大きな3つの画像はここでSourceを指定 -->
                    <Image
                        WidthRequest="100"
                        HeightRequest="100"
                        Source="image1.png"/>
                    <Image
                        WidthRequest="100"
                        HeightRequest="100"
                        Source="image2.png"/>
                    <Image
                        WidthRequest="100"
                        HeightRequest="100"
                        Source="image3.png"/>
                </StackLayout>
            </StackLayout>
        </StackLayout>
    </ViewCell.View>
</ViewCell>

コードビハインド

MyCell.xaml.cs
public partial class MyCell : ViewCell
{
    public static readonly BindableProperty IconImageSourceProperty = BindableProperty.Create(
        nameof(IconImageSource),
        typeof(ImageSource),
        typeof(MyCell),
        propertyChanged: (b, o, n) => (b as MyCell).iconImage.Source = n as ImageSource);

    public static readonly BindableProperty TitleProperty = BindableProperty.Create(
        nameof(Title),
        typeof(string),
        typeof(MyCell),
        propertyChanged: (b, o, n) => (b as MyCell).SetTitle(n as string));

    public ImageSource IconImageSource
    {
        get { return (ImageSource)GetValue(IconImageSourceProperty); }
        set { SetValue(IconImageSourceProperty, value); }
    }

    public string Title
    {
        get { return (string)GetValue(TitleProperty); }
        set { SetValue(TitleProperty, value); }
    }

    // このようにメソッドからテキストをセットしてもOK
    private void SetTitle(string title)
    {
        titleLabel.Text = title;
    }

    protected override void OnBindingContextChanged()
    {
        base.OnBindingContextChanged();
        // BindablePropertyのpropertyChangedを使わないなら、ここでコントロールの設定をする
    }

    public MyCell()
    {
        InitializeComponent();
        Console.WriteLine("MyCellが生成されました");
    }
}

BindablePropertyのpropertyChangedかオーバーライドしたOnBindingContextChanged()のどちらかで、XAMLで定義したコントロールのプロパティを設定してあげましょう。

ListViewを表示するページ(MainPage)

MyCellを用いたListViewを表示するMainPageを作ります。コードビハインドはInitializeComponent()以外何も書いていないので、省略します。

MyCellに使うデータクラス

CellItem.cs
public class CellItem
{
    public ImageSource IconImageSource { get; set; }
    public string Title { get; set; }

    public CellItem(string iconImageName, string title)
    {
        IconImageSource = ImageSource.FromFile(iconImageName);
        Title = title;
    }
}

XAML

ItemsSourceはMainViewModelのItemsとバインドしてます。

MainPage.xaml
<ContentPage 
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:ViewCellTest"
    x:Class="ViewCellTest.MainPage">

    <ContentPage.BindingContext>
        <local:MainViewModel/>
    </ContentPage.BindingContext>

    <ListView
        ItemsSource="{Binding Items}"
        HasUnevenRows="True">
        <ListView.ItemTemplate>
            <DataTemplate>
                <local:MyCell
                    IconImageSource="{Binding IconImageSource}"
                    Title="{Binding Title}"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

ViewModel

データの生成をコンストラクタでしているだけです。

MainViewModel.cs
public class MainViewModel
{
    public ObservableCollection<CellItem> Items { get; set; }

    public MainViewModel()
    {
        var random = new Random();
        // データ生成。画像はimage1.png, image2.png, image3.pngの三枚を用意した 
        var data = Enumerable.Range(1, 100)
            .Select(x => new CellItem($"image{random.Next(1, 4)}.png", $"MyCell{x}"));
        Items = new ObservableCollection<CellItem>(data);
    }
}

実行結果

プログラムが完成したので起動してみるとこんな感じになります。
スクロールに画像の読み込みが追いついてませんね。それではこれを高速化していきます。

RecycleElementの指定

MainPage.xaml
<ContentPage 
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:ViewCellTest"
    x:Class="ViewCellTest.MainPage">

    <ContentPage.BindingContext>
        <local:MainViewModel/>
    </ContentPage.BindingContext>

    <ListView
        ItemsSource="{Binding Items}"
        HasUnevenRows="True"
        CachingStrategy="RecycleElement"> <!-- 新しく追加 -->
        <ListView.ItemTemplate>
            <DataTemplate>
                <local:MyCell
                    IconImageSource="{Binding IconImageSource}"
                    Title="{Binding Title}"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

ListViewにCachingStrategy="RecycleElement"(デフォルトはRetainElement)を指定してあげるだけです。こうすることで、生成されたMyCellクラスのインスタンスを使いまわしながらBindingContextを切り替えてくれるので、MyCell生成にかかるコストを削減でき、読み込みが速くなります。

MyCellのコンストラクタに書いたConsole.WriteLine("MyCellが生成されました");より、初回のMyCellの生成後は、インスタンスが使い回されていることが確認できます。

おわりに

並べて比べて見ると、これだけ差があります。

RetainElement(デフォルト) RecycleElement

今回作成したMyCellではCachingStrategyにRecycleElementを指定することで高速化が出来ましたが、どのようなCellでもRecycleElementで高速化出来るとは限りません。データバインディングの数が20以上などの場合には、デフォルトのRetainElementの方が適しているみたいです。作成したCellに合わせてCachingStrategyを適切に設定する必要があります。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2