Last active
July 8, 2016 09:24
-
-
Save Heroes84/104c7e03cee3e8761ebc6c216dff73af to your computer and use it in GitHub Desktop.
Fragment backstack manager to access fragment all time in stack
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.chimeraprime.prizecam.tools.fragment; | |
import android.os.Bundle; | |
import android.support.v4.BuildConfig; | |
import android.support.v4.app.Fragment; | |
import android.support.v4.app.FragmentActivity; | |
import android.support.v4.app.FragmentManager; | |
import android.support.v4.app.FragmentTransaction; | |
import android.util.Log; | |
import java.util.HashSet; | |
import java.util.Stack; | |
public class FragmentsManager { | |
private final static String FRAGMENT_TAG = "it_s_just_a_fragment_tag"; | |
private final static String STATE_ARRAY = "backstack_array"; | |
private final static String STATE_ARGS_ARRAY = "args_array"; | |
private final static String STATE_SAVED_ARRAY = "saved_array"; | |
private static final String LOG = FragmentsManager.class.getSimpleName(); | |
/** | |
* If this type of fragment is already started, should it be possible to | |
* start the same type | |
*/ | |
public static Integer FLAG_ALLOW_SAME_FRAGMENT = 1; | |
/** | |
* When fragment is in backstack go back to it, instead creating new | |
* fragment | |
*/ | |
public static Integer FLAG_BACKSTACK_CLEAR_TOP = 2; | |
public static Integer FLAG_HIDE_BACK_ARROW = 3; | |
private Stack<Fragment> mBackStack; | |
private Fragment mCurrentFragment; | |
private boolean mAnimationsEnabled = true; | |
private int animationEnter = android.R.anim.fade_in; | |
private int animationExit = android.R.anim.fade_out; | |
private int animationPopEnter = android.R.anim.fade_in; | |
private int animationPopExit = android.R.anim.fade_out; | |
private int fragmentFrameId; | |
private FragmentChangedListener mFragmentChangedListener; | |
private FragmentActivity activity; | |
/** | |
* Has to be called from Activity onCreate | |
*/ | |
public void onCreate(FragmentActivity a, Bundle state, int fragmentFrameId) { | |
this.fragmentFrameId = fragmentFrameId; | |
this.activity = a; | |
mBackStack = new Stack<Fragment>(); | |
if (state != null) { | |
if (state.containsKey(STATE_ARRAY)) { | |
String[] fragmentsArray = state.getStringArray(STATE_ARRAY); | |
if (BuildConfig.DEBUG) { | |
StringBuilder sb = new StringBuilder(); | |
for (String name : fragmentsArray) { | |
sb.append(name); | |
sb.append(" "); | |
} | |
Log.d(LOG, "Restoring " + sb.toString()); | |
} | |
Bundle[] args = getBundleArray(state, STATE_ARGS_ARRAY); | |
Bundle[] states = getBundleArray(state, STATE_SAVED_ARRAY); | |
mAnimationsEnabled = false; | |
for (int i = 0; i < fragmentsArray.length; i++) { | |
nextFragment(fragmentsArray[i], args[i], true, null); | |
if (mCurrentFragment instanceof TabFragment) { | |
((TabFragment) mCurrentFragment).stateInfo(states[i]); | |
} | |
} | |
mAnimationsEnabled = true; | |
} | |
} | |
} | |
public void onDestroy() { | |
mFragmentChangedListener = null; | |
activity = null; | |
} | |
public void setFragmentChangedListener(FragmentChangedListener l) { | |
mFragmentChangedListener = l; | |
} | |
/** | |
* Has to be called from activity using it | |
* | |
* @return true if handled the request and it shouldn't be handled anywhere | |
* else | |
*/ | |
public boolean onBackPressed() { | |
// first check if fragment wants to handle this back action | |
if (mCurrentFragment != null && (mCurrentFragment instanceof TabFragment)) { | |
if (((TabFragment) mCurrentFragment).onBackPressed()) { | |
return true; | |
} | |
if (((TabFragment) mCurrentFragment).onExit()) { | |
return true; | |
} | |
} | |
// if backstack empty just return unhandled event | |
if (mBackStack.empty()) { | |
return false; | |
} | |
// pop item from backstack and show | |
Fragment b = mBackStack.pop(); | |
showFragmentFromBackStack(b); | |
return true; | |
} | |
private void showFragmentFromBackStack(Fragment b) { | |
FragmentManager fm = activity.getSupportFragmentManager(); | |
FragmentTransaction ft = fm.beginTransaction(); | |
if (mAnimationsEnabled) | |
ft.setCustomAnimations(animationPopEnter, animationPopExit); | |
if (mCurrentFragment != null) { | |
ft.remove(mCurrentFragment); | |
} | |
ft.attach(b); | |
ft.commitAllowingStateLoss(); | |
Fragment old = mCurrentFragment; | |
mCurrentFragment = b; | |
if (mBackStack.empty()) { | |
showBackButton(mCurrentFragment, false); | |
} else { | |
showBackButton(mCurrentFragment, true); | |
} | |
fragmentChanged(old, mCurrentFragment); | |
} | |
public Fragment getCurrentFragment() { | |
return mCurrentFragment; | |
} | |
public Fragment getLastFragment() { | |
if (mBackStack == null || mBackStack.size() == 0) { | |
return null; | |
} else return mBackStack.peek(); | |
} | |
public boolean hasAnyHistory() { | |
if (mBackStack == null || mBackStack.size() == 0) { | |
return false; | |
} | |
return true; | |
} | |
public void nextFragment(Class<? extends Fragment> newFragClass) { | |
nextFragment(newFragClass.getName()); | |
} | |
public void nextFragment(Class<? extends Fragment> newFragClass, Bundle args, boolean backStack) { | |
nextFragment(newFragClass, args, backStack, null); | |
} | |
public void nextFragment(Class<? extends Fragment> newFragClass, Bundle args, boolean backStack, | |
HashSet<Integer> flags) { | |
nextFragment(newFragClass.getName(), args, backStack, flags); | |
} | |
public void nextFragment(String newFragClassName) { | |
nextFragment(newFragClassName, null, false, null); | |
} | |
public void nextFragment(String newFragClassName, Bundle args, boolean backStack, HashSet<Integer> flags) { | |
NextFragmentParams p = new NextFragmentParams(); | |
p.activity = activity; | |
p.backStack = backStack; | |
p.className = newFragClassName; | |
p.fragment = null; | |
p.initArgs = args; | |
if (flags == null) { | |
flags = new HashSet<Integer>(); | |
} | |
nextFragment(p, flags); | |
} | |
public void nextFragment(Fragment fragment) { | |
nextFragment(fragment, null, false, null); | |
} | |
public void nextFragment(Fragment fragment, Bundle args, boolean backStack, HashSet<Integer> flags) { | |
NextFragmentParams p = new NextFragmentParams(); | |
p.activity = activity; | |
p.backStack = backStack; | |
p.className = fragment.getClass().getName(); | |
p.fragment = fragment; | |
p.initArgs = args; | |
if (flags == null) { | |
flags = new HashSet<Integer>(); | |
} | |
nextFragment(p, flags); | |
} | |
private void nextFragment(NextFragmentParams params, HashSet<Integer> flags) { | |
// check allowSameFragment flag | |
if (mCurrentFragment != null && !flags.contains(FLAG_ALLOW_SAME_FRAGMENT) | |
&& mCurrentFragment.getClass().getName().equals(params.className)) { | |
return; | |
} | |
// call onExit in current fragment | |
if (mCurrentFragment != null && mCurrentFragment instanceof TabFragment && ((TabFragment) mCurrentFragment).onExit()) { | |
return; | |
} | |
// check backstackClearTop flag | |
boolean getFromBackstack = false; | |
if (flags.contains(FLAG_BACKSTACK_CLEAR_TOP)) { | |
for (Fragment f : mBackStack) { | |
if (f.getClass().getName().equals(params.className)) { | |
getFromBackstack = true; | |
break; | |
} | |
} | |
} | |
// if backstackClearTop is true and fragment is in backstack clear | |
// backstack to this fragment | |
if (getFromBackstack) { | |
Fragment f; | |
do { | |
f = mBackStack.pop(); | |
} while (!f.getClass().getName().equals(params.className)); | |
} | |
FragmentManager fm = params.activity.getSupportFragmentManager(); | |
FragmentTransaction ft = fm.beginTransaction(); | |
if (mAnimationsEnabled) | |
ft.setCustomAnimations(animationEnter, animationExit); | |
if (mCurrentFragment != null) { | |
if (params.backStack) { | |
mBackStack.add(mCurrentFragment); | |
ft.detach(mCurrentFragment); | |
} else { | |
ft.remove(mCurrentFragment); | |
} | |
} | |
Fragment old = mCurrentFragment; | |
if (params.fragment == null) { | |
mCurrentFragment = Fragment.instantiate(params.activity, params.className, params.initArgs); | |
} else { | |
mCurrentFragment = params.fragment; | |
} | |
ft.add(fragmentFrameId, mCurrentFragment, FRAGMENT_TAG); | |
showBackButton(mCurrentFragment, !mBackStack.isEmpty() && !flags.contains(FLAG_HIDE_BACK_ARROW)); | |
ft.commitAllowingStateLoss(); | |
fragmentChanged(old, mCurrentFragment); | |
} | |
public void clearBackStack() { | |
if (mBackStack != null) { | |
mBackStack.clear(); | |
} | |
} | |
public void setCustomAnimationsForFragments(int enter, int exit, int popEnter, int popExit) { | |
animationEnter = enter; | |
animationExit = exit; | |
animationPopEnter = popEnter; | |
animationPopExit = popExit; | |
} | |
public int[] getCustomAnimationsForFragments() { | |
return new int[]{ | |
animationEnter, animationExit, animationPopEnter, animationPopExit | |
}; | |
} | |
public void setCustomAnimationsForFragments(int[] anims) { | |
setCustomAnimationsForFragments(anims[0], anims[1], anims[2], anims[3]); | |
} | |
public void showAnimations(boolean show) { | |
mAnimationsEnabled = show; | |
} | |
/** | |
* has to be called from Activity onSaveInstanceState | |
*/ | |
public void onSaveInstanceState(Bundle outState) { | |
if (mCurrentFragment == null) | |
return; | |
String[] fragmentsStack; | |
Bundle[] argStack; | |
Bundle[] savedStack; | |
if (mBackStack == null) { | |
fragmentsStack = new String[1]; | |
argStack = new Bundle[1]; | |
savedStack = new Bundle[1]; | |
} else { | |
fragmentsStack = new String[mBackStack.size() + 1]; | |
argStack = new Bundle[mBackStack.size() + 1]; | |
savedStack = new Bundle[mBackStack.size() + 1]; | |
for (int i = 0; i < mBackStack.size(); i++) { | |
Fragment fragment = mBackStack.get(i); | |
fragmentsStack[i] = fragment.getClass().getName(); | |
argStack[i] = fragment.getArguments(); | |
savedStack[i] = new Bundle(); | |
fragment.onSaveInstanceState(savedStack[i]); | |
} | |
} | |
fragmentsStack[fragmentsStack.length - 1] = mCurrentFragment.getClass().getName(); | |
argStack[fragmentsStack.length - 1] = mCurrentFragment.getArguments(); | |
savedStack[fragmentsStack.length - 1] = new Bundle(); | |
mCurrentFragment.onSaveInstanceState(savedStack[fragmentsStack.length - 1]); | |
outState.putStringArray(STATE_ARRAY, fragmentsStack); | |
writeBundleArray(outState, STATE_ARGS_ARRAY, argStack); | |
writeBundleArray(outState, STATE_SAVED_ARRAY, savedStack); | |
} | |
private void writeBundleArray(Bundle b, String key, Bundle[] array) { | |
b.putInt(key + "length", array.length); | |
for (int i = 0; i < array.length; i++) { | |
b.putBundle(key + i, array[i]); | |
} | |
} | |
private Bundle[] getBundleArray(Bundle b, String key) { | |
int len = b.getInt(key + "length"); | |
Bundle[] array = new Bundle[len]; | |
for (int i = 0; i < array.length; i++) { | |
array[i] = b.getBundle(key + i); | |
} | |
return array; | |
} | |
private void fragmentChanged(Fragment oldFragment, Fragment newFragment) { | |
if (mFragmentChangedListener != null) | |
mFragmentChangedListener.fragmentChanged(oldFragment, newFragment); | |
} | |
private void showBackButton(Fragment current, boolean show) { | |
if (mFragmentChangedListener != null) | |
mFragmentChangedListener.showBackButton(current, show); | |
} | |
public void nextFragment(Class<? extends Fragment> newFragClass, boolean backStack) { | |
nextFragment(newFragClass, null, backStack); | |
} | |
public interface FragmentChangedListener { | |
public void fragmentChanged(Fragment oldFragment, Fragment newFragment); | |
public void showBackButton(Fragment current, boolean show); | |
} | |
private static class NextFragmentParams { | |
FragmentActivity activity; | |
Fragment fragment; | |
String className; | |
boolean backStack; | |
Bundle initArgs; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment