-
Notifications
You must be signed in to change notification settings - Fork 76
3. Installation
By default, you'd only need a Backstack instance and set a StateChanger to manipulate and navigate between your states. However, that wouldn't survive process death and wouldn't help with saving your viewstate - which is why generally, you'd need a BackstackManager instance.
But it's worth knowing that freezing/unfreezing the queue, surviving config change and process death; they need different lifecycle callbacks to work.
Therefore, two additional "utility classes" are provided that help with activity lifecycle integration of the BackstackManager.
These classes are the BackstackDelegate and the Navigator.
If you're above API 11+ and you're not trying to use Simple-Stack as a replacement for the fragment backstack, then you most likely need Navigator.
Navigator creates a BackstackHost (retained fragment) internally, and survives config changes while listening to all required lifecycle callbacks.
The simplest integration for Navigator would look like this:
public class MainActivity
extends AppCompatActivity {
public static final String TAG = "MainActivity";
@BindView(R.id.root)
RelativeLayout root;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
Navigator.install(this, root, History.single(FirstKey.create()));
}
@Override
public void onBackPressed() {
if(!Navigator.onBackPressed(this)) {
super.onBackPressed();
}
}
}But there are additional means to configure() the Navigator, for example providing a custom key parceler (which can make your state be parcelable if by default it isn't), or a configured version of the DefaultStateChanger.
Backstack backstack = Navigator.configure()
.setDeferredInitialization(true)
.setStateChanger(DefaultStateChanger.configure()
.setExternalStateChanger(this)
.setViewChangeCompletionListener(this)
.create(this, root))
.install(this, root, History.single(TasksKey.create()));
backstackHolder.setBackstack(backstack);
Navigator.executeDeferredInitialization(this);You're not required to provide the DefaultStateChanger as a state changer, you can provide any StateChanger you like. This is really just for convenience so that you don't need to implement swapping views and persisting/restoring their states manually.
If you use the DefaultStateChanger, then your keys are expected to implement StateKey interface (which specifies a view change handler - that determines how you animate your views; and also the layout resource for view inflation).
The BackstackDelegate is the original lifecycle integration, where you had to delegate all callbacks manually. Typically not needed, unless you need some sort of fine-grained behavior (as in, you need to be able to create the backstack before super.onCreate() - as it is the case with fragments; or you need to manage multiple backstacks inside the same activity).
For example, this is for fragments.
public class MainActivity
extends AppCompatActivity
implements StateChanger {
@BindView(R.id.root)
RelativeLayout root;
BackstackDelegate backstackDelegate;
FragmentStateChanger fragmentStateChanger;
@Override
protected void onCreate(Bundle savedInstanceState) {
// for fragments
backstackDelegate = new BackstackDelegate(null /* delayed init */);
backstackDelegate.onCreate(savedInstanceState, // <-- restoration BEFORE super.onCreate()!
getLastCustomNonConfigurationInstance(), //
History.single(TasksKey.create()));
backstackHolder.setBackstack(backstackDelegate.getBackstack());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
this.fragmentStateChanger = new FragmentStateChanger(getSupportFragmentManager(), R.id.root);
backstackDelegate.setStateChanger(this); // <-- delayed init for fragments
}
@Override
public Object onRetainCustomNonConfigurationInstance() {
return backstackDelegate.onRetainCustomNonConfigurationInstance(); // <-- !
}
@Override
protected void onPostResume() {
super.onPostResume();
backstackDelegate.onPostResume(); // <-- !
}
@Override
public void onBackPressed() {
if(!backstackDelegate.onBackPressed()) {
super.onBackPressed();
}
}
@Override
protected void onPause() {
backstackDelegate.onPause(); // <-- !
super.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
backstackDelegate.onSaveInstanceState(outState); // <-- !
}
@Override
protected void onDestroy() {
backstackDelegate.onDestroy(); // <-- !
super.onDestroy();
}
@Override
public void handleStateChange(StateChange stateChange, Callback completionCallback) {
if(stateChange.topNewState().equals(stateChange.topPreviousState())) {
// no-op
completionCallback.stateChangeComplete();
return;
}
fragmentStateChanger.handleStateChange(stateChange); // <--- check sample for this
completionCallback.stateChangeComplete();
}
}