Here is solution I came up over time.
The idea is following, you need to keep a stack data structure and whenever you add a fragment add it to stack as well, then override onBackPress method and check if stack is not empty then replace your fragment container with new fragment from top of the stack when it is empty do super.onbackpress
So here is a parent class for all kind of fragment based navigation.
public abstract class FragmentsStackActivity extends BaseActivity {
public static final String TAG_BUNDLE = "bundle_tag";
protected final Bundle fragmentArgs = new Bundle();
protected Stack<Fragment> fragments = new Stack<>();
abstract protected void setupFragments();
public void setFragmentArguments(Fragment fragment, Bundle arguments){
if(!fragments.isEmpty() && fragments.peek()!=fragment){
fragment.setArguments(arguments);
}
}
public void setFragmentFromStack() {
if(!fragments.isEmpty()) {
Fragment fragment = fragments.peek();
final Fragment oldFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (oldFragment == null || oldFragment != fragment) {
getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
//transaction.setCustomAnimations(R.anim.animator_left_right_in, R.anim.animator_left_right_in);
transaction.replace(R.id.fragment_container, fragment).commit();
}
}else {
finish();
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//TODO need to save fragment stack
}
}
example of an activity that extends this class
public class LoginActivity extends FragmentsStackActivity{
private final MyFragment1 fragment1 = new MyFragment1();
private final MyFragment2 fragment2 = new MyFragment2();
private final User mUser = new User();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
setupFragments();
setFragmentFromStack();
}
@Override
protected void setupFragments() {
fragments.add(fragment2);
//fragment2.setNotifier(this); // I use notifiers listener but you can choose whatever convenient for you
Bundle fragmentArgs = new Bundle();
fragmentArgs.putBoolean(Constants.TAG_LOGIN, true);
fragmentArgs.putParcelable(User.TAG, mUser);
fragmentArgs.putInt(Constants.TYPE, getIntent().getIntExtra(Constants.TYPE, 0));
fragment2.setArguments(fragmentArgs);
//fragment1.setNotifier(this); // I use notifiers listener but you can choose whatever convenient for you
}
// this method teals with handling messages from fragments in order to provide navigation
// when some actions taken inside the fragment, you can implement your own version
public void onReceiveMessage(String tag, Bundle bundle) {
switch (tag) {
case MyFragment2.TAG_BACK:
case MyFragment1.TAG_BACK:
fragments.pop();
setFragmentFromStack();
break;
case MyFragment2.TAG_NEXT:
fragment1.setArguments(bundle);
fragments.add(fragment1);
setFragmentFromStack();
break;
case MyFragment1.TAG_NEXT:
goToWelcomeScreen(bundle);
finish();
break;
}
}
private void goToWelcomeScreen(Bundle bundle){
}
}