歡迎大家加入以下開源社區
Xamarin-Cn:https://github.com/Xamarin-Cn
Mvvmcross-Cn:https://github.com/Mvvmcross-Cn
(另外微信訂閱號 Xamarin 所有者@善友兄也給予了運營權限,后面將開始陸續運營起來,給大家推送相關的文章)
簡介
相信做了開發有一定經驗的人都知道IOC的存在,而Xamarin.Android中還沒有IOC的存在。特別是在Xamarin.Android下,如果僅僅只是顯示簡單的數據,就需要通過很多的FindViewById來查找組件,並且還要負責呈現,今天我們將通過學習MVVMCross組件來簡化這些操作,達到PCL部分的最大化,下面我們以一個官方的DEMO來學習。
題外話:
由於公司的發展需要,需要招聘Xamarin方面的人才,如果你對Xamarin感興趣的可以直接聯系樓主,現在我們要召集8個人進行這方面的培訓,所以只要有C#基礎,並且有很強的興趣都可以來。並且在產品完成后我們將會把開發的經驗都撰寫成博客發表,造福整個Xamarin(Q+976691141)。
PCL部分
基礎准備
首先我們需要新建一個名為“TipCalc.Core”的“類庫(可移植)”的項目,並且我們需要按照下圖選擇對應的平台:
如果你的平台沒有紅色框住的部分也沒有問題的,因為當前主流的PCL的方案是Profile259,如果你要查看你新建的類庫是不是259可以查看項目的csproj文件,就是{項目名}.csproj,用記事本打開,然后定位到這個位置就可以看到了:
警告:
如果上面的PCL部分你按照我的選擇之后,確定按鈕是禁用的,就需要跟着下面的教程來解決這個問題,首先我們需要打開下面這個文件夾:
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\Profile\Profile78\SupportedFrameworks
將該文件夾中的以下文件復制:
如果不存在“Xamarin.iOS.Unified.xml”也沒有關系,之后我們打開下面這個文件夾:
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\Profile\Profile259\SupportedFrameworks
將上面的文件復制到其中,然后重啟VS就可以解決問題了。
項目搭建
刪除Class1.cs文件
我們並不需要使用該文件
安裝MvvmCross
我們打開“程序包管理器控制台”輸入以下指令后回車:
Install-Package MvvmCross.HotTuna.MvvmCrossLibraries
添加Tip Calculation 服務
創建一個名為“Services”的文件夾,並在該文件夾中新建一個名為“ICalculation”的接口,具體的內容代碼如下所示:
1 namespace TipCalc.Core.Services 2 { 3 public interface ICalculation 4 { 5 double TipAmount(double subTotal, int generosity); 6 } 7 }
並在該文件夾中新建一個Calculation類實現上面的接口,具體代碼如下:
1 namespace TipCalc.Core.Services 2 { 3 public class Calculation : ICalculation 4 { 5 public double TipAmount(double subTotal, int generosity) 6 { 7 return subTotal * ((double)generosity)/100.0; 8 } 9 } 10 }
以上部分在實際項目中我們可以認為是應用服務層。
添加視圖模型
視圖模型將會連接最終的界面與上面的服務層,而為了達到這些效果,我們的視圖模型必須繼承自MvxViewModel。下面我們先新建一個ViewModels文件夾並新建一個TipViewModel類,具體的代碼如下所示:
1 namespace TipCalc.Core.ViewModels 2 { 3 public class TipViewModel : MvxViewModel 4 { 5 private readonly ICalculation _calculation; 6 public TipViewModel(ICalculation calculation) 7 { 8 _calculation = calculation; 9 } 10 11 public override void Start() 12 { 13 _subTotal = 100; 14 _generosity = 10; 15 Recalcuate(); 16 base.Start(); 17 } 18 19 private double _subTotal; 20 21 public double SubTotal 22 { 23 get { return _subTotal; } 24 set { _subTotal = value; RaisePropertyChanged(() => SubTotal); Recalcuate(); } 25 } 26 27 private int _generosity; 28 29 public int Generosity 30 { 31 get { return _generosity; } 32 set { _generosity = value; RaisePropertyChanged(() => Generosity); Recalcuate(); } 33 } 34 35 private double _tip; 36 37 public double Tip 38 { 39 get { return _tip; } 40 set { _tip = value; RaisePropertyChanged(() => Tip);} 41 } 42 43 private void Recalcuate() 44 { 45 Tip = _calculation.TipAmount(SubTotal, Generosity); 46 } 47 } 48 }
通過上面的代碼我們可以看到,視圖模型的構造函數要求傳入一個實現了ICalculation接口的參數,而這個過程將會有IOC幫我們完成。剩下的我們可以看到我們重寫了Start方法用來給視圖模型中的值賦初始值,並且在每個值改變的同時還調用了Recalcuate方法來重新計算。
OK,完成這些之后,我們還需要一個啟動項對依賴注入進行配置,並且指定初始顯示的視圖模型,我們直接在項目根目錄下新建一個App類,並且該類需要繼承自MvxApplication類,並在構造函數中寫入以下代碼:
1 namespace TipCalc.Core 2 { 3 public class App : MvxApplication 4 { 5 public App() 6 { 7 Mvx.RegisterType<ICalculation,Calculation>(); 8 Mvx.RegisterSingleton<IMvxAppStart>(new MvxAppStart<TipViewModel>()); 9 } 10 } 11 }
其中我們可以看到我們通過Mvx.RegisterType對IOC進行了配置,這樣IOC才能夠知道在需要ICalculation接口的時候實例化哪個類,而下一行的代碼則是指定初始的視圖模型,也就是首頁,完成上面這些配置后我們的PCL部分完成了,可以通用於Android和IOS。
Android UI呈現
首先我們創建一個“Blank App(Android)”項目,項目名為“TipCalc.UI.Droid”。並將項目默認生成的MainActivity.cs文件和Resources/Layout文件夾下的Main.axml刪除並添加TipCalc.Core.csproj引用。
安裝MvvmCross
跟PCL部分一樣,我們還需要安裝對應的類庫:
Install-Package MvvmCross.HotTuna.MvvmCrossLibraries
添加MvvmCross Android綁定資源
該部分是為了我們能夠在界面中使用bind屬性,所以我們需要在Resources/Values文件夾下新建一個xml資源,名字可以為“MvxBind”,對應的內容如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3 <declare-styleable name="MvxBinding"> 4 <attr name="MvxBind" format="string"/> 5 <attr name="MvxLang" format="string"/> 6 </declare-styleable> 7 <declare-styleable name="MvxListView"> 8 <attr name="MvxItemTemplate" format="string"/> 9 <attr name="MvxDropDownItemTemplate" format="string"/> 10 </declare-styleable> 11 <item type="id" name="MvxBindingTagUnique"/> 12 <declare-styleable name="MvxImageView"> 13 <attr name="MvxSource" format="string"/> 14 </declare-styleable> 15 </resources>
添加啟動類
雖然PCL中有啟動項了,但是我們需要在特定中進行注冊,所以我們還需要新建一個Setup類,並且該類的主要內容如下:
1 namespace TipCalc.UI.Droid 2 { 3 public class Setup : MvxAndroidSetup 4 { 5 public Setup(Context applicationContext) 6 : base(applicationContext) 7 { 8 } 9 10 protected override IMvxApplication CreateApp() 11 { 12 return new App(); 13 } 14 } 15 }
主要作用就是將App實例化並返回,以便在特定平台中進行初始化。
添加視圖
我們需要在Resources/Layout文件夾下新建一個視圖名為View_Tip.axml,並且其中的內容如下所示:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:local="http://schemas.android.com/apk/res/TipCalc.UI.Droid" 4 android:orientation="vertical" 5 android:layout_width="fill_parent" 6 android:layout_height="fill_parent"> 7 <TextView 8 android:layout_width="fill_parent" 9 android:layout_height="wrap_content" 10 android:text="SubTotal" /> 11 <EditText 12 android:layout_width="fill_parent" 13 android:layout_height="wrap_content" 14 local:MvxBind="Text SubTotal" /> 15 <TextView 16 android:layout_width="fill_parent" 17 android:layout_height="wrap_content" 18 android:text="Generosity" /> 19 <SeekBar 20 android:layout_width="fill_parent" 21 android:layout_height="wrap_content" 22 android:max="40" 23 local:MvxBind="Progress Generosity" /> 24 <View 25 android:layout_width="fill_parent" 26 android:layout_height="1dp" 27 android:background="#ffff00" /> 28 <TextView 29 android:layout_width="fill_parent" 30 android:layout_height="wrap_content" 31 android:text="Tip to leave" /> 32 <TextView 33 android:layout_width="fill_parent" 34 android:layout_height="wrap_content" 35 local:MvxBind="Text Tip" /> 36 </LinearLayout>
通過其中的內容我們可以看到,其中多了一個MvxBind屬性,通過這個屬性我們可以直接將控件的屬性直接與ViewModel中對應的屬性直接關聯起來,這樣我們可以省去與控件交互的部分,但是我們還是需要Activity來將界面與我們的ViewModel進行關聯。
添加視圖類
我們直接新建一個名為TipCalcView的活動,然后將其繼承的類改成MvxActivity,具體的代碼如下:
1 namespace TipCalc.UI.Droid.Views 2 { 3 [Activity(Label = "Tip", MainLauncher = true)] 4 public class TipView : MvxActivity 5 { 6 public new TipViewModel ViewModel 7 { 8 get { return (TipViewModel) base.ViewModel; } 9 set { base.ViewModel = value; } 10 } 11 12 protected override void OnViewModelSet() 13 { 14 base.OnViewModelSet(); 15 SetContentView(Resource.Layout.View_Tip); 16 } 17 } 18 }
完成這些之后,直接運行,我們可以看到調整進度條的同時,Tip to leave的值會跟着動,完全是實時的。
當然原生的方式與MvvmCross的方式是可以直接並存的。