Xamarin逆引きTips
MvvmCrossで画像をバインディングするには?
MvvmCrossでのiOS/Androidアプリ開発において、画像のURLをViewへバインディングできるMvxImageViewの使い方を説明する。
MvvmCrossのiOS/Androidアプリ開発では、MvxImageViewへ画像のURLをバインディングすることで画像のダウンロードと表示ができる。
今回は、MvxImageViewへバインディングして画像を表示する方法を解説する*1。
- *1 なお、本TipsはMac OS X(10.10.4)+Xamarin Studio(5.9.4)+MvvmCross(3.5.1)で動作を確認している。
MvxImageView
MvvmCrossに標準で含まれているMvxImageView
クラスは、任意の画像をバインディング可能なViewクラスである。iOSではUIImageView
クラスを、AndroidではImageView
クラスを継承しており、画面へ配置することが可能だ。
MvxImageView
クラスのImageUrl
プロパティには、ローカルの画像リソース名または画像URLを指定する。例えば、ファイル名が「sample.png」である画像リソースをアプリが保持しているとき、ImageUrl
プロパティへ文字列「res:sample」を設定すると、それぞれのプラットフォームのリソースから「sample.png」を取得して表示する。このように、リソース名の先頭に「res:」を付与したものはローカルリソースと見なされる。iOSであればXCAssetsの仕組みに従って画像が選択され、AndroidであればDrawableフォルダーの仕組みに従って画像が選択される*2。
- *2 画像リソースは画面解像度や端末の種類によって、OSが適切な画像を選択する。iOSのリソースは「Asset Catalog Help(英語)」を、Androidのリソースは「Providing Resources | Android Developers(英語)」を参照のこと。
ImageUrl
プロパティへHTTPやHTTPSの画像URLが指定されると、MvxImageView
クラスが画面へ表示されるタイミングで画像のダウンロードを開始し、ダウンロードが完了したら画面へ表示される。MvxImageView
クラスにはDefaultImagePath
プロパティとErrorImagePath
プロパティがあり、DefaultImagePath
プロパティは、画像のダウンロードが完了するまでの間に表示する画像リソースを指定し、ErrorImagePath
プロパティは画像のダウンロードが失敗したときに表示する画像リソースを指定する。DefaultImagePath
とErrorImagePath
にはローカルの画像リソースのみ指定することができる。
プロパティ | 説明 |
---|---|
ImageUrl | 画像URLまたはローカル画像リソース名 |
DefaultImagePath | ローカル画像リソース名 |
ErrorImagePath | ローカル画像リソース名 |
MvxImageView
クラスの画像のダウンロードにはDownloadCacheプラグインが使用されており、画像のメモリキャッシュやストレージキャッシュもDownloadCacheプラグインの設定に従って動作する。
実装方針
それでは、実際にMvxImageView
クラスを用いた画像表示を実装してみよう。
今回はDefaultImagePath
プロパティとImageUrl
プロパティの動作を確認する。画面に2つのボタンとMvxImageView
を設置し、ボタンによってImageUrl
プロパティの値を変化させることでMvxImageView
の動作を確認する。
また、DefaultImagePath
プロパティへ設定する画像として、次の「placeholder.png」画像リソースを使用する。
プロジェクトの作成
「Tips: MvvmCrossのプロジェクトをセットアップするには?」の手順に従い、MvvmCrossプロジェクトを作成する。ソリューション名は「CrossImageSample」と設定する。
プラグインの追加
まずは、MvxImageView
クラスの動作に必要なプラグインをNuGetから追加する。Coreプロジェクト、Touchプロジェクト、Droidプロジェクト全てに、以下のNuGetパッケージを取得する。Xamarin Studioでは[ソリューション]ビューのプロジェクトを右クリックし、(それにより表示されるコンテキストメニューの)[追加]-[Add NuGet Packages]から取得する。
Coreの実装
CoreプロジェクトのViewModelクラスを実装する。ViewModels
フォルダーのFirstViewModel.cs
ファイルを次のように実装する。
using Cirrious.MvvmCross.ViewModels;
namespace CrossImageSample.Core.ViewModels
{
public class FirstViewModel : MvxViewModel {
IMvxCommand _reset;
public IMvxCommand Reset {
get {
return _reset ?? (_reset = new MvxCommand(() => {
// 1
ImageUrl = null;
}));
}
}
IMvxCommand _getImage;
public IMvxCommand GetImage {
get {
return _getImage ?? (_getImage = new MvxCommand(() => {
// 2
ImageUrl = "https:///re.buildinsider.net/img/logo-fb.png";
}));
}
}
string _imageUrl = null;
public string ImageUrl { // 3
get { return _imageUrl; }
set {
_imageUrl = value;
RaisePropertyChanged (() => ImageUrl);
}
}
}
}
|
1は画像URLにnull
を設定することで画像表示をリセットするコマンドを実装している。
2は画像URLに「https:///re.buildinsider.net/img/logo-fb.png」を設定するコマンドを実装している。
3はMvxImageView
へバインドするプロパティを定義している。初期値はnull
としている。
Touchプロジェクトの実装
Touchプロジェクトにplaceholder.png画像リソースを設置する。Xamarin Studioでは[ソリューション]ビューから[Resources]フォルダ内の[Images.xcassets]を右クリックし、[追加]ー[New Image Set]によってImages.xcassetsフォルダー内へ「Image.imageset」フォルダーを作成する。作成した「Image.imageset」フォルダーの名前を「placeholder.imageset」へ変更する。「Images.xcassets」をダブルクリックで開くと「placeholder」項目が追加されているので、Finderから「placeholder.png」ファイルをiPhone 1xの項目へドラッグ&ドロップする*3。
- *3 Xamarin Studioではなく、XcodeからImages.xcassetsを開き、Xcode上でXCAssetsの編集をしても構わない
TouchプロジェクトのViewクラスを実装する。Views
フォルダーのFirstView.cs
ファイルを次のように実装する。
using Cirrious.MvvmCross.Binding.BindingContext;
using Cirrious.MvvmCross.Touch.Views;
using CoreGraphics;
using Foundation;
using ObjCRuntime;
using UIKit;
using Cirrious.MvvmCross.Binding.Touch.Views;
namespace CrossImageSample.Touch.Views
{
[Register("FirstView")]
public class FirstView : MvxViewController
{
public override void ViewDidLoad()
{
View = new UIView { BackgroundColor = UIColor.White };
base.ViewDidLoad();
// ios7 layout
if (RespondsToSelector(new Selector("edgesForExtendedLayout")))
{
EdgesForExtendedLayout = UIRectEdge.None;
}
// 1
var resetButton = new UIButton (UIButtonType.System);
resetButton.Frame = new CGRect (10, 10, 100, 40);
resetButton.SetTitle("Reset", UIControlState.Normal);
Add (resetButton);
var getButton = new UIButton (UIButtonType.System);
getButton.Frame = new CGRect (10, 50, 100, 40);
getButton.SetTitle("Get Image", UIControlState.Normal);
Add (getButton);
var imageView = new MvxImageView (new CGRect (10, 100, 200, 200));
imageView.DefaultImagePath = "res:placeholder";
imageView.ContentMode = UIViewContentMode.ScaleAspectFit;
Add (imageView);
// 2
var set = this.CreateBindingSet<FirstView, Core.ViewModels.FirstViewModel>();
set.Bind (resetButton).To (vm => vm.Reset);
set.Bind (getButton).To (vm => vm.GetImage);
set.Bind (imageView).For (v => v.ImageUrl).To (vm => vm.ImageUrl);
set.Apply();
}
}
}
|
1で画面レイアウトを作成している。imageView
のDefaultImagePath
プロパティにローカルリソースの「placeholder」画像リソースを設定している。UIImage
クラスのContentMode
プロパティを設定し、画像がViewのサイズに収まるようにしている。
2でバインディングを設定している。imageView
はImageUrl
プロパティがバインディングされている。
Droidプロジェクトの実装
Droidプロジェクトにplaceholder.png画像リソースを追加する。具体的には図2の手順で、[Resources]ー[drawable-mdpi]フォルダーへplaceholder.png画像リソースを設置する。
Droidプロジェクトのレイアウトを実装する。[Resources]ー[layout]フォルダー内のFirstView.axml
ファイルを次のように実装する。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reset"
local:MvxBind="Click Reset" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Get Image"
local:MvxBind="Click GetImage" />
<!-- 1 -->
<Mvx.MvxImageView
android:id="@+id/image"
android:layout_width="200dp"
android:layout_height="200dp"
android:scaleType="fitCenter"
local:MvxBind="ImageUrl ImageUrl" />
</LinearLayout>
|
1ではMvxImageView
を設置している。MvxImageView
はレイアウトXMLファイル内ではタグ名にMvx.MvxImageView
と指定する。MvxImageView
クラスのImageUrl
プロパティをバインディング設定している。
次に、Viewクラスを実装する。[Views]フォルダー内のFirstView.cs
ファイルを次のように実装する。
using Android.App;
using Android.OS;
using Cirrious.MvvmCross.Droid.Views;
using Cirrious.MvvmCross.Binding.Droid.Views;
namespace CrossImageSample.Droid.Views
{
[Activity(Label = "View for FirstViewModel")]
public class FirstView : MvxActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.FirstView);
// 1
MvxImageView imageView = FindViewById<MvxImageView> (Resource.Id.image);
imageView.DefaultImagePath = "res:placeholder";
}
}
}
|
1で、imageView
のDefaultImagePath
プロパティを設定する。DefaultImagePath
プロパティにはローカルのplaceholder画像リソースを指定している。レイアウトXMLファイルではDefaultImagePath
プロパティを設定することはできないため、FirstView
クラスを実装している。
実行結果
Touchプロジェクト、Droidプロジェクトを実行すると、それぞれ次のような動作結果となる。
起動後はMvxImageView
にはplaceholder画像が表示されており、[Get Image]ボタンをタップすると、画像をダウンロードした後、その画像を表示する。[Reset]ボタンをタップするとplaceholder画像の表示に戻る。さらに、2回目以降の[Get Image]ボタンタップでは、画像がキャッシュされているため、ダウンロードをスキップして指定された画像を表示する。
※以下では、本稿の前後を合わせて5回分(第55回~第59回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
55. MvvmCrossでカスタムコントロールをTwo-Wayバインディングに対応させるには?
MvvmCrossでのiOS/Androidアプリ開発において、カスタムビュークラスをTwo-Wayバインディングに対応させる方法を解説する。
56. Xamarin.FormsでAzureモバイルサービスによるToDoアプリを作成するには?
ひな型プロジェクトが用意されているXamarin.iOSやXamarin.Androidではなく、Xamarin.FormsからAzureモバイルサービスを活用する基本的な方法を、簡単なToDoアプリを題材に解説する。
57. 【現在、表示中】≫ MvvmCrossで画像をバインディングするには?
MvvmCrossでのiOS/Androidアプリ開発において、画像のURLをViewへバインディングできるMvxImageViewの使い方を説明する。
58. MvvmCrossで文字列をローカライズ(多言語化)するには?
MvvmCrossでのiOS/Androidアプリ開発において、ViewModelの文字列リソースを多言語化してローカライズする方法を解説する。
59. MvvmCrossでViewModelからViewにイベントを通知するには?(Messengerパターン)
MvvmCrossでのiOS/Androidアプリ開発において、ViewModelからViewにイベントを通知するMessengerパターンの実装方法を紹介する。