HOW TO Revert Action Bar after Switching Fragments - android

Problem:
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.
Why does this happen?
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...

Solution:
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.
But how do I actually revert the action bar!?
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!

Related

How to correctly put my activities in Tutorial´s Bottom Navigation?

I am new to programming. I have 4 working activities with 4 xml layouts. I also copied this simple example fragment (I think I need 4 of those for my 4 activites) from a tutorial:
public class SelectFriends extends Fragment {
public SelectFriends () {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_SelectFriends , container, false);
}
}
This is the relevant part from MainActivity:
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.navigation_item1:
getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.fade_in, R.anim.fade_out).replace(R.id.container, SelectFriends).commit();
return true;
case R.id.navigation_item2:
getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.fade_in, R.anim.fade_out).replace(R.id.container, StartFood).commit();
return true;
[...]
}
return false;
}
I can start my activitie´s selectFriends.xml when I replace fragment_SelectFriends in #Override public View onCreateView with ID of selectFriends.xml . The problem is I don´t know where to put the corresponding Activity, so the selectFriends.xml shows up correctly with BottomNavigation but there is no interaction possible, of course. What is best practice? Internet is confusing me: am I understanding the use of fragments false? I don´t even understand why I should make fragments with BottomNavigation when the things I want to show in the different BottomNavigation displays are completely different from each other. Thank you
Let's start from your question:
How to correctly put my activities in Tutorial´s Bottom Navigation?
You will not put your activities in the Bottom Navigation.
You will have one Activity which will draw the layout of your screen. Secondly you will have a number of Fragment's which will represent some parts of your screen.
How many Fragment's? As many as the Bottom Navigation's options.
Take the Youtube app as an example.
The whole screen is an Activity, Youtube's MainActivity. As you can see the user pressed Subscriptions in the Bottom Navigation so the MainActivity called a SubscriptionsFragment to draw the subscriptions part of the screen (all the layout except the ActionBar at the top and the Bottom Navigation at the bottom.
If a user selects Home at the Bottom Navigation then the MainActivity will replace the part of the screen where the SubscriptionsFragment draw its layout with the HomeFragment's layout. And the same thing will happen with the other options of Youtube's Bottom Navigation bar.
So to clarify. There is only one activity here. The MainActivity. Not four. This activity commands 4 fragments to draw the 4 main parts of its screen (the home part with HomeFragment, the trending part with TrendingFragment, the subscription part with SubscriptionFragment and finally the library part with LibraryFragment.
From your last comment:
I have to put the two pieces of code that I posted above into one big
class?
No you don't have to. It is not necessary to create one file, such as MainActivity.java (where your MainActivity is defined), and then define the Fragment's classes inside the same file.
You want to display 4 screens using a bottom navigation, right? Create a file for your activity and four separate files, one per Fragment.
Example:
Your first file, SelectFriends.java, where SelectFriends fragment is defined, as you've posted above...
public class SelectFriends extends Fragment {
public SelectFriends () {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_SelectFriends , container, false);
}
}
Three more separate files like this one, one per fragment.
And lastly your activity, again, as you've posted above.
public class MainActivity extends AppCompatActivity{
#Override
public void onCreate(Bundle savedInstanceState){
// ... some code here
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.navigation_item1:
..
// Here the activity figured out that the first item of the bottom navigation
// was clicked, so it calls the support fragment manager to display a fragment
// inside the container view
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.fade_in, R.anim.fade_out)
.replace(R.id.container, SelectFriends).commit();
return true;
case R.id.navigation_item2:
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.fade_in, R.anim.fade_out)
.replace(R.id.container, StartFood).commit();
return true;
}
return false;
}
}
Edit: Read the Ultimate Guide to Bottom Navigation on Android

How to change header according to navigation drawer

I am using navigation drawer in my application, with fragments. There are icons and text in each row(one of them is products, on clicking these a fragment is open with a list view.After clicking on the list view row i have an activity in this i want header name same as i have selected(products).but bydefault it is showing name off the app which is sales.please help.
pass product name in Intent in your list's onItemClickListner
Intent intent = new Intent(getActivity(),YOURACTIVITY.class);
intent.putExtra("product_name",YOUR PRODUCT_NAME);
getActivity().startActivity(intent);
and in your target activity (your product detail activity) put below code in onCreate() method.
getSupportActionBar().setTitle("YOUR DESIRED TITLE");
If using Frament , you can have a method in parent hosting Activity like
this
public void setActionBarTitle(String title) {
getSupportActionBar().setTitle(title);
}
and invoke this method from your individual fragment like this and set the title
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//usual statements
((HomeActivity)getActivity()).setActionBarTitle("Child fragment name");
}
You can set the actionbar header with the following. You can try this.
ActionBar actionBar = getActionBar();
actionBar.setTitle("My Title");

Add and Remove views from Toolbar depending on Fragment Displayed

