Edited at

[Xamarin.Forms 1.4.0-pre1] ListViewの新機能 "Pull To Refresh"

More than 3 years have passed since last update.


Pull To Refresh

何だかプレビュー版の記事ばかり書いている気がします。

先日、 Xamarin.Forms 1.4.0 のプレビュー版がリリースされました。主な内容はScrollViewとListViewの機能強化です。

その中に、ListViewのPull To Refresh(引っ張って更新するアレ)が簡単に実装できるようになった、と書かれていたので早速試してみました。

Xamarin.Forms 1.4.0のアナウンスによるとPull To Refreshに関わる変更内容は次の通りです。

// ListView

public event EventHandler Refreshing;

public bool IsPullToRefreshEnabled { get; set; } = false;
public bool IsRefreshing { get; set; } = false;
public ICommand RefreshCommand { get; set; } = null;

public void BeginRefresh ();
public void EndRefresh ();

IsPullToRefreshEnabledtrueをセットすることでPull To Refreshが有効になります、falseだと引っ張ってもクルクルしません。

ユーザーがListViewを引っ張るとIsRefreshingtrue変更され、RefreshingイベントやRefreshCommandに登録した処理が呼ばれます。リフレッシュ処理の最後にEndRefresh()メソッドを呼ぶか、IsRefreshingfalseに変更しなければなりません。(忘れるとずっとクルクル)

コードビハインドで書く場合はRefreshingイベント、ViewModelの場合はRefreshCommandにバインド、と使い分けると良いでしょう。

正直なところ、Pull To RefreshでBeginRefresh()を使う必要はありません。これは...


It is important to note that this is the "easy" version of this API, in the future a more complete API with a standalone View may be added.


とあるように、将来追加される"より完璧なAPI"を見越して追加されたのではないかと思います。


イベントで更新するサンプル

Viewのコードビハインドに更新処理を書く場合や、AppクラスでViewを組み立ててしまうような小規模なコードの場合はRefreshingイベントを使うと良いでしょう。

まずはXamarin.Formsのnugetパッケージを更新してください。検索時に「Show pre-release packages」をチェックするとプレビュー版も表示されるようになります。この辺りは以前の記事でも解説していますので合わせてご覧ください。

Appクラスのコンストラクタを次のように書き換えます。


App.cs

            // 追加で必要なusing

// using System.Linq;
// using System.Threading.Tasks;

var listView = new ListView
{
// 「引っ張って更新」を有効にする
IsPullToRefreshEnabled = false,
ItemsSource = Enumerable.Range (1, 10).ToList (),
};

var random = new Random(140);

// 引っ張った時に呼ばれる
listView.Refreshing += async (sender, e) => {
// ネットワークアクセスを装ったディレイ
await Task.Delay(1000);

// リストビューのデータを更新
listView.ItemsSource =
Enumerable.Range(1, 10)
.Select(x => random.Next()).ToList();

// リフレッシュ完了、どちらかを実行すれば良い
listView.IsRefreshing = false;
// listView.EndRefresh();
};

MainPage = new ContentPage {
Content = listView,
Padding = new Thickness(5, Device.OnPlatform(20, 0, 0), 5, 0),
};


実行すると、リストを下に引っ張る度に数値が変化します。


コマンドで更新するサンプル

MVVMでアプリケーションを実装する場合はRefreshCommandを使うと良いでしょう。通常、ViewModelはViewの参照を直接持たないのでIsRefreshingEndRefresh()にアクセスできません、ちょっと工夫が必要です。例えば、ViewModelにbool型のプロパティを設け、IsRefreshingプロパティと 双方向にバインド してやると良いでしょう。

Sharedプロジェクト(またはPCLプロジェクト)に次のようなViewModelクラスを追加します。


MainPageViewModel.cs

using System;

using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Xamarin.Forms;
using System.Windows.Input;

namespace PullToRefreshSample
{
public class MainPageViewModel : BindableObject
{
// ListViewのデータソース
public ObservableCollection<Color> Colors {
get;
private set;
}

// ListView.IsRefreshingと同期させるプロパティ
private bool isRefreshing;
public bool IsRefreshing
{
get { return isRefreshing; }
set
{
if (value == isRefreshing)
return;
isRefreshing = value;
OnPropertyChanged ();
}
}

// ListViewを引っ張った時に実行させるコマンド
public ICommand RefreshCommand {
get;
private set;
}

public MainPageViewModel ()
{
Colors = new ObservableCollection<Color> {
Color.Aqua,
Color.Blue,
Color.Fuchsia,
Color.Gray,
Color.Green,
Color.Lime,
Color.Maroon,
Color.Navy,
Color.Olive,
Color.Pink,
};

var random = new Random (140);

RefreshCommand = new Command (async (nothing) => {
// ランダムな色に更新
for(var i = 0; i < Colors.Count; i++)
{
await Task.Delay(100);
Colors[i] = new Color(
random.NextDouble (),
random.NextDouble (),
random.NextDouble ()
);
}

// Binding機構経由でListViewのIsRefreshingプロパティも変更する
IsRefreshing = false;
},
// ICommand.CanExecuteにもバインドしたプロパティを利用できる
(nothing) => !IsRefreshing
);
}
}
}


Sharedプロジェクト(またはPCLプロジェクト)にContentPageを追加、XAMLコードを次のように修正します。


MainPage.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="PullToRefreshSample.MainPage">
<ContentPage.Content>
<!-- IsRefreshingプロパティに双方向でバインドするのがミソ -->
<ListView IsPullToRefreshEnabled="true"
SeparatorVisibility="None"
RefreshCommand="{Binding RefreshCommand}"
IsRefreshing="{Binding IsRefreshing, Mode=TwoWay}"
ItemsSource="{Binding Colors}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<BoxView Color="{Binding}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>

最後にAppクラスのコンストラクタを次のように書き換えます。


App.cs

            MainPage = new MainPage {

BindingContext = new MainPageViewModel(),
};

実行するとこうなります。

PullToRefreshSample.gif


まとめ

Xamarin.Forms 1.3.4の更新内容("iOS Dynamic cell sizing support")と合わせて、すぐにでもTwitterクライアントが作れそうですね!