From a9927224d3031fdb417f3dde553c2490ecf89cae Mon Sep 17 00:00:00 2001 From: Toine Date: Mon, 19 Sep 2016 15:18:18 +0200 Subject: [PATCH] - Implemented Stack to track fragment tags (top most is current/last fragment) - Inserted tracking in Show And Close methods - Implemented OnSaveInstanceState and Restore to keep Stack - ToFragment() for IMvxFragmentView to Fragments --- .../MvxCachingFragmentCompatActivity.cs | 79 ++++++++++++++++--- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/MvvmCross.Droid.Support.V7.AppCompat/MvxCachingFragmentCompatActivity.cs b/MvvmCross.Droid.Support.V7.AppCompat/MvxCachingFragmentCompatActivity.cs index 93bb050..e1faba6 100644 --- a/MvvmCross.Droid.Support.V7.AppCompat/MvxCachingFragmentCompatActivity.cs +++ b/MvvmCross.Droid.Support.V7.AppCompat/MvxCachingFragmentCompatActivity.cs @@ -17,7 +17,6 @@ using MvvmCross.Platform; using MvvmCross.Platform.Exceptions; using MvvmCross.Platform.Platform; -using MvvmCross.Binding.Droid.BindingContext; using MvvmCross.Droid.Platform; using MvvmCross.Droid.Views; using MvvmCross.Core.ViewModels; @@ -26,6 +25,7 @@ using MvvmCross.Droid.Shared.Presenter; using MvvmCross.Droid.Shared.Attributes; using MvvmCross.Droid.Shared.Fragments; +using MvvmCross.Droid.Support.V4; namespace MvvmCross.Droid.Support.V7.AppCompat { @@ -34,7 +34,10 @@ public class MvxCachingFragmentCompatActivity : MvxAppCompatActivity, IFragmentC { public const string ViewModelRequestBundleKey = "__mvxViewModelRequest"; private const string SavedFragmentTypesKey = "__mvxSavedFragmentTypes"; + private const string SavedTagBackStack = "__mvxSavedTagBackStack"; + private IFragmentCacheConfiguration _fragmentCacheConfiguration; + private readonly Stack _tagBackStack = new Stack(); public override View OnCreateView(View parent, string name, Context context, IAttributeSet attrs) { @@ -119,6 +122,21 @@ private void RestoreFragmentsCache() } } + private void RestoreTagBackStackFromBundle(IMvxJsonConverter serializer, Bundle savedInstanceState) + { + var savedTagBackStackJson = savedInstanceState.GetString(SavedTagBackStack); + if (!string.IsNullOrEmpty(savedTagBackStackJson)) + { + var savedReversedTagBackStackState = serializer.DeserializeObject(savedTagBackStackJson); + + _tagBackStack.Clear(); + for (int i = 0; i < savedReversedTagBackStackState.Length; i++) + { + _tagBackStack.Push(savedReversedTagBackStackState[i]); + } + } + } + private Dictionary CreateFragmentTypesDictionary(Bundle outState) { IMvxSavedStateConverter savedStateConverter; @@ -157,7 +175,19 @@ protected override void OnSaveInstanceState(Bundle outState) { base.OnSaveInstanceState(outState); IMvxJsonConverter ser; - if (FragmentCacheConfiguration.HasAnyFragmentsRegisteredToCache && Mvx.TryResolve(out ser)) + if (!Mvx.TryResolve(out ser)) + { + return; + } + + if (_tagBackStack.Any()) + { + var reversedTagBackStack = _tagBackStack.Reverse().ToArray(); + var json = ser.SerializeObject(reversedTagBackStack); + outState.PutString(SavedTagBackStack, json); + } + + if (FragmentCacheConfiguration.HasAnyFragmentsRegisteredToCache) { FragmentCacheConfiguration.SaveFragmentCacheConfigurationState(outState, ser); @@ -208,9 +238,10 @@ protected virtual void ShowFragment(string tag, int contentId, Bundle bundle, bo //If we already have a previously created fragment, we only need to send the new parameters if (fragInfo.CachedFragment != null && fragmentReplaceMode == FragmentReplaceMode.ReplaceFragment) { - (fragInfo.CachedFragment as Fragment).Arguments.Clear(); - (fragInfo.CachedFragment as Fragment).Arguments.PutAll(bundle); - } + var fragment = fragInfo.CachedFragment.ToFragment(); + fragment.Arguments.Clear(); + fragment.Arguments.PutAll(bundle); + } else { //Otherwise, create one and cache it @@ -222,6 +253,8 @@ protected virtual void ShowFragment(string tag, int contentId, Bundle bundle, bo currentFragment = fragInfo.CachedFragment as Fragment; ft.Replace(fragInfo.ContentId, fragInfo.CachedFragment as Fragment, fragInfo.Tag); + _tagBackStack.Push(fragInfo.Tag); + //if replacing ViewModel then clear the cache after the fragment //has been added to the transaction so that the Tag property is not null //and the UniqueImmutableCacheTag property (if not overridden) has the correct value @@ -277,6 +310,9 @@ public override void OnBackPressed() { if (SupportFragmentManager.BackStackEntryCount >= 1) { + var backStackEntry = SupportFragmentManager.GetBackStackEntryAt(SupportFragmentManager.BackStackEntryCount - 1); + RemoveFromTagBackStack(backStackEntry.Name); + SupportFragmentManager.PopBackStackImmediate(); if (FragmentCacheConfiguration.EnableOnFragmentPoppedCallback) @@ -289,6 +325,7 @@ public override void OnBackPressed() return; } + _tagBackStack.Clear(); base.OnBackPressed(); } @@ -311,12 +348,12 @@ protected virtual IEnumerable GetCurrentCacheableFragments() protected virtual IMvxCachedFragmentInfo GetLastFragmentInfo() { - var currentCacheableFragments = GetCurrentCacheableFragments().ToList(); - if (!currentCacheableFragments.Any()) + if (!_tagBackStack.Any()) + { throw new InvalidOperationException("Cannot retrieve last fragment as FragmentManager is empty."); + } - var lastFragment = currentCacheableFragments.Last(); - var tagFragment = GetTagFromFragment(lastFragment); + var tagFragment = _tagBackStack.Peek(); return GetFragmentInfoByTag(tagFragment); } @@ -351,7 +388,9 @@ protected override void OnCreate (Bundle bundle) return; } - FragmentCacheConfiguration.RestoreCacheConfiguration(bundle, serializer); + RestoreTagBackStackFromBundle(serializer, bundle); + + FragmentCacheConfiguration.RestoreCacheConfiguration(bundle, serializer); // Gabriel has blown his trumpet. Ressurect Fragments from the dead. RestoreFragmentsCache(); @@ -389,6 +428,7 @@ protected virtual void CloseFragment(string tag, int contentId) var frag = SupportFragmentManager.FindFragmentById(contentId); if (frag == null) return; + RemoveFromTagBackStack(frag.Tag); SupportFragmentManager.PopBackStackImmediate(tag, 1); } @@ -456,6 +496,7 @@ public virtual bool Close(IMvxViewModel viewModel) { if (SupportFragmentManager.BackStackEntryCount == 0) { + _tagBackStack.Clear(); base.OnBackPressed(); return true; } @@ -471,6 +512,24 @@ public virtual bool Close(IMvxViewModel viewModel) CloseFragment(frag.Tag, frag.ContentId); return true; } + + private void RemoveFromTagBackStack(string tag) + { + // reverse to more logical order + var tagBackStack = _tagBackStack.Reverse().ToList(); + + // remove tag + var firstFoundTagIndex = tagBackStack.FindIndex(backStackTag => backStackTag == tag); + if (firstFoundTagIndex > 0) + tagBackStack.RemoveAt(firstFoundTagIndex); + + // hard reset and reinitialize _tagBackStack + _tagBackStack.Clear(); + for (var i = 0; i < tagBackStack.Count; i++) + { + _tagBackStack.Push(tagBackStack[i]); + } + } } public abstract class MvxCachingFragmentCompatActivity