Save state of fragment A when navigating from bottomNavigationView to fragment B - android

I am new to Android and I have been struggling for some time now with saving state of a fragment.
My app has 5 options in the bottomNavigationView (I am using the default activity from Android). In fragment A (actually called "SearchFragment") I have a button and when clicked it sets some text in a TextBox. I want to save the state of this fragment (with the text there) when navigating to another fragment and the coming back to the fragment A (and have the text still there from the previous button click).
From the code I wrote it saves the fragment's state when changing orientation for example, but doesn't do so when navigating to other fragments and then coming back.
How could I change the code? Or is there any other method to save the current state of a fragment on navigation? I really need some help here...and thanks for your attention!
My mainActivity looks like this:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView navView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_search, R.id.navigation_explore, R.id.navigation_trips, R.id.navigation_groups,R.id.navigation_profile)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);
}
}
And the fragment class looks like this:
public class SearchFragment extends Fragment {
private SearchViewModel searchViewModel;
Button b;
TextView text;
Bundle savedState = null;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_search, container, false);
searchViewModel = ViewModelProviders.of(this).get(SearchViewModel.class);
final TextView textView = root.findViewById(R.id.text_dashboard);
searchViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
#Override
public void onChanged(#Nullable String s) {
textView.setText(s);
}
});
Log.i("state","onCreate");
Log.i("bundleIsNull", "" + (savedState == null));
b = root.findViewById(R.id.button2);
text = root.findViewById(R.id.textView);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
text.setText("YEEEY");
Log.i("bundleIsNull", "" + (savedState == null));
}
});
if(savedInstanceState != null && savedState == null) {
savedState = savedInstanceState.getBundle("buttonText");
}
if(savedState != null) {
text.setText(savedState.getCharSequence("buttonText"));
}
savedState = null;
return root;
}
#Override
public void onDestroyView() {
super.onDestroyView();
Log.i("state", "onDestroyView");
savedState = saveState();
text = null;
b = null;
}
private Bundle saveState() {
Bundle state = new Bundle();
state.putCharSequence("buttonText", text.getText());
return state;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.i("state", "onSaveInstanceState");
outState.putBundle("buttonText", (savedState != null)? savedState : saveState());
}
#Override
public void onPause() {
super.onPause();
Log.i("state","onPause");
}
}

Few facts around Android Navigation Component with Bottom Navigation Bar.
- > Fragments are always recreated (onCreate, onViewCreated, onViewDestroyed are called as soon as the user navigates to another fragment)
- > Fragment will save its state only when activity is recreated (e.g. screen rotation) , navigating between fragments doesn't save fragment's state.
I want to save the state of this fragment (with the text there) when
navigating to another fragment and the coming back to the fragment A
(and have the text still there from the previous button click).
This wont be possible as a new instance of FragmentA is created when
you navigate back from fragmentB to Fragment A.
you can not achieve this with navigation controller as of now.
Since you are using navigation component, You should switch to using Viewmodel to solve retain fragment state issue.
Consult the following link to see how to communicate and save data between fragments using viewmodel.
https://androidwave.com/fragment-communication-using-viewmodel/

Related

Navigation component shared element transitions works for enter but not for popping back

