 
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パターンの実装方法を紹介する。





 
  
  
 
