Skip to content

Instantly share code, notes, and snippets.

@dbeattie71
Last active December 5, 2015 04:11
Show Gist options
  • Select an option

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

Select an option

Save dbeattie71/694407cabcd14e104049 to your computer and use it in GitHub Desktop.
FragmentStatePager ports
//From here:
//https://github.com/adamsp/FragmentStatePagerIssueExample/blob/master/app/src/main/java/com/example/fragmentstatepagerissueexample/app/FixedFragmentStatePagerAdapter.java
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 MvvmCross.Droid.Support.V4
{
//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 readonly FragmentManager _fragmentManager;
private readonly List<Fragment> _fragments = new List<Fragment>();
private Fragment _currentPrimaryItem;
private FragmentTransaction _curTransaction;
private List<string> _savedFragmentTags = new List<string>();
private List<IParcelable> _savedState = new List<IParcelable>();
protected FixedFragmentStatePagerAdapter(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected FixedFragmentStatePagerAdapter(FragmentManager fragmentManager)
{
_fragmentManager = fragmentManager;
}
public abstract Fragment GetItem(int position);
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 fragment = GetItem(position);
var fragmentTag = GetTag(position);
#if DEBUG
Mvx.Trace("Adding item #" + position + ": f=" + fragment + " t=" + fragmentTag);
#endif
if (_savedState.Count > position)
{
var savedTag = _savedFragmentTags.ElementAtOrDefault(position);
if (string.Equals(fragmentTag, savedTag))
{
var fss = _savedState.ElementAtOrDefault(position);
if (fss != null)
fragment.SetInitialSavedState((Fragment.SavedState) fss);
}
}
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)
{
foreach (var t in fss)
_savedState.Add((Fragment.SavedState) t);
}
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 List<IParcelable>();
_savedState = fss;
state.PutParcelableArray("states", fss.ToArray());
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;
}
}
}
//From here:
//https://github.com/adamsp/FragmentStatePagerIssueExample/blob/master/app/src/main/java/com/example/fragmentstatepagerissueexample/app/FixedFragmentStatePagerAdapter.java
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 bob = fss.ElementAt(i);
var jim = bob.JavaCast<Fragment.SavedState>();
_savedState.Add(jim);
}
}
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;
}
}
}
//From here:
//https://gist.github.com/ypresto/8c13cb88a0973d071a64
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.Support.V4.App;
using Android.Support.V4.View;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.V4.View;
using Android.Views;
using Android.Widget;
using Java.Lang;
using Exception = System.Exception;
using Object = Java.Lang.Object;
using String = System.String;
namespace EnjinMobile.Droid.Framework
{
public abstract class FragmentItemIdStatePagerAdapter : PagerAdapter
{
private static bool DEBUG = false;
private static String TAG = "FragmentStatePagerAdapter";
private Fragment mCurrentPrimaryItem;
private FragmentTransaction mCurTransaction;
private readonly FragmentManager mFragmentManager;
private readonly List<Fragment> mFragments = new List<Fragment>();
private Dictionary<Fragment, int> mFragmentToItemIdMap = new Dictionary<Fragment, int>();
private Dictionary<int, Fragment> mItemIdToFragmentMap = new Dictionary<int, Fragment>();
private HashSet<Fragment> mUnusedRestoredFragments = new HashSet<Fragment>();
private List<Fragment.SavedState> mSavedState = new List<Fragment.SavedState>();
protected FragmentItemIdStatePagerAdapter(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected FragmentItemIdStatePagerAdapter(FragmentManager fm)
{
mFragmentManager = fm;
}
public abstract Fragment GetItem(int position);
protected virtual int GetItemId(int position)
{
return position;
}
public override bool IsViewFromObject(View view, Object objectValue)
{
return ((Fragment)objectValue).View == view;
}
public override Object InstantiateItem(ViewGroup container, int position)
{
var itemId = GetItemId(position);
Fragment f;
var hasFragment = mItemIdToFragmentMap.TryGetValue(itemId, out f);
if (hasFragment)
{
// if (DEBUG) Log.v(TAG, "Returning cached fragment: #" + itemId + ": f=" + f);
mUnusedRestoredFragments.Remove(f);
return f;
}
if (mCurTransaction == null)
{
mCurTransaction = mFragmentManager.BeginTransaction();
}
Fragment fragment = GetItem(position);
//if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mFragmentToItemIdMap.Add(fragment, itemId);
mItemIdToFragmentMap.Add(itemId, fragment);
Fragment.SavedState fss = mSavedState.ElementAtOrDefault(itemId);
if (fss != null)
{
fragment.SetInitialSavedState(fss);
}
fragment.SetMenuVisibility(false);
fragment.UserVisibleHint = false;
mCurTransaction.Add(container.Id, fragment);
return fragment;
}
public override void DestroyItem(ViewGroup container, int position, Object objectValue)
{
var fragment = (Fragment) objectValue;
DestroyFragment(fragment);
}
private void DestroyFragment(Fragment fragment)
{
if (mCurTransaction == null)
{
mCurTransaction = mFragmentManager.BeginTransaction();
}
int itemId;
var hasItem = mFragmentToItemIdMap.TryGetValue(fragment, out itemId);
if (hasItem)
{
mFragmentToItemIdMap.Remove(fragment);
mItemIdToFragmentMap.Remove(itemId);
}
else
{
itemId = -1;
}
//if (DEBUG)
// Log.v(TAG, "Removing item #" + itemId + ": f=" + fragment
// + " v=" + fragment.getView());
// XXX: Workaround for NullPointerException, but I don't know why ViewPager passes fragment
// which is not owned by pager adapter (i.e. mFragmentToItemIdMap does not contain it).
//if (itemId != null)
try
{
mSavedState[itemId] = mFragmentManager.SaveFragmentInstanceState(fragment);
}
catch (Exception exception)
{
}
mCurTransaction.Remove(fragment);
}
public override void SetPrimaryItem(ViewGroup container, int position, Object objectValue)
{
try
{
var fragment = (Fragment)objectValue;
if (fragment != mCurrentPrimaryItem)
{
if (mCurrentPrimaryItem != null)
{
mCurrentPrimaryItem.SetMenuVisibility(false);
mCurrentPrimaryItem.UserVisibleHint = false;
}
if (fragment != null)
{
fragment.SetMenuVisibility(true);
fragment.UserVisibleHint = true;
}
mCurrentPrimaryItem = fragment;
}
}
catch (Exception exception)
{
}
}
public override void FinishUpdate(ViewGroup container)
{
if (mUnusedRestoredFragments.Count ==0)
{
// Remove fragments which are restored but unused after first finishUpdate.
foreach (var mUnusedRestoredFragment in mUnusedRestoredFragments)
{
DestroyFragment(mUnusedRestoredFragment);
}
mUnusedRestoredFragments.Clear();
}
if (mCurTransaction != null)
{
mCurTransaction.CommitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.ExecutePendingTransactions();
}
}
private static string KEY_FRAGMENT = "fragment";
public override IParcelable SaveState()
{
Bundle state = null;
if (mSavedState.Any())
{
state = new Bundle();
var itemIdsForState = new int[mSavedState.Count];
List<Android.Support.V4.App.Fragment.SavedState> fss = new List<Android.Support.V4.App.Fragment.SavedState>(mSavedState.Count);
for (int i = 0; i < mSavedState.Count; i++)
{
itemIdsForState[i] = mSavedState.IndexOf(mSavedState.ElementAt(i));
fss[i] = mSavedState.ElementAt(i);
}
state.PutIntArray("itemIdsForState", itemIdsForState.ToArray());
state.PutParcelableArray("states", fss.ToArray());
}
foreach (var fragmentToIdEntry in mFragmentToItemIdMap)
{
Fragment f = fragmentToIdEntry.Key;
if (f != null && f.IsAdded)
{
if (state == null)
{
state = new Bundle();
}
var itemId = fragmentToIdEntry.Value;
var bundleKey = KEY_FRAGMENT + itemId;
mFragmentManager.PutFragment(state, bundleKey, f);
}
}
return state;
}
public override void RestoreState(IParcelable state, ClassLoader loader)
{
if (state != null)
{
Bundle bundle = (Bundle)state;
bundle.SetClassLoader(loader);
var itemIdsForState = bundle.GetIntArray("itemIdsForState");
var fss = bundle.GetParcelableArray("states");
mFragmentToItemIdMap.Clear();
mItemIdToFragmentMap.Clear();
mUnusedRestoredFragments.Clear();
mSavedState.Clear();
if (fss != null)
{
for (int i = 0; i < fss.Length; i++)
{
mSavedState[itemIdsForState[i]] = (Fragment.SavedState)fss[i];
}
}
var keys = bundle.KeySet();
foreach (var key in keys)
{
if (key.StartsWith(KEY_FRAGMENT))
{
var itemId = Integer.ParseInt(KEY_FRAGMENT.Substring(KEY_FRAGMENT.Length));
Fragment f = mFragmentManager.GetFragment(bundle, key);
if (f != null)
{
f.SetMenuVisibility(false);
mFragmentToItemIdMap.Add(f, itemId);
mItemIdToFragmentMap.Add(itemId, f);
}
else
{
// Log.w(TAG, "Bad fragment at key " + key);
}
}
}
foreach (var bob in mFragmentToItemIdMap)
{
mUnusedRestoredFragments.Add(bob.Key);
}
}
}
public override void StartUpdate(View container)
{
}
}
}
//Port of Support lib FragmentStatePagerAdapter
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 Java.Lang;
using Object = Java.Lang.Object;
using String = System.String;
namespace EnjinMobile.Droid.Framework
{
public abstract class FragmentStatePagerAdapterEx : PagerAdapter
{
private static bool DEBUG = false;
private static String TAG = "FragmentStatePagerAdapter";
private Fragment mCurrentPrimaryItem;
private FragmentTransaction mCurTransaction;
private readonly FragmentManager mFragmentManager;
private readonly List<Fragment> mFragments = new List<Fragment>();
private List<Fragment.SavedState> mSavedState = new List<Fragment.SavedState>();
protected FragmentStatePagerAdapterEx(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected FragmentStatePagerAdapterEx(FragmentManager fm)
{
mFragmentManager = fm;
}
public abstract Fragment GetItem(int position);
public override void DestroyItem(ViewGroup container, int position, Object objectValue)
{
var fragment = (Fragment) objectValue;
if (mCurTransaction == null)
mCurTransaction = mFragmentManager.BeginTransaction();
//if (DEBUG)
// Log.v(TAG, "Removing item #" + position + ": f=" + object
// + " v=" + ((Fragment)object).getView());
while (mSavedState.Count() <= position)
mSavedState.Add(null);
mSavedState[position] = mFragmentManager.SaveFragmentInstanceState(fragment);
mFragments[position] = null;
mCurTransaction.Remove(fragment);
}
public override void FinishUpdate(ViewGroup container)
{
if (mCurTransaction != null)
{
mCurTransaction.CommitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.ExecutePendingTransactions();
}
}
public override Object InstantiateItem(ViewGroup container, int position)
{
if (mFragments.Count > position)
{
var f = mFragments.ElementAtOrDefault(position);
if (f != null)
return f;
}
if (mCurTransaction == null)
mCurTransaction = mFragmentManager.BeginTransaction();
Fragment fragment = GetItem(position);
//if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
if (mSavedState.Count > position)
{
var fss = mSavedState.ElementAtOrDefault(position);
if (fss != null)
fragment.SetInitialSavedState(fss);
}
while (mFragments.Count() <= position)
mFragments.Add(null);
fragment.SetMenuVisibility(false);
fragment.UserVisibleHint = false;
mFragments[position] = fragment;
mCurTransaction.Add(container.Id, fragment);
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)
{
var bundle = (Bundle) state;
bundle.SetClassLoader(loader);
var fss = bundle.GetParcelableArray("states");
mSavedState.Clear();
mFragments.Clear();
if (fss != null)
{
for (var i = 0; i < fss.Length; i++)
mSavedState.Add((Fragment.SavedState) fss[i]);
}
var keys = bundle.KeySet();
foreach (var key in keys)
{
if (key.StartsWith("f"))
{
var index = Integer.ParseInt(key.Substring(1));
var f = mFragmentManager.GetFragment(bundle, key);
if (f != null)
{
while (mFragments.Count() <= index)
mFragments.Add(null);
f.SetMenuVisibility(false);
mFragments[index] = f;
}
}
}
}
}
public override IParcelable SaveState()
{
Bundle state = null;
if (mSavedState.Count() > 0)
{
state = new Bundle();
var fss = new List<Fragment.SavedState>(mSavedState.Count());
mSavedState = fss;
state.PutParcelableArray("states", fss.ToArray());
}
for (var i = 0; i < mFragments.Count(); i++)
{
var f = mFragments.ElementAtOrDefault(i);
if (f != null && f.IsAdded)
{
if (state == null)
state = new Bundle();
var key = "f" + i;
mFragmentManager.PutFragment(state, key, f);
}
}
return state;
}
public override void SetPrimaryItem(ViewGroup container, int position, Object objectValue)
{
var fragment = (Fragment) objectValue;
if (fragment != mCurrentPrimaryItem)
{
if (mCurrentPrimaryItem != null)
{
mCurrentPrimaryItem.SetMenuVisibility(false);
mCurrentPrimaryItem.UserVisibleHint = false;
}
if (fragment != null)
{
fragment.SetMenuVisibility(true);
fragment.UserVisibleHint = true;
}
mCurrentPrimaryItem = fragment;
}
}
public override void StartUpdate(View container)
{
}
}
}
//From Unified Mail:
//https://android.googlesource.com/platform/packages/apps/UnifiedEmail/+/master/src/com/android/mail/utils/FragmentStatePagerAdapter2.java
using System;
using System.Collections.Generic;
using System.Linq;
using Android.OS;
using Android.Runtime;
using Android.Support.V4.App;
//using Android.Support.V13.App;
using Android.Support.V4.View;
using Android.Util;
using Android.Views;
using Java.Lang;
using Object = Java.Lang.Object;
using String = System.String;
namespace EnjinMobile.Droid.Framework
{
public abstract class FragmentStatePagerAdapter2 : PagerAdapter
{
private FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private List<Fragment.SavedState> mSavedState = new List<Fragment.SavedState>();
private SparseArray<Fragment> mFragments = new SparseArray<Fragment>();
private Fragment mCurrentPrimaryItem = null;
private bool mEnableSavedStates;
protected FragmentStatePagerAdapter2(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected FragmentStatePagerAdapter2(FragmentManager fm, bool enableSavedStates)
{
mFragmentManager = fm;
mEnableSavedStates = enableSavedStates;
mEnableSavedStates = true;
}
public abstract Fragment GetItem(int position);
public override Object InstantiateItem(ViewGroup container, int position)
{
var existing = mFragments.Get(position);
if (existing != null)
{
return existing;
}
if (mCurTransaction == null)
{
mCurTransaction = mFragmentManager.BeginTransaction();
}
var fragment = GetItem(position);
if (mEnableSavedStates && mSavedState.Count > position)
{
var fss = mSavedState.ElementAtOrDefault(position);
if (fss != null)
{
fragment.SetInitialSavedState(fss);
}
}
if (fragment != mCurrentPrimaryItem)
{
SetItemVisible(fragment, false);
}
mFragments.Put(position, fragment);
mCurTransaction.Add(container.Id, fragment);
return fragment;
}
public override void DestroyItem(ViewGroup container, int position, Object objectValue)
{
Fragment fragment = (Fragment)objectValue;
if (mCurTransaction == null)
{
mCurTransaction = mFragmentManager.BeginTransaction();
}
//if (DEBUG)
// LogUtils.v(TAG, "Removing item #" + position + ": f=" + object
// + " v=" + ((Fragment)object).getView());
if (mEnableSavedStates)
{
while (mSavedState.Count <= position)
{
mSavedState.Add(null);
}
mSavedState[position]= mFragmentManager.SaveFragmentInstanceState(fragment);
}
mFragments.RemoveAt(position);
mCurTransaction.Remove(fragment);
}
public void setPrimaryItem(ViewGroup container, int position, Object @object)
{
Fragment fragment = (Fragment)@object;
if (fragment != mCurrentPrimaryItem)
{
if (mCurrentPrimaryItem != null)
{
SetItemVisible(mCurrentPrimaryItem, false);
}
if (fragment != null)
{
SetItemVisible(fragment, true);
}
mCurrentPrimaryItem = fragment;
}
}
public override void FinishUpdate(ViewGroup container)
{
if (mCurTransaction != null)
{
mCurTransaction.CommitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.ExecutePendingTransactions();
}
}
public override bool IsViewFromObject(View view, Object objectValue)
{
return ((Fragment)objectValue).View == view;
}
public override IParcelable SaveState()
{
Bundle state = null;
if (mEnableSavedStates && mSavedState.Count > 0)
{
state = new Bundle();
var fss = new List<Fragment.SavedState>(mSavedState.Count);
mSavedState = fss;
state.PutParcelableArray("states", fss.ToArray());
}
for (int i = 0; i < mFragments.Size(); i++)
{
int pos = mFragments.KeyAt(i);
Fragment f = mFragments.ValueAt(i);
if (state == null)
{
state = new Bundle();
}
String key = "f" + pos;
mFragmentManager.PutFragment(state, key, f);
}
return state;
}
public override void RestoreState(IParcelable state, ClassLoader loader)
{
if (state != null)
{
var bundle = (Bundle)state;
bundle.SetClassLoader(loader);
mFragments.Clear();
if (mEnableSavedStates)
{
var fss = bundle.GetParcelableArray("states");
mSavedState.Clear();
if (fss != null)
{
for (int i = 0; i < fss.Length; i++)
{
mSavedState.Add((Fragment.SavedState)fss[i]);
}
}
}
var keys = bundle.KeySet();
foreach (var key in keys)
{
var index = Integer.ParseInt(key.Substring(1));
var f = mFragmentManager.GetFragment(bundle, key);
if (f != null)
{
SetItemVisible(f, false);
mFragments.Put(index, f);
}
else
{
//LogUtils.w(TAG, "Bad fragment at key " + key);
}
}
}
}
public override void NotifyDataSetChanged()
{
// update positions in mFragments
var newFragments = new SparseArray<Fragment>(mFragments.Size());
for (int i = 0; i < mFragments.Size(); i++)
{
int oldPos = mFragments.KeyAt(i);
Fragment f = mFragments.ValueAt(i);
int newPos = GetItemPosition(f);
if (newPos != PositionNone)
{
int pos = (newPos >= 0) ? newPos : oldPos;
newFragments.Put(pos, f);
}
}
mFragments = newFragments;
base.NotifyDataSetChanged();
}
public void SetItemVisible(Fragment item, bool visible)
{
item.SetMenuVisibility(true);
item.UserVisibleHint = true;
//FragmentCompat.SetMenuVisibility(item, visible);
//FragmentCompat.SetUserVisibleHint(item, visible);
}
}
}
Fragments / ViewPager and State save / restore
Source code:
ViewPager.java source
http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/support/v4/view/ViewPager.java/?v=source
FragmentStatePagerAdapter.java source
http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/support/v4/app/FragmentStatePagerAdapter.java/?v=source
(Used in gmail but fixes not propagated to Support libs ?!?)
FragmentStatePagerAdapter2.java source
https://android.googlesource.com/platform/packages/apps/UnifiedEmail/+/master/src/com/android/mail/utils/FragmentStatePagerAdapter2.java
FragmentManager.java source
http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/support/v4/app/FragmentManager.java/?v=source
SmartFragmentStatePagerAdapter.java source
https://gist.github.com/nesquena/c715c9b22fb873b1d259
Google Bugs:
Reordering fragments in FragmentStatePagerAdapter by overriding getItemPosition() does not work
https://code.google.com/p/android/issues/detail?id=37990
Child FragmentManager retained after detaching parent Fragment
https://code.google.com/p/android/issues/detail?id=42601
ViewPager NullPointerException when onPause is called from activity and ViewPager has no adapter set
https://code.google.com/p/android/issues/detail?id=19917
Compatibility library v.4 ViewPager returns uninitialized item after orientation change
https://code.google.com/p/android/issues/detail?id=19211
NullPonterException encountered in Support Library while using FragmentStatepagerAdapter
https://code.google.com/p/android/issues/detail?id=177192
Articles:
https://www.youtube.com/watch?v=ekN2zvFytZk
G+ thread
https://plus.google.com/u/0/+BillyNgYuHang/posts/a1xwgEEehCs
Probably be the best way (?) to save/restore Android Fragment’s state so far
http://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en
http://trickyandroid.com/saving-android-view-state-correctly/
A Deeper Look of ViewPager and FragmentStatePagerAdaper
http://billynyh.github.io/blog/2014/03/02/fragment-state-pager-adapter/
A bug in (and a fix for) the way FragmentStatePagerAdapter handles fragment restoration
http://speakman.net.nz/blog/2014/02/20/a-bug-in-and-a-fix-for-the-way-fragmentstatepageradapter-handles-fragment-restoration/
Stackoverflow’s:
ViewPager inside fragment, how to retain state
http://stackoverflow.com/questions/26993853/viewpager-inside-fragment-how-to-retain-state
ViewPager and fragments -- what’s the right way to store fragment’s state?
http://stackoverflow.com/questions/7951730/viewpager-and-fragments-whats-the-right-way-to-store-fragments-state?rq=1
Once for all, how to correctly save instance state of Fragments in back stack?
http://stackoverflow.com/questions/15313598/once-for-all-how-to-correctly-save-instance-state-of-fragments-in-back-stack
IllegalStateException: <MyFragment> is not currently in the FragmentManager
http://stackoverflow.com/questions/11937622/illegalstateexception-myfragment-is-not-currently-in-the-fragmentmanager
NullPointerException in FragmentManager
http://stackoverflow.com/questions/10456077/nullpointerexception-in-fragmentmanager
http://stackoverflow.com/questions/13393693/android-fragmentmanager-backstackrecord-run-throwing-nullpointerexception
NPE in Fragment.setUserVisibleHint() when using ViewPager
http://stackoverflow.com/questions/23162343/npe-in-fragment-setuservisiblehint-when-using-viewpager
Very simple code, but got error “Activity has been destroyed” when use Fragment
http://stackoverflow.com/questions/9132027/very-simple-code-but-got-error-activity-has-been-destroyed-when-use-fragment
Starting Activity from Fragment causes NullPointerException
http://stackoverflow.com/questions/8748064/starting-activity-from-fragment-causes-nullpointerexception
FragmentStatePagerAdapter with ChildFragmentManager - FragmentManagerImpl.getFragment results in NullPointerException
http://stackoverflow.com/questions/18642890/fragmentstatepageradapter-with-childfragmentmanager-fragmentmanagerimpl-getfra
Add / Delete pages to ViewPager dynamically
http://stackoverflow.com/questions/8060904/add-delete-pages-to-viewpager-dynamically
ViewPager showing duplicate views
http://stackoverflow.com/questions/24241603/viewpager-showing-duplicate-views
(don’t have this problem but same scenario)
How to prevent custom views from losing state across screen orientation changes
http://stackoverflow.com/questions/3542333/how-to-prevent-custom-views-from-losing-state-across-screen-orientation-changes
(doing this correctly)
How to add a Fragment inside a ViewPager using Nested Fragment (Android 4.2)
http://stackoverflow.com/questions/13379194/how-to-add-a-fragment-inside-a-viewpager-using-nested-fragment-android-4-2
Github:
ViewPagerChildFragments Ex
https://github.com/rukmaldias/ViewPagerChildFragments/blob/master/src/com/vp/childfragments/PageFragmentListener.java
FragmentStatePager.java port to C# for debugging
https://gist.github.com/dbeattie71/694407cabcd14e104049
SortableFragmentStatePagerAdapter.java
https://github.com/unverbraucht/sortable-fragment-pager/blob/master/src/main/java/com/kevinread/sortablefragmentpager/SortableFragmentStatePagerAdapter.java
FragmentStatePagerIssueExample
https://github.com/adamsp/FragmentStatePagerIssueExample/blob/master/app/src/main/java/com/example/fragmentstatepagerissueexample/app/FixedFragmentStatePagerAdapter.java
FragmentItemIdStatePagerAdapter.java
https://gist.github.com/ypresto/8c13cb88a0973d071a64
Xamarin forums:
Can’t access ChildFragmentManager by reflection
https://forums.xamarin.com/discussion/16888/cant-access-childfragmentmanager-by-reflection
try
{
//https://forums.xamarin.com/discussion/16888/cant-access-childfragmentmanager-by-reflection
//var classRefProp = typeof(Fragment).GetProperty("class_ref", BindingFlags.NonPublic | BindingFlags.Static);
//IntPtr classRef = (IntPtr)classRefProp.GetValue(x);
//var field = JNIEnv.GetFieldID(classRef, "mChildFragmentManager", "Landroid/support/v4/app/FragmentManagerImpl;");
//JNIEnv.SetField(base.Handle, field, IntPtr.Zero);
}
catch (Exception exception)
{
}
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