I have an application, that at the moment only has 2 fragments. Fragment 1, this has the nav drawer and the title.
Fragment 2 requires a custom view as adding menu items won't work as I need alignment. So I add the view as follows:
ActionBar.LayoutParams params = new ActionBar.LayoutParams(
ActionBar.LayoutParams.MATCH_PARENT,
ActionBar.LayoutParams.MATCH_PARENT,
Gravity.LEFT);
//Remove nav drawer "hamburger"
mMainActivity.mActionBarDrawerToggle.setDrawerIndicatorEnabled(false);
//Remove title from Toolbar
bar.setDisplayShowTitleEnabled(false);
LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
View postToolBar = layoutInflater.inflate(R.layout.upload_content_toolbar, null);
mMainActivity.mToolBar.addView(postToolBar, params);
That is fine and it displays correctly. However, when I want to return to the previous fragment Fragment 1, I then call mMainActivity.mToolBar.removeView(postToolBar); I call this on return to Fragment 1 as the user can navigate either by the back button or by button in the postToolBar. However, the view is still in place. I can't get rid of it. I have now tried setting the visibility to GONE but that won't work either.
This was pretty simple with the Action Bar, however things seem to have gotten a bit complicated with the Tool bar.
I must add that in each of my two fragments I extend a BaseFragment in which I declare the toolbar view.
Can any one help or send me in the direction of a tutorial?
This is how i achieved it at this time:
in activity onCreateView();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowCustomEnabled(true); // enable overriding the default toolbar_home layout
getSupportActionBar().setDisplayShowTitleEnabled(false); // disable the default title element here (for centered title)
getSupportActionBar().setDisplayShowHomeEnabled(false);
cutomToolbarView=getLayoutInflater().inflate(R.layout.custom_toolbar_home, null);
getSupportActionBar().setCustomView(cutomToolbarView);
}
and here is two simple method to do the magic
public void setToolbarTitleEnabled(String title) {
getSupportActionBar().setDisplayShowCustomEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(true);
getSupportActionBar().setTitle(title);
}
public void setCustomToolbarEnabled() {
getSupportActionBar().setDisplayShowCustomEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(false);
}
then simply when you can change actionbar like these:
when to select HomeFragment.java or whatever in your case
setToolbarTitleEnabled(CURRENT_TAG);
and when to select other fragment:
setCustomToolbarEnabled();

Android - Calling Activity into Fragments - Navigation Drawer

I've downloaded the sample project from the Android official site http://developer.android.com/training/implementing-navigation/nav-drawer.html. I am trying to understand how the Navigation Drawer works. So, I have one doubt, they call a Fragment for each item from the left menu. In my project, I have a big activity which I am trying to call by this Fragment:
public class HomeFragment extends Fragment {
public HomeFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Intent intent = new Intent();
intent.setClass(getActivity(), ListMatch.class);
startActivity(intent);
View rootView = inflater.inflate(R.layout.list_match, container, false);
return rootView;
}
}
But, if I do that, it calls perfectly my Activity, but the menu disappear. How can I call this activity and keep my menu? Thanks a lot.
Like Raghunandan said, the drawer applies wihin a single activity. It's common to launch a new activity from the "main" drawer, but usually that kind of activity would have the up action set in the action bar to go back to the main activity.

Android: Change ActionBar Menu Items from Fragment

Can anyone give a quick example of how to change the contents of an Activity action bar based on something that takes place in a fragment? My intent:
Normal menu items -> Something in the fragment is moved -> menu items change to save / discard buttons.
My first impulse is to setup Broadcast Receivers in both the activity and the fragment to cross talk, but I am not sure if this is correct.
Fragments can change menu in actionbar. For that you have to add necessary flag in fragment's oncreate() using method setHasOptionsMenu(true);
When your fragment is loaded you will get call at onCreateOptionsMenu(Menu menu, MenuInflater inflater) just like in an activity. Then do necessary changes to your menu.
Save you menu as global in fragment, and whenever you want to make a change, apply on it.
The following works for me. I have a custom class that implements ListView.MultiChoiceModeListener inside a Fragment:
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
// Choose the correct Action Bar menu to display
int menu = myCondition == true ? R.menu.my_default_menu : R.menu.my_menu_2;
// Configure to use the desired menu
mode.getMenu().clear();
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(menu);
}
Given how you detect 'something in the fragment has moved', extending ListView.MultiChoiceModeListener may not work for you, but hopefully this illustrates how to change the menu. The key is to get access to a ActionMode instance.
I think you want to use a contextual action mode. On the drag event, you will start a new ActionMode which can replace the contents of the action bar with menu items specific to what you want to allow the user to do. Once the user chooses an action, you finish the action mode and the action bar returns to its previous state.
Not sure if an ActionBar instance would help with the menu you but would surely be useful.. Here's a way to get about it
Try this to get the ActionBar from the FragmentActivity using the onAttach(Activity activity) method in the Fragment.
First of all make a global object of your FragmentActivity in the Fragment like this
public class YourFragment extends Fragment {
private YourFragmentActivity context;
}
Override this in the YourFragment class
#Override
public void onAttach(Activity activity){
context = (YourFragmentActivity)activity;
super.onAttach(activity);
}
Then in the OnCreate method in the YourFragment do this
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState){
...
android.support.v7.ActionBar actionBar = context.getSupportActionBar();
...
}

Categories

Resources