I'm trying to use a shared element animation between 2 fragments, BlankFragment and BlankFragment2. BlankFragment has a recycler view and BlankFragment2 is a details screen. They share an image and I'm using the new navigation component.
In BlankFragment I'm building FragmentNavigator.Extras and passing the extras to my call to navigate with the transition name of the shared image (as its a recycler view and these need to be unique),
In BlankFragment2 I'm receiving this name setting it to my image and setting the setSharedElementEnterTransition
The result is that the enter animation works fine but the exit/return doesn't, I've tried setting them and not setting them (because I believe the navigation component should handle this for me) can anyone help?
MainActivity Navigation Setup
private void setNavigation() {
navController = Navigation.findNavController(this, R.id.main_fragment);
NavigationUI.setupActionBarWithNavController(this, navController);
}
Handling back button
#Override
public boolean onSupportNavigateUp() {
return Navigation.findNavController(this, R.id.main_fragment).navigateUp()
|| super.onSupportNavigateUp();
}
BlankFragment OnClick
#Override
public void onClick(View view, int position) {
NavController navController = Navigation.findNavController(recyclerView);
FragmentNavigator.Extras extras = new FragmentNavigator.Extras.Builder().addSharedElement(view, view.getTransitionName()).build();
BlankFragmentDirections.ActionBlankFragmentToBlankFragment2 directions = BlankFragmentDirections.actionBlankFragmentToBlankFragment2(view.getTransitionName());
navController.navigate(directions,extras);
}
BlankFragment2 onCreate with return/exit transition
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.move));
setExitTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.no_transition));
setSharedElementEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.move));
getFragmentArguments();
}
Get Arguments Method
private void getFragmentArguments(){
if (getArguments() != null){
transitionName = BlankFragment2Args.fromBundle(getArguments()).getTransitionName();
Log.d(TAG, "transition name " + transitionName);
}
}
Set image transition name
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
image = view.findViewById(R.id.image);
image.setTransitionName(transitionName);
text = view.findViewById(R.id.text);
}
Java
To Fix on return transition, use viewTreeObserver.addOnPreDrawListener
In BlankFragment (fragment with recycle view)
We need to call postponeEnterTransition(); so the transition
will be pospone
Setup addOnPreDrawListener on recycleView as following
RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
//setup for recycle view adapter
ViewTreeObserver viewTreeObserver = recyclerView.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
startPostponedEnterTransition();
return true;
}
});
That's it.
Follow this link to get more info on ViewTreeObserver follow this link
FIN !!

Transition between fragments via back button and home button

I have a fragment with a view and an options menu:
public class OfferingsFragment extends Fragment
{
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState)
{
setHasOptionsMenu(true);
View view = inflater.inflate(R.layout.offering_tiles, null);
...
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
Intent intent = new Intent(getActivity(), SettingsActivity.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
}
From the options menu, the user opens this preference fragment, which is hosted by the SettingsActivity:
public class SettingsActivity extends Activity {
private SettingsFragment settingsFragment = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
settingsFragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, settingsFragment)
.commit();
}
The view of the OfferingsFragment depends on one of the preferences. That is, after this preference has changed, the OfferingsFragment must be refreshed by calling onCreateView again. What I do is this:
Open preference screen from OfferingsFragment's option menu
Change preference
Return to OfferingsFragment
If I return to the OfferingsFragment via the Home Button (left arrow in ActionBar), then the OfferingsFragment gets refreshed by calling its onCreateView (which is the desired effect). However, if I return to the OfferingsFragment via the Back Button (on the device), onCreateView is NOT CALLED and thus the view is NOT re-created. What I want is that the view is also re-created when the user presses the Back Button. Any ideas how to achieve this?
What Happens
When you press Up button parent activity is called via startActivity which means a new instance is created by default.
When you press Back button current activity is finished and you're back in the previous activity and its already existing instance (it was in stopped state).
How To Deal With It
What I want is that the view is also re-created when the user presses the Back Button. Any ideas how to achieve this?
Start the settings activity via startActivityForResult:
public static final int RC_SETTINGS = 1;
private void startSettingsActivity() {
Intent i = new Intent(this, SettingsActivity.class);
startActivityForResult(i, RC_SETTINGS);
}
When the result comes back reattach the fragment. This will recreate its view hierarchy.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Call to super if you value your life. And want proper lifecycle handling.
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SETTINGS) {
// If we just came back from SettingsActivity...
// ...reattach fragment and trigger view recreation.
final FragmentManager fm = getFragmentManager();
final f = fm.findFragmentById(android.R.id.content);
fm.beginTransaction().detach(f).attach(f).commit();
}
}
Replace the fragment ID with whatever you used.
Pro tip
If your fragment is not misconfigured you should be able to call
public class SettingsActivity extends Activity {
private SettingsFragment settingsFragment = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
settingsFragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, settingsFragment)
.commit();
} else {
settingsFragment = (SettingsFragment) getFragmentManager().findFragmentById(android.R.id.content);
}
}
}
This is both resourceful and practical as your original code would lose state (for example scroll position) on configuration change.

Fragment navigation using activity

