7

I am maintaining a backstack of fragments and pop the stack when back button is pressed. I need to reload data every time a fragment is made visible and do some cleanup when it gets hidden. For this I need to detect when a fragment is shown and hidden. This is a very common question but surprisingly none of accepted answers work for me. I

I am adding fragments to backstack using code like this:

public void pushFragment(Fragment f) {
    getFragmentManager().beginTransaction()
            .add(R.id.content_frame, f, null)
            .addToBackStack(null)
            .commit();
}

I am popping fragments off using this code:

public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() > 1) {
        getFragmentManager().popBackStack();
    } else {
        super.onBackPressed();
    }
}

I am trying to detect from a fragment class when it becomes visible or hidden (either because it is being popped off the stack or another fragment was pushed on top). So far I have tried these callbacks:

  • onViewCreated/onDestroyView: Only called when the fragment is added to stack and popped off the stack. Not called when the fragment gets hidden or visible because of other fragments on the stack.
  • onHiddenChanged: Never called. Many have said on SO that this works. But not working for me for some reason.
  • setUserVisibleHint: Never called
  • onStart/onPause etc: They don't really apply here because they simply reflect the lifecycle of the host activity.

Is there a Fragment callback that will let me detect when a fragment is being shown or hidden? I will rather not use a backstack listener because I want every fragment class to have its own show/hide logic.

Edit: If I use replace() to add the fragment (instead of add()) then the view for the previously shown fragment gets destroyed. As a result if that fragment ever to appear on top of the stack again its view is recreated. In this situation onViewCreated/onDestroyView or onStart/onStop will be called every time a fragment is shown or hidden. I suppose I could use that approach. The down side is that the views are created and destroyed frequently. I might as well be using activities instead of fragments for master-detail navigation in that case.

RajV
  • 6,860
  • 8
  • 44
  • 62

3 Answers3

4

Edit again If you need a callback you can override onHiddenChanged like this:

public void onHiddenChanged(boolean hidden) {
if(hidden){// Do you stuff here}
} 

As PPartisan mentioned you claimed that onHiddenChanged is never called.
The reason for that is because onHiddenChanged doesn't get called the first time an fragment is shown.

Called when the hidden state (as returned by isHidden()) of the fragment has changed. Fragments start out not hidden; this will be called whenever the fragment changes state from that.

To fix this: add this to your code:

FragmentTransaction mFragmentTransaction = getFragmentManager().beginTransaction();
if (f!= null) {
        mFragmentTransaction .hide(f);
    }
mFragmentTransaction.add(R.id.content_frame, f, null)
mFragmentTransaction.addToBackStack(null)
mFragmentTransaction.commit();

More on the on this SO thread

Community
  • 1
  • 1
Nir Duan
  • 6,164
  • 4
  • 24
  • 38
  • When do I call isVisible? My fragment classes need to get notified when they are getting hidden or visible. I need a callback mechanism. – RajV Aug 30 '16 at 14:07
  • He did say in his question that `onHiddenChanged()` wasn't being called. – PPartisan Aug 30 '16 at 14:25
  • onHiddenChanged is never getting called. I already tried this as I have said in my question. – RajV Aug 30 '16 at 14:34
  • @PPartisan Thanks for let me know, hopped it fixed. – Nir Duan Aug 30 '16 at 14:38
  • @RajV fixed again* – Nir Duan Aug 30 '16 at 14:38
  • So basically you are calling hide(f) before adding a fragment to the backstack. If I do that onHiddenChanged(true) gets called. But the fragment appears as invisible (because we hid it). Also, if the fragment gets popped from the stack onHiddenChanged does not get called again. This is not a very workable solution. – RajV Aug 30 '16 at 15:10
-1

From the Android documentation:

void onStart () Called when the Fragment is visible to the user. This is generally tied to Activity.onStart of the containing Activity's lifecycle.

void onStop () Called when the Fragment is no longer started. This is generally tied to Activity.onStop of the containing Activity's lifecycle.

The correct way is using Fragment Lifecycle's methods.

josemigallas
  • 3,761
  • 1
  • 29
  • 68
  • 1
    This is not entirely true. onStart/onStop/onResume/onPause of fragments are tied to the lifecycle of the host activity. For example, if a fragment gets hidden because another one was pushed on top of the stack then onStop or onPause does not get called. But if you open another activity then they will get called. – RajV Aug 30 '16 at 15:03
-2

This is get called then the fragment is changing visibility

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {}
Antonios Tsimourtos
  • 1,676
  • 1
  • 14
  • 37
  • setUserVisibleHint is never getting called. I already tried this as I have said in my question. – RajV Aug 30 '16 at 14:34