Dominik Suszczewicz
Dominik Suszczewicz

Reputation: 1499

Android detect when fragment is detached

I can easily detect when Fragments are attached to Activity via Activity.onAttachFragment()

But how can I detect in Activity that some Fragment is detached from activity? There is no Activity.onDetachFragment()

Is subcclasing Fragment and write some code to notify Activity about that state is the only solution?

Upvotes: 7

Views: 15651

Answers (5)

Cagdas
Cagdas

Reputation: 111

You can use ViewModel for update host activity. Shared ViewModel could be better choice than across the old listener based polymorphism model. You can follow the official documentation.

Data, fragment lifecyle etc. can be observable with shared viewmodel.

sealed class FragmentStates {
    object Attached : FragmentStates()
    object Started : FragmentStates()
    object Stopped : FragmentStates()
    object DeAtached : FragmentStates()
}

class FragmentStateViewModel : ViewModel() {
    private val _fragmentState = MutableLiveData<FragmentStates>()
    val fragmentStates: LiveData<FragmentStates> get() = _fragmentState

    fun fragmentAttached() {
        _fragmentState.value = FragmentStates.Attached
    }

    fun fragmentDeAtached() {
        _fragmentState.value = FragmentStates.DeAtached
    }

}

class HostActivity : AppCompatActivity() {
    private val fragmentStateViewModel: FragmentStateViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        fragmentStateViewModel.fragmentStates.observe(this, Observer {
          when(it) {
              FragmentStates.Attached -> {}
              FragmentStates.Started -> {}
              FragmentStates.Stopped -> {}
              FragmentStates.DeAtached -> {}
          }
        })
    }
}

class MyFragment: Fragment() {
    private val fragmentStateViewModel: FragmentStateViewModel by activityViewModels()

    override fun onAttach(context: Context) {
        super.onAttach(context)
        fragmentStateViewModel.fragmentAttached()
    }

    override fun onDetach() {
        super.onDetach()
        fragmentStateViewModel.fragmentDeAtached()
    }
}

Upvotes: 0

KaRa
KaRa

Reputation: 59

An alternative would be:

mFragmentManager.findFragmentByTag("Tag").getView()

If the view is null the fragment must be detached.

Upvotes: 1

methodsignature
methodsignature

Reputation: 4402

Mohammad's original answer is close to I would do. He has since updated it to leverage a mechanism provided by Android - Fragment.onAttach(Context context).In that approach, the fragment grabs components (ie, the activity) from the system and calls into it. This breaks inversion of control.

Here is my preferred approach:

public class MyActivity extends AppCompatActivity {

    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);
        if (fragment instanceof MyFragment) {
            ((MyFragment) fragment).setListener(mMyFragmentListener);
        }
    }

    private final MyFragment.Listener mMyFragmentListener = new MyFragment.Listener() {
        @Override
        public void onDetached(MyFragment fragment) {
            fragment.setListener(null);
        }

        // implement other worker methods.
    };
}

public class MyFragment extends Fragment {

    @Nullable
    private Listener mListener;

    public void setListener(@Nullable Listener listener) {
        mListener = listener;
    }

    public interface Listener {
        void onDetached(MyFragment fragment);

        // declare more worker methods here that leverage the connection.
    }

    @Override
    public void onDetach() {
        super.onDetach();

        if (mListener != null) {
            mListener.onDetached(this);
        }
    }
}

In this solution, the fragment doesn't dictate it's surroundings. Some control is given the to fragment in that it breaks the connection itself. We also already don't own the detaching of the fragment anyways, so clearing the listener is really just cleanup.

Here is an alternative approach that is more explicit, less prone to developer error, but also creates extra boiler plate (I prefer the previous approach because the goodbye handshake feels like an unnecessary distraction):

public static class MyActivity extends AppCompatActivity {

    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);
        if (fragment instanceof MyFragment) {
            ((MyFragment) fragment).setListener(mMyFragmentListener);
        }
    }

    private final MyFragment.Listener mMyFragmentListener = new MyFragment.Listener() {
        @Override
        public void onDetached(MyFragment fragment) {
            fragment.setListener(null);
        }

        // implement other worker methods.
    };
}

public static class MyFragment extends Fragment {

    @Nullable
    private Listener mListener;

    public void setListener(@Nullable Listener listener) {
        mListener = listener;
    }

    public interface Listener {
        void onDetached(MyFragment fragment);

        // declare more worker methods here that leverage the connection.
    }

    @Override
    public void onDetach() {
        super.onDetach();

        if (mListener != null) {
            mListener.onDetached(this);
        }
    }
}

Upvotes: 2

Mohammad Rahchamani
Mohammad Rahchamani

Reputation: 5220

you can use interface for communicating between Fragment and Activity

something like this :

  public Class MyFragment extends Fragment {
    FragmentCommunicator communicator;
    public void setCommunicator(FragmentCommunicator communicator) {
      this.communicator = communicator;
    }
    @Override
    public void OnDetach() {
      communicator.fragmentDetached();
    }
    ...

     public Interface FragmentCommunicator {
        public void fragmentDetached();
     }
  }

and in your activity :

 public Class MyActivity extends Activity Implements FragmentCommunicator {
   ...
   MyFragment fragment = new MyFragment();
   fragment.setCommunicator(this);
   ...
   @Override 
   public void fragmentDetached() {
       //Do what you want!
   }
 }

Edit:

the new approach is setting interface instance in onAttach.

public void onAttach(Activity activity) {
    if (activity instanceof FragmentCommunicator) {
        communicator = activity;
    } else {
        throw new RuntimeException("activity must implement FragmentCommunicator");
    }
}

now there is no need to have setCommunicator method.

Upvotes: 11

N Sharma
N Sharma

Reputation: 34507

You have a callback in the fragment life cycle. onDetach() is called when fragment is no longer attached to activity.

Upvotes: 1

Related Questions