I have one activity that contains a view pager with fragments in it. I also have a navigation drawer that has fragments. whenever I'm starting my app, in main activity, I'm adding the first fragment to the fragment manager, so it will shown whenever the app is starting.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
home = new FragmentHome();
mainToolbar = (Toolbar) findViewById(R.id.mainActivityToolBar);
setSupportActionBar(mainToolbar);
getSupportActionBar().setDisplayShowHomeEnabled(true);
if(savedInstanceState == null){
home = new FragmentHome();
FragmentManager manager = getSupportFragmentManager();
// changed the back stack here..
String Tag = "start";
manager.beginTransaction().add(R.id.layout_container, home, Tag).commit();
getSupportActionBar().setTitle("Home");
}
FragmentNavigationDrawer fragmentNavigationDrawer = (FragmentNavigationDrawer) getSupportFragmentManager().findFragmentById(R.id.fragment_contain_drawer);
fragmentNavigationDrawer.setUpDrawer(R.id.fragment_contain_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), mainToolbar);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState, "fragment", home);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if(savedInstanceState != null){
home = getSupportFragmentManager().getFragment(savedInstanceState, "fragment");
}
}
#Override
public void changeToolBarTitle(String title) {
getSupportActionBar().setTitle(title);
}
in the navigation drawer adapter I have a method that get the fragments and show or add them when needed. my main problem that I cant figure how to solve is that I'm trying to keep the state of first fragment(which contain a recycler view with array list of custom object), while switching to other fragments in the drawer and display their content
public void onNavigationDrawerItemSelected(int position){
// Add the fragments only once
if (getFragmentManager().findFragmentByTag(navigationDrawerFragmentsTags[position]) == null)
{
fragmentTransaction.add(R.id.layout_container,navigationDrawerFragments[position],navigationDrawerFragmentsTags[position]);
}
// Hiding & Showing fragments
for(int i=0;i<navigationDrawerFragments.length;i++)
{
if(i == position)
{
fragmentTransaction.show(navigationDrawerFragments[i]);
}
else
{
// Check if the fragment is added and then hide it
if (getFragmentManager().findFragmentByTag(navigationDrawerFragmentsTags[i]) != null) {
fragmentTransaction.hide(navigationDrawerFragments[i]);
}
}
}
fragmentTransaction.commit();
}
however the first fragment that was added is always exists beneath the other layers and I cant hide it. i want to keep it state, but also to hide when I'm going to another fragment and I need to keep its content to display the list when the user is going back to this fragment.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
itemList = (ListView) v.findViewById(R.id.drawer_list_items);
adapter = new NavigationDrawerAdapter(getContext(), R.layout.drawer_item_views, addItemsToList());
itemList.setAdapter(adapter);
itemList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
onNavigationDrawerItemSelected(position);
myDrawerLayout.closeDrawers();
}
});
return v;
}
I tried to use interfaces, I tried to look in google but I cant find an answer to that.
//
to keep the state, add it to backstack
and instead of adding new fragment, just replace your fragment i.e. remove older and then add new one. i.e. fragmentTransaction.replace() instead of fragmentTransaction.add()

Can't keep fragments live through changing orientation or Saving fragment UI

I have an app that uses SherlockActionBar and switches between dynamic fragments through ActionItems in the Action Bar.
The problem is that i can't save the fragment states and their UI data.
This is the FragmentActivity:
public class MainActivity extends SherlockFragmentActivity {
private LocationFragment locationFragement;
private HomeFragment homeFragment;
private SettingsFragment settingsFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(false);
locationFragement = new LocationFragment();
homeFragment = new HomeFragment();
settingsFragment = new SettingsFragment();
}
#Override
public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
getSupportMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
FragmentManager fragManager = getSupportFragmentManager();
FragmentTransaction fragTransaction = fragManager.beginTransaction();
switch (item.getItemId()){
case R.id.home:
fragTransaction.replace(android.R.id.content, homeFragment);
break;
case R.id.location:
fragTransaction.replace(android.R.id.content, locationFragement);
break;
case R.id.settings:
fragTransaction.replace(android.R.id.content, settingsFragment);
break;
}
fragTransaction.commit();
return true;
}
Firstly, I tried to call to a single instance of the fragments, only to encounter NullPointerExceptions every time i did in the onOptionsItemSelected switch block
fragTransaction.replace(android.R.id.content, myFragmentClassName);
while the fragment instantiation was under
if(savedInstanceState == null){
locationFragement = new LocationFragment();
homeFragment = new HomeFragment();
settingsFragment = new SettingsFragment();
}
The fragments would not have created under orientation change obviously, and i couldn't figure anyway i could keep them instantiated. So i decided to create them anew every onCreate (which is bad but i wanted something to work) and save the UI data just to pass it back to the fragment.
This is one of the fragments, the other 2 are the same:
public class LocationFragment extends SherlockFragment {
private View myView;
private CheckBox myCheckBok;
#Override
public void onCreate(Bundle savedInstanceState) {
setRetainInstance(true);
Log.d("LocationFragment", "onCreate running");
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("LocationFragment", "onCreateView running");
myView = inflater.inflate(R.layout.map_fragment, container, false);
myCheckBok = (CheckBox) myView.findViewById(R.id.mapFragmentCheckBox);
if(savedInstanceState != null){
myCheckBok.setChecked(savedInstanceState.getBoolean("isChecked"));
}
return myView;
}
#Override
public void onResume()
{
Log.d("LocationFragment", "onResume running");
super.onResume();
}
#Override
public void onPause() {
Log.d("LocationFragment", "onPause running");
super.onPause();
}
#Override
public void onSaveInstanceState(Bundle outState) {
boolean isChecked = myCheckBok.isChecked();
outState.putBoolean("isChecked", isChecked);
Log.d("LocationFragment", "onSaved Instant running");
super.onSaveInstanceState(outState);
}
I'm saving the checkbox's state, when i pass through the icons the checkbox state is saved and also through orientation change. BUT, when i change orientation, press another icon (or press the same one) and return to my fragment, the state isn't saved. I realized this is caused due to onSavedInstanceState not running when i change to another fragment (thus not saving or passing the checkBox data).
I haven't found answers anywhere on how to save correctly UI data from fragments (just through savedInstanceState doesn't cut it for my purposes) or how to manage several fragment instantiations while changing them. I know that each time i change a fragment, all it's data
is saved automatically by the system, but can't seem to get it to work.
Please help.
It's not part of the problem but you should be aware that the use of onSaveInstanceState is not needed for saving a View state. Android does that for you already as long as there's an ID to the View.
Seems like the problem you are facing is that you are not restoring the fragments from their FragmentManager. The fragments are there, but you simply didn't go to the FragmentManager and brought them once the onCreate has happened again.
This code should go in the onCreate:
// Try and fetch the fragments from the manager
locationFragement = fragmentManager.findFragmentByTag(LOCATION_FRAGMENT_TAG);
homeFragment = fragmentManager.findFragmentByTag(HOME_FRAGMENT_TAG);
settingsFragment = fragmentManager.findFragmentByTag(SETTINGS_FRAGMENT_TAG);
// If the fragment weren't found - meaning it's the first time the activity was
// started, create them
if (locationFragement == null) locationFragement = new LocationFragment();
if (homeFragment == null) homeFragment = new HomeFragment();
if (settingsFragment == null) settingsFragment = new SettingsFragment();
And, whenever you are adding/removing/replacing a fragment, make sure to add him with the TAG, i.e.
// EXAMPLE HOW TO USE A REPLACE WITH A TAG
getSupportFragmentManager().beginTransaction().replace(android.R.id.content,
locationFragement, LOCATION_FRAGMENT_TAG);
Does that work?

Fragments onResume from back stack

I'm using the compatibility package to use Fragments with Android 2.2.
When using fragments, and adding transitions between them to the backstack, I'd like to achieve the same behavior of onResume of an activity, i.e., whenever a fragment is brought to "foreground" (visible to the user) after poping out of the backstack, I'd like some kind of callback to be activated within the fragment (to perform certain changes on a shared UI resource, for instance).
I saw that there is no built in callback within the fragment framework. is there s a good practice in order to achieve this?
For a lack of a better solution, I got this working for me:
Assume I have 1 activity (MyActivity) and few fragments that replaces each other (only one is visible at a time).
In MyActivity, add this listener:
getSupportFragmentManager().addOnBackStackChangedListener(getListener());
(As you can see I'm using the compatibility package).
getListener implementation:
private OnBackStackChangedListener getListener()
{
OnBackStackChangedListener result = new OnBackStackChangedListener()
{
public void onBackStackChanged()
{
FragmentManager manager = getSupportFragmentManager();
if (manager != null)
{
MyFragment currFrag = (MyFragment) manager.findFragmentById(R.id.fragmentItem);
currFrag.onFragmentResume();
}
}
};
return result;
}
MyFragment.onFragmentResume() will be called after a "Back" is pressed. few caveats though:
It assumes you added all
transactions to the backstack (using
FragmentTransaction.addToBackStack())
It will be activated upon each stack
change (you can store other stuff in
the back stack such as animation) so
you might get multiple calls for the
same instance of fragment.
I've changed the suggested solution a little bit. Works better for me like that:
private OnBackStackChangedListener getListener() {
OnBackStackChangedListener result = new OnBackStackChangedListener() {
public void onBackStackChanged() {
FragmentManager manager = getSupportFragmentManager();
if (manager != null) {
int backStackEntryCount = manager.getBackStackEntryCount();
if (backStackEntryCount == 0) {
finish();
}
Fragment fragment = manager.getFragments()
.get(backStackEntryCount - 1);
fragment.onResume();
}
}
};
return result;
}
After a popStackBack() you can use the following callback : onHiddenChanged(boolean hidden) within your fragment
The following section at Android Developers describes a communication mechanism Creating event callbacks to the activity. To quote a line from it:
A good way to do that is to define a callback interface inside the fragment and require that the host activity implement it. When the activity receives a callback through the interface, it can share the information with other fragments in the layout as necessary.
Edit:
The fragment has an onStart(...) which is invoked when the fragment is visible to the user. Similarly an onResume(...) when visible and actively running. These are tied to their activity counterparts.
In short: use onResume()
If a fragment is put on backstack, Android simply destroys its view. The fragment instance itself is not killed. A simple way to start should to to listen to the onViewCreated event, an put you "onResume()" logic there.
boolean fragmentAlreadyLoaded = false;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null && !fragmentAlreadyLoaded) {
fragmentAlreadyLoaded = true;
// Code placed here will be executed once
}
//Code placed here will be executed even when the fragment comes from backstack
}
In my activity onCreate()
getSupportFragmentManager().addOnBackStackChangedListener(getListener());
Use this method to catch specific Fragment and call onResume()
private FragmentManager.OnBackStackChangedListener getListener()
{
FragmentManager.OnBackStackChangedListener result = new FragmentManager.OnBackStackChangedListener()
{
public void onBackStackChanged()
{
Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (currentFragment instanceof YOURFRAGMENT) {
currentFragment.onResume();
}
}
};
return result;
}
A little improved and wrapped into a manager solution.
Things to keep in mind. FragmentManager is not a singleton, it manages only Fragments within Activity, so in every activity it will be new. Also, this solution so far doesn't take ViewPager into account that calls setUserVisibleHint() method helping to control visiblity of Fragments.
Feel free to use following classes when dealing with this issue (uses Dagger2 injection). Call in Activity:
//inject FragmentBackstackStateManager instance to myFragmentBackstackStateManager
FragmentManager fragmentManager = getSupportFragmentManager();
myFragmentBackstackStateManager.apply(fragmentManager);
FragmentBackstackStateManager.java:
#Singleton
public class FragmentBackstackStateManager {
private FragmentManager fragmentManager;
#Inject
public FragmentBackstackStateManager() {
}
private BackstackCallback backstackCallbackImpl = new BackstackCallback() {
#Override
public void onFragmentPushed(Fragment parentFragment) {
parentFragment.onPause();
}
#Override
public void onFragmentPopped(Fragment parentFragment) {
parentFragment.onResume();
}
};
public FragmentBackstackChangeListenerImpl getListener() {
return new FragmentBackstackChangeListenerImpl(fragmentManager, backstackCallbackImpl);
}
public void apply(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
fragmentManager.addOnBackStackChangedListener(getListener());
}
}
FragmentBackstackChangeListenerImpl.java:
public class FragmentBackstackChangeListenerImpl implements FragmentManager.OnBackStackChangedListener {
private int lastBackStackEntryCount = 0;
private final FragmentManager fragmentManager;
private final BackstackCallback backstackChangeListener;
public FragmentBackstackChangeListenerImpl(FragmentManager fragmentManager, BackstackCallback backstackChangeListener) {
this.fragmentManager = fragmentManager;
this.backstackChangeListener = backstackChangeListener;
lastBackStackEntryCount = fragmentManager.getBackStackEntryCount();
}
private boolean wasPushed(int backStackEntryCount) {
return lastBackStackEntryCount < backStackEntryCount;
}
private boolean wasPopped(int backStackEntryCount) {
return lastBackStackEntryCount > backStackEntryCount;
}
private boolean haveFragments() {
List<Fragment> fragmentList = fragmentManager.getFragments();
return fragmentList != null && !fragmentList.isEmpty();
}
/**
* If we push a fragment to backstack then parent would be the one before => size - 2
* If we pop a fragment from backstack logically it should be the last fragment in the list, but in Android popping a fragment just makes list entry null keeping list size intact, thus it's also size - 2
*
* #return fragment that is parent to the one that is pushed to or popped from back stack
*/
private Fragment getParentFragment() {
List<Fragment> fragmentList = fragmentManager.getFragments();
return fragmentList.get(Math.max(0, fragmentList.size() - 2));
}
#Override
public void onBackStackChanged() {
int currentBackStackEntryCount = fragmentManager.getBackStackEntryCount();
if (haveFragments()) {
Fragment parentFragment = getParentFragment();
//will be null if was just popped and was last in the stack
if (parentFragment != null) {
if (wasPushed(currentBackStackEntryCount)) {
backstackChangeListener.onFragmentPushed(parentFragment);
} else if (wasPopped(currentBackStackEntryCount)) {
backstackChangeListener.onFragmentPopped(parentFragment);
}
}
}
lastBackStackEntryCount = currentBackStackEntryCount;
}
}
BackstackCallback.java:
public interface BackstackCallback {
void onFragmentPushed(Fragment parentFragment);
void onFragmentPopped(Fragment parentFragment);
}
This is the correct answer you can call onResume() providing the fragment is attached to the activity. Alternatively you can use onAttach and onDetach
onResume() for the fragment works fine...
public class listBook extends Fragment {
private String listbook_last_subtitle;
...
#Override
public void onCreate(Bundle savedInstanceState) {
String thisFragSubtitle = (String) getActivity().getActionBar().getSubtitle();
listbook_last_subtitle = thisFragSubtitle;
}
...
#Override
public void onResume(){
super.onResume();
getActivity().getActionBar().setSubtitle(listbook_last_subtitle);
}
...
public abstract class RootFragment extends Fragment implements OnBackPressListener {
#Override
public boolean onBackPressed() {
return new BackPressImpl(this).onBackPressed();
}
public abstract void OnRefreshUI();
}
public class BackPressImpl implements OnBackPressListener {
private Fragment parentFragment;
public BackPressImpl(Fragment parentFragment) {
this.parentFragment = parentFragment;
}
#Override
public boolean onBackPressed() {
((RootFragment) parentFragment).OnRefreshUI();
}
}
and final extent your Frament from RootFragment to see effect
My workaround is to get the current title of the actionbar in the Fragment before setting it to the new title. This way, once the Fragment is popped, I can change back to that title.
#Override
public void onResume() {
super.onResume();
// Get/Backup current title
mTitle = ((ActionBarActivity) getActivity()).getSupportActionBar()
.getTitle();
// Set new title
((ActionBarActivity) getActivity()).getSupportActionBar()
.setTitle(R.string.this_fragment_title);
}
#Override
public void onDestroy() {
// Set title back
((ActionBarActivity) getActivity()).getSupportActionBar()
.setTitle(mTitle);
super.onDestroy();
}
I have used enum FragmentTags to define all my fragment classes.
TAG_FOR_FRAGMENT_A(A.class),
TAG_FOR_FRAGMENT_B(B.class),
TAG_FOR_FRAGMENT_C(C.class)
pass FragmentTags.TAG_FOR_FRAGMENT_A.name() as fragment tag.
and now on
#Override
public void onBackPressed(){
FragmentManager fragmentManager = getFragmentManager();
Fragment current
= fragmentManager.findFragmentById(R.id.fragment_container);
FragmentTags fragmentTag = FragmentTags.valueOf(current.getTag());
switch(fragmentTag){
case TAG_FOR_FRAGMENT_A:
finish();
break;
case TAG_FOR_FRAGMENT_B:
fragmentManager.popBackStack();
break;
case default:
break;
}

Categories

Resources