Android 3.0 ã§ã¯ãç»é¢ã«è¡¨ç¤ºãããé ç®ã¨ãã¦ãActivity ã®ä¸ã« Fragment ã¨ããè¦ç´ ã使ãããã«ãªããã¿ãã¬ãã対å¿ãã¿ãã®ä½¿ç¨ãªã©ã§ã¯å¿ é ã¬ãã«ã§ä½¿ç¨ããããã«ãªã£ã¦ãã¾ãã
MvvmCross ã«ã¯ããã®æ©è½ã«å¯¾ã㦠Fragging ã¢ã¸ã¥ã¼ã«ã¨ãFullFragging ã¢ã¸ã¥ã¼ã«ã¨ãã 2 種é¡ã® Fragment ãµãã¼ããããã¾ãã Fragging ã¢ã¸ã¥ã¼ã«ã¯ Android 2.x ã§ã使ããããã«ããã¯ãã¼ãããã Support Library ã® Fragment ã使ç¨ãããã®ãFullFragging ã¢ã¸ã¥ã¼ã«ã¯ Android 3.0 以éã«æè¼ãããæ¨æºã® Fragment ã使ç¨ãããã®ã¨ãªã£ã¦ãã¾ãã
Fragging/FullFragging ã¢ã¸ã¥ã¼ã«ã§ã¯ MvxFragment ã¨ãã View ã¯ã©ã¹ãæä¾ããã¾ããããã®ã¯ã©ã¹ã«é¢ãã使ç¨æ¹æ³ãããã¥ã¡ã³ãä¸ã«ããã¾ãããã¾ããã¢ããªãåæ¢ç¶æ ã«ãªãéã®å¦ççãç¾ç¶ãµãã¼ãããã¦ãããããã®ã¾ã¾ã®ç¶æ ã§ä½¿ç¨ããã¨ã¡ã¢ãªãå°ãªãæ©ç¨®çã§åé¡ã«ãªã£ã¦ãã¾ãã
ãã®è¨äºã§ã¯ãMvxFragment ã¯ã©ã¹ã®ä½¿ãæ¹ã¨ãMvxFragment ã¯ã©ã¹ä¸ã§åæ¢æ©è½ã使ããããã«æ¡å¼µããæ¹æ³ãç´¹ä»ãã¾ãã
MvxFragment ã¯ã©ã¹ã使ç¨ã§ããããã«ãã
NuGet ã§ä»¥ä¸ã®ããã±ã¼ã¸ãå°å ¥ãã¾ããä»å㯠FullFragging ã¢ã¸ã¥ã¼ã«ããã¼ã¹ã«ç´¹ä»ãã¾ãã
- MvvmCross.HotTuna.Droid.FullFragging (éçºä¸ã®ã¢ããªã Android 3.0 以éã®ã¿ã対象ã«ããå ´å)
- MvvmCross.HotTuna.Droid.Fragging (éçºä¸ã®ã¢ããªã Android 2.x ã対象ã«ããå ´å)
Fragment ãå¼ã³åºããããã«ã¤ã³ã¿ã¼ãã§ã¤ã¹ã»ã¯ã©ã¹ã追å ãã
ViewModel ã表示ãããªã¯ã¨ã¹ããã Fragment ã表示ããããã«ã¯ãActivity ã表示ãããã¨ããé常ã®åä½ãããã¯ãã¦ãFragment ã表示ããå¦çã«ç½®ãæãã¾ãã
表示ä¸ã® Activity ããViewModel ã«å¯¾å¿ãã Fragment ãå«ãå ´åã¯ãããå¦çãã¦è¡¨ç¤ºã§ãããããã¤ã³ã¿ã¼ãã§ã¤ã¹ãç¨æãã¾ããIFragmentHost ã¤ã³ã¿ã¼ãã§ã¤ã¹ãã.Droid ã®ããã¸ã§ã¯ãã«è¿½å ãã¾ã
using Cirrious.MvvmCross.ViewModels; namespace RestoreTest.Droid { /// <summary> /// Fragment ã表示ãã Activity ãæã¤ã¤ã³ã¿ã¼ãã§ã¤ã¹ /// </summary> public interface IFragmentHost { /// <summary> /// Fragment ã表示ãã /// </summary> /// <param name="request">表示ãããªã¯ã¨ã¹ã</param> /// <returns>渡ãããViewModelRequest ãå¦çãã㦠Fragment ã®è¡¨ç¤ºå¦çãå®è¡ãããã trueãããã§ãªãå ´å㯠false ã</returns> bool Show(MvxViewModelRequest request); } }
次ã«ããã®ã¤ã³ã¿ã¼ãã§ã¤ã¹ãæ㤠Activity ã表示ããã¦ããå ´åã¯ãShow ã¡ã½ãããéãã¦ãã® Activity ã« ViewModelRequest ã渡ãããããã«ã¹ã¿ã ã® ViewPresenter ãå®ç¾©ãã¾ãã
using Cirrious.MvvmCross.Droid.Views; using Cirrious.MvvmCross.ViewModels; namespace RestoreTest.Droid { /// <summary> /// ã«ã¹ã¿ã ã® ViewPresenter /// </summary> public class ViewPresenter : MvxAndroidViewPresenter { /// <summary> /// ViewModelRequest ã®å¦ç /// </summary> /// <param name="request">Request.</param> public override void Show(MvxViewModelRequest request) { // ç¾å¨ã® Activity ã IFragmentHost ãå®è£ ãã¦ããã var host = Activity as IFragmentHost; if (host != null) { // IFragmentHost.Show çµç±ã§ Fragment ã表示 if (host.Show(request)) { // å¼ã³åºããã¡ã½ããå 㧠Fragment ã表示ãããã°ããã§çµäº return; } } // å½ã¦ã¯ã¾ããªããã°é常㮠MvxActivity ã表示ããå¦ç base.Show(request); } } }
Fragment 㯠Views 以ä¸ã« Fragments ã®é層ãä½ã£ã¦ãã®ä¸ã«å ¥ãããã¨ãå¤ãããã§ããFragments ãã©ã«ããä½ã£ã¦ããã®ä¸ã«ãã©ã°ã¡ã³ãåºåºã¯ã©ã¹ BaseFragment ãä½æãããã®ã¯ã©ã¹ããã¼ã¹ã¯ã©ã¹ã¨ã㦠Fragment ãå®è£ ãã¾ãã(å¾ã»ã©ã復帰æ©è½ãã¤ããã¨ãã«å¿ è¦ã«ãªã£ã¦ãã¾ã)
using Android.Views; using Cirrious.MvvmCross.Droid.FullFragging.Fragments; namespace RestoreTest.Droid.Views.Fragments { public class FragmentBase : MvxFragment { } }
Fragment ã¯ãActivity ã¨åæ§ã«ã¬ã¤ã¢ã¦ããã¡ã¤ã«ãä½ãã対å¿ããã¯ã©ã¹ã以ä¸ã®ããã«å®ç¾©ãã¾ã(OnCreateView 㧠View ãä½æãã¾ãã詳ãã㯠Fragment ã®ã©ã¤ããµã¤ã¯ã«ã«ã¤ãã¦èª¿ã¹ã¦ã¿ã¦ãã ãã)ã
using Android.OS; using Android.Views; using Cirrious.MvvmCross.Binding.Droid.BindingContext; using Cirrious.CrossCore; namespace RestoreTest.Droid.Views.Fragments { public class ThirdView : FragmentBase { public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // MvxFragment ã®ä»æ§ä¸ãå¿ ã base ãå¼ã¶å¿ è¦ãããã // æ»ãå¤ã¯æ¨ã¦ã base.OnCreateView(inflater, container, savedInstanceState); // 表示ãã View ãã¬ã¤ã¢ã¦ããã¡ã¤ã«ããã BindingInflate ã§çæããã // BindingInflate ã§ãªã㨠local:MvxBind ãæ¸ãã¦ããç®æã§ã¯ã©ãã·ã¥ããã®ã§æ³¨æã return this.BindingInflate(Resource.Layout.ThirdView, null); } } }
ãã¨ã¯ããã® Fragment ã表示ãã Activity ã« Fragment ãå¦çããæ©è½ã追å ãã¾ãã
using Cirrious.MvvmCross.Droid.Views; using Android.App; using Android.OS; using Cirrious.CrossCore; using Cirrious.MvvmCross.ViewModels; using RestoreTest.Core.ViewModels; using RestoreTest.Droid.Views.Fragments; namespace RestoreTest.Droid.Views { [Activity(Label = "View for SecondViewModel")] public class SecondView : MvxActivity, IFragmentHost { ThirdView _thirdViewFragment; protected override void OnCreate(Bundle bundle) { SetContentView(Resource.Layout.SecondView); } #region IFragmentHost implementation public bool Show(MvxViewModelRequest request) { // ãã®ç»é¢ã§è¡¨ç¤ºã§ããåã® ViewModel ãªã if (request.ViewModelType == typeof(ThirdViewModel)) { // View ãä½ã£ã¦ _thirdViewFragment = new ThirdView(); // ViewModel ãä½ã£ã¦ var loaderService = Mvx.Resolve<IMvxViewModelLoader>(); _thirdViewFragment.ViewModel = loaderService.LoadViewModel(viewModelRequest, savedState); // Fragment ã®è¡¨ç¤ºé åã«è¿½å ãã var trans = FragmentManager.BeginTransaction(); trans.Add(Resource.Id.fragmentFrame, _thirdViewFragment); trans.Commit(); return true; } return false; } #endregion } }
ããã¾ã§ã®å 容ã¯å ¬å¼ã®ãµã³ãã«ã®éãã§ãã
ããããªããããã®æ¹æ³ã§ä½ã£ãã¢ããªã¯ãããã¯ã°ã©ã¦ã³ãã«éãããå¾ãããç¨åº¦ã¡ã¢ãªè² è·ããããç¶æ ã§å¾©å¸°ãããã¨ããå ´åã¯ã©ãã·ã¥ãããããã¾ãã¾è¡¨ç¤ºã§ããã¨ãã¦ãæå¾ ããåä½ãããªããªã£ã¦ãã¾ãã¾ãã
ããã¯ãããç¨åº¦ãªã½ã¼ã¹ãå§è¿«ãããã¨ãAndroid ã® OS å´ã®å¶å¾¡ã§ã¢ããªãå¼·å¶çã«åæ¢ãããããã§ããå¼·å¶çã«åæ¢ãããã¢ããªã¯ãå ã 表示ããã¦ããç»é¢ã§å¾©å¸°ãããã¨ãã¾ãããæ°è¦ã¤ã³ã¹ã¿ã³ã¹ã¨ãªãããããã£ã¼ã«ããããããã£ã¯ãã¹ã¦ã¯ãªã¢ããã¦ãã¾ãã¾ãã
Activity ã Fragment ã«ã¯ OnSaveInstanceState ã¨ããã¡ã½ãããããããã®ã¡ã½ããã«æ¸¡ããã Bundle ã«å種ãã¼ã¿ãä¿åãã¦ããã° OnCreate ã OnCreateView ãªã©ã«æ¸¡ããã Bundle ã§ååå¾ã§ãã復å ãããã¨ãã§ãã¾ããMvvmCross ã§ããã®æ©è½ã¯ãµãã¼ãããã¦ãã¾ãããFragment ã«é¢ãã¦ã¯å¦çãå®è£ ããã¦ãã¾ããã
ã¾ããShow ã¡ã½ãã㧠ViewModel ãç´æ¥ã»ãããã¾ãããããããåæåãã㦠ViewModel ãæ¶ãã¦ããã¨ããç¶æ ã«ãªã£ã¦ãã¾ãã¾ããFragment ã®åæåã«é¢ããæ å ±ã¯ Argument ã¨ããããããã£ã«å ¥ãã¦ãããã¨ãã§ããã®ã§ããã¡ãã使ããFragment èªèº«ã« ViewModel ãä½ãããå¿ è¦ãããã¾ãã
Fragment ã«ç¶æ ä¿åæ©è½ã追å ãã
æ¬æ¥ã§ããã° MvvmCross å´ãä¿®æ£ãã¹ãæã§ãããããªãè¤éãã¤ãå ¥ãæ¿ããæéãªã®ã§æ¡å¼µã¡ã½ããã¨ã¢ããªã±ã¼ã·ã§ã³å´ã®ãã¼ã¹ã¯ã©ã¹ãç¨æããå½¢ã§å¯¾å¿ãããã¨ã«ãã¾ããã
ã¾ããFragmentExtension ã追å ãã¾ããããã«ã¯ç¶æ ä¿åã ViewModel ã®ãã©ã¡ã¼ã¿ã®å¼ã渡ããªã©ã®å¦çãè¨è¿°ãã¦ãã¾ãã
using System; using Cirrious.MvvmCross.Droid.FullFragging.Fragments; using Android.OS; using Android.Views; using Cirrious.MvvmCross.ViewModels; using Cirrious.MvvmCross.Views; using Cirrious.CrossCore; using Cirrious.MvvmCross.Droid.Platform; using Cirrious.MvvmCross.Droid.Views; namespace RestoreTest.Droid.Views.Fragments { /// <summary> /// Fragment é¢é£ã®æ¡å¼µã¡ã½ãã /// </summary> public static class FragmentExtensions { /// <summary> /// Arguments ã«ç»é²ããèµ·åãã©ã¡ã¼ã¿ /// </summary> public const string ExtrasKey = "MvxLaunchData"; /// <summary> /// ViewModel ã« Request ãç»é²ãã /// </summary> public static void ProvideViewModelRequest(this MvxFragment fragment, MvxViewModelRequest request) { var bundle = fragment.Arguments ?? (fragment.Arguments = new Bundle()); var converter = Mvx.Resolve<IMvxNavigationSerializer>(); var requestText = converter.Serializer.SerializeObject(request); bundle.PutString(ExtrasKey, requestText); } /// <summary> /// Fragment ã® OnCreateView ã§å¼ã³åºãã¡ã½ãã /// ViewModel ãçæã»ç¶æ 復å ããã /// </summary> public static void CreateViewCalled(this MvxFragment fragment, LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { fragment.ViewModel = fragment.LoadViewModel(savedInstanceState); } /// <summary> /// Fragment ã® OnSaveInstanceStateCalled ã§å¼ã³åºãã¡ã½ãã /// ViewModel ã®ç¶æ ä¿åã¡ã½ãããå¼ã³åºã /// </summary> public static void SaveInstanceStateCalled(this MvxFragment fragment, Bundle outState) { var mvxBundle = fragment.CreateSaveStateBundle(); if (mvxBundle != null) { IMvxSavedStateConverter converter; if (!Mvx.TryResolve<IMvxSavedStateConverter>(out converter)) { Mvx.Warning("Saved state converter not available - saving state will be hard"); } else { converter.Write(outState, mvxBundle); } } var cache = Mvx.Resolve<IMvxSingleViewModelCache>(); cache.Cache(fragment.ViewModel, outState); } /// <summary> /// ViewModel ãèªã¿è¾¼ã /// </summary> /// <remarks>MvxActivityViewExtensions ã®ååã¡ã½ããã®ã³ãããªã®ã§ä½ã¨ãããã</remarks> static IMvxViewModel LoadViewModel(this MvxFragment fragment, Bundle savedInstanceState) { var viewModelType = fragment.FindAssociatedViewModelTypeOrNull(); if (viewModelType == typeof(MvxNullViewModel)) return new MvxNullViewModel(); if (viewModelType == null || viewModelType == typeof (IMvxViewModel)) { Mvx.Trace("No ViewModel class specified for {0} in LoadViewModel", fragment.GetType().Name); } var extraData = fragment.Arguments.GetString(ExtrasKey); if (extraData == null) return null; var savedState = GetSavedStateFromBundle(savedInstanceState); var converter = Mvx.Resolve<IMvxNavigationSerializer>(); var viewModelRequest = converter.Serializer.DeserializeObject<MvxViewModelRequest>(extraData); var loaderService = Mvx.Resolve<IMvxViewModelLoader>(); var viewModel = loaderService.LoadViewModel(viewModelRequest, savedState); return viewModel; } /// <summary> /// SavedInstanceState ã«ä¿åããã¦ããç¶æ ãªãã¸ã§ã¯ããåãåºã /// </summary> /// <remarks>MvxActivityViewExtensions ã®ååã¡ã½ããã®ã³ãããªã®ã§ä½ã¨ãããã</remarks> static IMvxBundle GetSavedStateFromBundle(Bundle bundle) { if (bundle == null) return null; IMvxSavedStateConverter converter; if (!Mvx.TryResolve<IMvxSavedStateConverter>(out converter)) { Mvx.Trace("No saved state converter available - this is OK if seen during start"); return null; } var savedState = converter.Read(bundle); return savedState; } } }
次ã«ãFragmentExtension ã«å®ç¾©ããå¦çãå¼ã³åºãããã«ãFragmentBase ãä¿®æ£ãã¾ãã
using Android.Views; using Cirrious.MvvmCross.Droid.FullFragging.Fragments; namespace RestoreTest.Droid.Views.Fragments { public class FragmentBase : MvxFragment { public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Android.OS.Bundle savedInstanceState) { this.CreateViewCalled(inflater, container, savedInstanceState); return base.OnCreateView(inflater, container, savedInstanceState); } public override void OnSaveInstanceState(Android.OS.Bundle outState) { this.SaveInstanceStateCalled(outState); } } }
æå¾ã«ãFragment ãçæããé¨åãä¿®æ£ãã¾ããViewModel ãç´æ¥ä½ãã®ã§ã¯ãªããæ¡å¼µã¡ã½ããã§å®ç¾©ãããã©ã¡ã¼ã¿å¼ã渡ãã®å¦çã使ç¨ããããã«ãã¾ãã
public bool Show(MvxViewModelRequest request) { if (request.ViewModelType == typeof(ThirdViewModel)) { _thirdViewFragment = new ThirdView(); _thirdViewFragment.ProvideViewModelRequest(request); var trans = FragmentManager.BeginTransaction(); trans.Add(Resource.Id.fragmentFrame, _thirdViewFragment); trans.Commit(); return true; } return false; }
ViewModel å´ã®ç¶æ ä¿å
ãªããããã¾ã§ã®å®è£ ã§ã©ã¤ããµã¤ã¯ã«ã¯ã¤ãªããã¾ããããViewModel ã§å®éã«å¤ãèªã¿æ¸ãããªãã¨ç¶æ 復帰ããããã¨ã¯ã§ãã¾ããã
ç¶æ ä¿åãå¿ è¦ãªã¨ããã«ãªãã¨ãViewModel ã® SaveStateToBundle ããç¶æ 復å ãå¿ è¦ã«ãªã㨠LoadStateFromBundle ãå¼ã³åºããã¾ãã®ã§ãããã§å¤ãèªã¿æ¸ãããããã«ãã¾ãã
using Cirrious.MvvmCross.ViewModels; namespace RestoreTest.Core.ViewModels { public class ThirdViewModel : MvxViewModel { const string HelloKey = "Hello"; string _hello = "Hello MvvmCross"; public string Hello { get { return _hello; } set { _hello = value; RaisePropertyChanged(() => Hello); } } /// <summary> /// ã³ã³ã¹ãã©ã¯ã¿ /// 1çªç®ã«å¼ã°ãã /// </summary> public ThirdViewModel() { } /// <summary> /// ViewModel ã®ãã©ã¡ã¼ã¿ãå¦çãã /// 2çªç®ã«å¼ã°ãã /// </summary> /// <param name="parameters">Parameters.</param> protected override void InitFromBundle(IMvxBundle parameters) { base.InitFromBundle(parameters); } /// <summary> /// ç¶æ ä¿åãããå¤ãèªã¿è¾¼ã /// ä¿åããããã¼ã¿ãããã° InitFromBundle ã®å¾ã«å¼ã°ãã /// </summary> /// <param name="state">State.</param> protected override void ReloadFromBundle(IMvxBundle state) { base.ReloadFromBundle(state); if (state != null) { if (state.Data.ContainsKey(HelloKey)) { Hello = state.Data[HelloKey]; } } } /// <summary> /// ä¸çªæåã«è¡¨ç¤ºãããã¿ã¤ãã³ã°ã§å¼ã°ãã /// </summary> public override void Start() { base.Start(); } /// <summary> /// ç¶æ ä¿åãå¿ è¦ãªã¿ã¤ãã³ã°ã§å¼ã°ãã /// </summary> /// <param name="bundle">Bundle.</param> protected override void SaveStateToBundle(IMvxBundle bundle) { base.SaveStateToBundle(bundle); bundle.Data.Add(HelloKey, Hello); } } }
ç¶æ 復帰ã®ãã¹ãæ¹æ³
ãªããç¶æ 復帰ã¯ç«¯æ«ã®è¨å®ãéçºè åããªãã·ã§ã³ããå¤ãããã¨ã§ç°¡åã«ãããã¨ãã§ãã¾ãã
- ã¢ã¯ãã£ããã£ãä¿æããªã
- ããã¯ã°ã©ã¦ã³ãããã»ã¹ã®ä¸é: 1
ãã®ç¶æ ã§å¥ã®ã¢ããªã«åãæ¿ãã¦æ»ã£ã¦ããã°ãç¶æ 復å ãèµ·ãããã¨ããããã¾ãã
æå¾ã«
ãã®è¨äºã§åãä¸ãã¦ããã³ã¼ãã®å ¨ä½ã¯ä»¥ä¸ã®å ´æã«ããã¾ãã®ã§ãä½µãã¦åèã«ãã¦ãã ããã
ãªããç¶æ ã復å ãããªãç¹ã«ã¤ãã¦ã¯ããã§ã« MvvmCross ã® Issue ã«ããã£ã¦ãã¾ãã