Skip to content

Instantly share code, notes, and snippets.

@dbeattie71
Last active December 23, 2015 19:46
Show Gist options
  • Select an option

  • Save dbeattie71/bbf27cf2612f34fb8031 to your computer and use it in GitHub Desktop.

Select an option

Save dbeattie71/bbf27cf2612f34fb8031 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using Android.OS;
using Android.Runtime;
using Android.Support.V4.App;
using Android.Support.V4.View;
using Android.Views;
using Cirrious.CrossCore;
using Java.Lang;
using Object = Java.Lang.Object;
namespace EnjinMobile.Common
{
//http://speakman.net.nz/blog/2014/02/20/a-bug-in-and-a-fix-for-the-way-fragmentstatepageradapter-handles-fragment-restoration/
//https://github.com/adamsp/FragmentStatePagerIssueExample/blob/master/app/src/main/java/com/example/fragmentstatepagerissueexample/app/FixedFragmentStatePagerAdapter.java
public abstract class FixedFragmentStatePagerAdapter : PagerAdapter
{
private Fragment _currentPrimaryItem;
private FragmentTransaction _curTransaction;
private readonly FragmentManager _fragmentManager;
private readonly List<Fragment> _fragments = new List<Fragment>();
private List<string> _savedFragmentTags = new List<string>();
private readonly List<Fragment.SavedState> _savedState = new List<Fragment.SavedState>();
protected FixedFragmentStatePagerAdapter(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected FixedFragmentStatePagerAdapter(FragmentManager fragmentManager)
{
_fragmentManager = fragmentManager;
}
public abstract Fragment GetItem(int position, Fragment.SavedState fragmentSavedState = null);
public override void DestroyItem(ViewGroup container, int position, Object objectValue)
{
var fragment = (Fragment) objectValue;
if (_curTransaction == null)
_curTransaction = _fragmentManager.BeginTransaction();
#if DEBUG
Mvx.Trace("Removing item #" + position + ": f=" + objectValue + " v=" + ((Fragment) objectValue).View +
" t=" + fragment.Tag);
#endif
while (_savedState.Count <= position)
{
_savedState.Add(null);
_savedFragmentTags.Add(null);
}
_savedState[position] = _fragmentManager.SaveFragmentInstanceState(fragment);
_savedFragmentTags[position] = fragment.Tag;
_fragments[position] = null;
_curTransaction.Remove(fragment);
}
public override void FinishUpdate(ViewGroup container)
{
if (_curTransaction == null)
return;
_curTransaction.CommitAllowingStateLoss();
_curTransaction = null;
_fragmentManager.ExecutePendingTransactions();
}
public override Object InstantiateItem(ViewGroup container, int position)
{
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (_fragments.Count > position)
{
var existingFragment = _fragments.ElementAtOrDefault(position);
if (existingFragment != null)
return existingFragment;
}
if (_curTransaction == null)
_curTransaction = _fragmentManager.BeginTransaction();
var fragmentTag = GetTag(position);
Fragment.SavedState fss = null;
if (_savedState.Count > position)
{
var savedTag = _savedFragmentTags.ElementAtOrDefault(position);
if (string.Equals(fragmentTag, savedTag))
fss = _savedState.ElementAtOrDefault(position);
}
var fragment = GetItem(position, fss);
if (fss != null)
fragment.SetInitialSavedState(fss);
#if DEBUG
Mvx.Trace("Adding item #" + position + ": f=" + fragment + " t=" + fragmentTag);
#endif
while (_fragments.Count <= position)
_fragments.Add(null);
fragment.SetMenuVisibility(false);
fragment.UserVisibleHint = false;
_fragments[position] = fragment;
_curTransaction.Add(container.Id, fragment, fragmentTag);
return fragment;
}
public override bool IsViewFromObject(View view, Object objectValue)
{
return ((Fragment) objectValue).View == view;
}
public override void RestoreState(IParcelable state, ClassLoader loader)
{
if (state == null)
return;
var bundle = (Bundle) state;
bundle.SetClassLoader(loader);
var fss = bundle.GetParcelableArray("states");
_savedState.Clear();
_fragments.Clear();
var tags = bundle.GetStringArrayList("tags");
if (tags != null)
_savedFragmentTags = tags.ToList();
else
_savedFragmentTags.Clear();
if (fss != null)
{
for (var i = 0; i < fss.Length; i++)
{
var parcelable = fss.ElementAt(i);
var savedState = parcelable.JavaCast<Fragment.SavedState>();
_savedState.Add(savedState);
}
}
var keys = bundle.KeySet();
foreach (var key in keys)
{
if (!key.StartsWith("f"))
continue;
var index = Integer.ParseInt(key.Substring(1));
var f = _fragmentManager.GetFragment(bundle, key);
if (f != null)
{
while (_fragments.Count() <= index)
_fragments.Add(null);
f.SetMenuVisibility(false);
_fragments[index] = f;
}
}
}
public override IParcelable SaveState()
{
Bundle state = null;
if (_savedState.Any())
{
state = new Bundle();
var fss = new IParcelable[_savedState.Count];
for (var i = 0; i < _savedState.Count; i++)
fss[i] = _savedState.ElementAt(i);
state.PutParcelableArray("states", fss);
state.PutStringArrayList("tags", _savedFragmentTags);
}
for (var i = 0; i < _fragments.Count; i++)
{
var f = _fragments.ElementAtOrDefault(i);
if (f == null)
continue;
if (state == null)
state = new Bundle();
var key = "f" + i;
_fragmentManager.PutFragment(state, key, f);
}
return state;
}
public override void SetPrimaryItem(ViewGroup container, int position, Object objectValue)
{
var fragment = (Fragment) objectValue;
if (fragment == _currentPrimaryItem)
return;
if (_currentPrimaryItem != null)
{
_currentPrimaryItem.SetMenuVisibility(false);
_currentPrimaryItem.UserVisibleHint = false;
}
if (fragment != null)
{
fragment.SetMenuVisibility(true);
fragment.UserVisibleHint = true;
}
_currentPrimaryItem = fragment;
}
public override void StartUpdate(View container)
{
}
protected virtual string GetTag(int position)
{
return null;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using Android.Content;
using Android.Runtime;
using Android.Support.V4.App;
using Cirrious.CrossCore;
using Cirrious.MvvmCross.Platform;
using Cirrious.MvvmCross.ViewModels;
using EnjinMobile.Common;
using Java.Lang;
using MvvmCross.Droid.Support.V7.Fragging;
using MvvmCross.Droid.Support.V7.Fragging.Fragments;
using String = Java.Lang.String;
namespace EnjinMobile.Droid.Framework
{
public class MvxFixedFragmentStatePagerAdapter
: FixedFragmentStatePagerAdapter
{
private readonly Context _context;
protected MvxFixedFragmentStatePagerAdapter(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public MvxFixedFragmentStatePagerAdapter(Context context, FragmentManager fragmentManager,
IEnumerable<FragmentInfo> fragments)
: base(fragmentManager)
{
_context = context;
Fragments = fragments;
}
public override int Count => Fragments.Count();
public IEnumerable<FragmentInfo> Fragments { get; }
protected static string FragmentJavaName(Type fragmentType)
{
var namespaceText = fragmentType.Namespace ?? "";
if (namespaceText.Length > 0)
namespaceText = namespaceText.ToLowerInvariant() + ".";
return namespaceText + fragmentType.Name;
}
public override Fragment GetItem(int position, Fragment.SavedState fragmentSavedState = null)
{
var fragInfo = Fragments.ElementAt(position);
var fragment = Fragment.Instantiate(_context, FragmentJavaName(fragInfo.FragmentType));
var mvxFragment = fragment as MvxFragment;
if (mvxFragment == null)
return fragment;
if (mvxFragment.GetType().IsCacheableFragmentAttribute() && fragmentSavedState != null)
return fragment;
var viewModel = CreateViewModel(position);
mvxFragment.ViewModel = viewModel;
return fragment;
}
public override ICharSequence GetPageTitleFormatted(int position)
{
return new String(Fragments.ElementAt(position).Title);
}
protected override string GetTag(int position)
{
return Fragments.ElementAt(position).Tag;
}
private IMvxViewModel CreateViewModel(int position)
{
var fragInfo = Fragments.ElementAt(position);
MvxBundle mvxBundle = null;
if (fragInfo.ParameterValuesObject != null)
mvxBundle = new MvxBundle(fragInfo.ParameterValuesObject.ToSimplePropertyDictionary());
var request = new MvxViewModelRequest(fragInfo.ViewModelType, mvxBundle, null, null);
return Mvx.Resolve<IMvxViewModelLoader>().LoadViewModel(request, null);
}
//Do call restore state
//public override void RestoreState(IParcelable state, ClassLoader loader)
//{
// //Don't call restore to prevent crash on rotation
// //base.RestoreState (state, loader);
//}
public class FragmentInfo
{
public FragmentInfo(string title, Type fragmentType, Type viewModelType, object parameterValuesObject = null)
: this(title, null, fragmentType, viewModelType, parameterValuesObject)
{
}
public FragmentInfo(string title, string tag, Type fragmentType, Type viewModelType,
object parameterValuesObject = null)
{
Title = title;
Tag = tag ?? title;
FragmentType = fragmentType;
ViewModelType = viewModelType;
ParameterValuesObject = parameterValuesObject;
}
public Type FragmentType { get; }
public object ParameterValuesObject { get; }
public string Tag { get; }
public string Title { get; }
public Type ViewModelType { get; }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment