I use an Otto event bus to communicate between my Activity
and Fragment
s. The controlling Activity
maintains its own Stack
of custom back events which each contain a back action Runnable
, i.e. what action should be taken when the back button is pressed.
The advantage to this approach is a slightly more decoupled design and should scale with more fragments. For readability, I have defined the Otto Events inside my Fragment
, here, but these could be easily moved elsewhere in your project.
Here's some sample code to give you an idea of how it's done.
Fragment(s)
The Fragment signals its intent to take hold of the next back press by posting a BackStackRequestEvent
to the Otto event bus and supplying a Runnable
action to be executed when the event is popped off the Activity
's custom stack. When the Fragment is detached, it sends a ClearBackStackEvent
to the bus to remove any of the Fragment
's back actions from the activity's custom stack.
public class MyFragment extends Fragment {
private final String BACK_STACK_ID = "MY_FRAGMENT";
...
public class BackStackRequestEvent {
private Runnable action;
private String id;
public BackStackRequestEvent(Runnable action, String id) {
this.action = action;
this.id = id;
}
public void goBack() {
action.run();
}
public String getId() {
return id;
}
}
public class ClearBackStackEvent {
private String id;
public ClearBackStackEvent(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
...
@Override
public void onDetach() {
super.onDetach();
// Get your Otto singleton and notify Activity that this
// Fragment's back actions are no longer needed
// The Fragment lifecycle stage in which you do this might vary
// based on your needs
EventBus.getInstance().post(new ClearBackStackEvent(BACK_STACK_ID));
}
...
public void someChangeInFragment() {
// Notify the Activity that we want to intercept the next onBackPressed
EventBus.getInstance().post(new BackStackRequestEvent(new Runnable()
{
@Override
public void run() {
// Reverse what we did
doBackAction();
}
}, BACK_STACK_ID)); // constant used later to remove items from Stack
}
}
Activity
The activity registers / unregisters its interest in the events we defined above in onStart()
and onStop()
. When it receives a new BackStackRequestEvent
it adds it to its custom back stack. Once onBackPressed()
is called, it pops the back stack and invokes the back action using BackStackRequestEvent.goBack()
which in turn runs the Fragment's Runnable
. If there is nothing on the Stack, the normal back behaviour is followed.
When the Fragment is detached, the Activity receives a ClearBackStackEvent
and it removes all items of the supplied id
from the Stack.
public class MyActivity extends Activity {
private Stack<MyFragment.BackStackRequestEvent> customBackStack = new Stack<>();
...
@Override
protected void onStart() {
super.onStart();
EventBus.getInstance().register(this);
}
@Override
protected void onStop() {
super.onStop();
EventBus.getInstance().unregister(this);
}
@Subscribe // Annotation indicating that we want to intercept this Otto event
public void backStackRequested(MyFragment.BackStackRequestEvent request) {
customBackStack.push(request);
}
@Override
public void onBackPressed() {
if (customBackStack.empty()) {
// No custom actions so default behaviour followed
super.onBackPressed();
}
else {
// Pop the custom action and call its goBack() action
MyFragment.BackStackRequestEvent back = customBackStack.pop();
back.goBack();
}
}
@Subscribe
public void clearBackStackRequested(MyFragment.ClearBackStackEvent request) {
String id = request.getId();
for (MyFragment.BackStackRequestEvent backItem : customBackStack) {
if (backItem.getId().contentEquals(id)) {
customBackStack.remove(backItem);
}
}
}
}