Reputation: 642
The Fragment
backstack is made so that traversing backwards through a stack of fragments in one activity does not revert the Action Bar to its original state in the previous fragment.
Turns out, the Action Bar is actually attached to the Activity itself, not the fragment! Remember, fragments are only modular bits of the UI, and have to explicitly specify control to other fragments, sections of the activity, or even the Action Bar.
Keep reading for the solution...
Upvotes: 0
Views: 222
Reputation: 642
I found that the best approach to this problem is done by what is generally described in Reto Meier's answer to a previous question. My solution will just expand more deeply on his answer.
What we want to establish though is that we don't want to re-create the action bar every time we switch to a different fragment, reason being it's not very efficient. I'm going to walk you through an I wrote for a student scheduling app. It's not very complicated, and it's onboarding experience is composed of multiple fragments held within an activity.
To make this work, we need to make sure we're using replace()
to switch between fragments. This is better than layering fragments on top of each other, because it lets you configure the action bar separately for each fragment.
The first chunk of code comes from the activity's inner class, LoginOptionsFragment
, in its onCreateView()
method.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_login_options, container, false);
//LoginOptionsFragment will have its own action bar
setHasOptionsMenu(true);
//inject views. e.g: Button add_course
ButterKnife.inject(this, rootView);
add_course.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getFragmentManager().beginTransaction()
//exchange fragments. no messy clean-up necessary.
.replace(R.id.container, new AddCourseFragment())
.addToBackStack(null)
.commit();
}
});
return rootView;
}
Here, I not only make sure to call onCreateOptionsMenu()
via the setHasOptionsMenu(true)
, but mainly, as soon as the "ADD COURSE" button is clicked to switch to the AddCourseFragment
, the new fragment replaces the old fragment as the primary child of the activity. Next, after we override the onCreateOptionsMenu()
, we come to onResume()
, but we'll get to that later ;)
Secondly, we arrive at the AddCourseFragment
, where we even inflate a custom done-cancel view for the action bar. So let's look at the code!
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// BEGIN_INCLUDE (inflate_set_custom_view)
// Inflate a "Done/Cancel" custom action bar view.
final ActionBar actionBar = getActivity().getActionBar();
inflater = (LayoutInflater) actionBar.getThemedContext()
.getSystemService(LAYOUT_INFLATER_SERVICE);
//inflate custom action bar view
View customActionBarView = inflater.inflate(
R.layout.actionbar_custom_view_done_cancel, null);
//set listeners to items in the view
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
// "Done"
//remove custom view from action bar
actionBar.setDisplayShowCustomEnabled(false);
getFragmentManager().popBackStack();
//add course to list
}
});
customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
// "Cancel"
//remove custom view from action bar
actionBar.setDisplayShowCustomEnabled(false);
getFragmentManager().popBackStack();
}
});
// Show the custom action bar view and hide the normal Home icon and title.
actionBar.setDisplayOptions(
ActionBar.DISPLAY_SHOW_CUSTOM,
ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME
| ActionBar.DISPLAY_SHOW_TITLE);
actionBar.setCustomView(customActionBarView,
new ActionBar.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
actionBar.setDisplayHomeAsUpEnabled(false);
// END_INCLUDE (inflate_set_custom_view)
View rootView = inflater.inflate(R.layout.fragment_add_course, container, false);
ButterKnife.inject(this, rootView);
return rootView;
}
The ONLY part that you need to pay attention to are the OnClickListener
's added to the DONE and CANCEL buttons. In here, I use my previous reference to the parent Activity
's action bar and tell it to stop displaying the custom view. Now in addition to this specific method, there are more setDisplayXEnabled()
methods that you can pass in false to. After that, I pop the backstack to get to the previous fragment.
Here's how. Remember that onResume()
method that was hanging out in our LoginOptionsFragment
? Well, onResume()
is called once a fragment gets back into focus from the backstack! So if we override it and re-enable the parts of the action bar that we want, we win right? Yes we do. Here is all you need to add into the onResume()
.
@Override
public void onResume() {
super.onResume();
ActionBar actionBar = getActivity().getActionBar();
actionBar.setDisplayShowHomeEnabled(true); //show Home icon
actionBar.setDisplayShowTitleEnabled(true); //show title
// actionBar.setDisplayUseLogoEnabled(true); <--- more options
// actionBar.setDisplayHomeAsUpEnabled(true); <--- more options
}
And we did it all without recreating the action bar. Here's how it looks!
Thanks for reading, and happy coding!
Upvotes: 1