4

I've written a method, setLoading in my activity that is responsible for setting the "loading" status of my app. This method is responsible for instantiating a LoadingFragment, removing any existing instances of it (Using FragmentManager) and then depending on it's first parameter loading, adding it to one of two possible containers (Depending on the top parameter).

protected LoadingFragment loadingFragment;

public void setLoading(boolean loading, boolean top) {
    FragmentManager fragmentManager = getFragmentManager();

    // Create LoadingFragment instance if it has not already been created
    if (loadingFragment == null || !(loadingFragment instanceof LoadingFragment)) {
        loadingFragment = new LoadingFragment();
    }

    // Remove the fragment first if it is present
    fragmentManager
            .beginTransaction()
            .remove(loadingFragment)
            .commit();

    // Only if loading is true should we display the fragment
    if (loading) {
        // Decide which container we're going to put the fragment in
        int id = top ? R.id.topContainer : R.id.container;

        // Place the fragment in the right position
        fragmentManager
                .beginTransaction()
                .add(id, loadingFragment)
                .commit();
    }
}

public void setLoading(boolean loading) {
    setLoading(loading, true);
}

I am triggering setLoading(true) from elsewhere in my activity and I have commented out it's corresponding setLoading(false) while testing.

What I want to happen is for my LoadingFragment to appear every time setLoading(true) is called. The first call shouldn't remove anything since it at that point it doesn't exist. All subsequent calls should remove the existing LoadingFragment and add it again.

What happens is that the first call to setLoading(true) does indeed create the LoadingFragment and put it in the correct container. However, subsequent calls to setLoading(true) remove the fragment, but it never seems to be re-added. I have checked to see that the fragment does indeed exist and is of type LoadingFragment at the point it is added and I have also checked to ensure that it's onCreateView method is being called.

Am I doing something wrong?

Edit

Using the answer given below by H Raval as a base I have now come up with the following:

public void setLoading(boolean loading, boolean top) {
    FragmentManager fragmentManager = getFragmentManager();

    Fragment currentLoadingFragment = fragmentManager.findFragmentById(R.id.loadingFragment);
    if (currentLoadingFragment != null) {
        fragmentManager
                .beginTransaction()
                .remove(currentLoadingFragment)
                .commit();
    }

    if (loading) {
        int id = top ? R.id.topContainer : R.id.container;

        fragmentManager
                .beginTransaction()
                .add(id, new LoadingFragment())
                .commit();
    }
}

This seems to work as expected. It seems that the primary difference is that this code is creating a new LoadingFragment instance each time (When loading = true) whereas originally I was trying to use the same instance and just adding/removing it using the FragmentManager.

Out of interest, is there a reason I need to create a new instance after using remove? Is this the correct way to do it? Or should it still work when using the same instance? Additionally, if it's recommended to create a new instance each time, is there anything I should do in terms of clean-up, freeing up resources etc. (Perhaps there's a way of gracefully destroying the obsolete instances)?

Jonathon
  • 15,873
  • 11
  • 73
  • 92
  • rather than using add method of fragment manager user replace – H Raval Mar 01 '16 at 08:05
  • I would do that however, I don't know prior to calling the method whether or not the fragment already exists. Not only that, my second parameter allows me to put the fragment into a different container as noted by the line `int id = top ? R.id.topContainer : R.id.container;`. Therefore, I don't think `replace` would be the way to go – Jonathon Mar 01 '16 at 08:07
  • well with replace you can set tag of fragment...and at adding time you can test whether tagged fragment exist...check this http://stackoverflow.com/questions/9294603/get-currently-displayed-fragment – H Raval Mar 01 '16 at 08:13
  • You can also set a tag using the `add` method. You're right that I can check if it exists, I can also do that using `fragmentManager.findFragmentById()`. However, I don't think that solves my issue. My problem is that when I re-add the fragment it is not visible. I would gladly have used the `replace` method, but as mentioned, I don't always want to put the fragment in the same container (My second parameter allows for an alternative) – Jonathon Mar 01 '16 at 08:18
  • Of course, if you know a way I can achieve the result I'm after, by using replace, by all means post an answer and I'll accept if it works. – Jonathon Mar 01 '16 at 08:20
  • You may be better off using show/hide in this instance or perhaps attach/detach. I have no idea why the remove and add doesn't work but it could be due to some weird internal backstack management code. Can you try and step into the remove and the subsequent add methods (android code) and see what's going on? – kha Mar 01 '16 at 09:04

1 Answers1

1

well i have made some changes in your code and works perfect for me..let me know if you face any difficulty

public void loadFragment(boolean loading, boolean top){
        FragmentManager fragmentManager = getSupportFragmentManager();

          loadingFragment = new LoadingFragment();

        // Only if loading is true should we display the fragment
        if (loading) {
            // Decide which container we're going to put the fragment in
            int id = top ? R.id.topContainer : R.id.container;

            if(top){
                if(fragmentManager.findFragmentByTag("loadingFragment")!=null)
                    fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("loadingFragment")).commit();

                fragmentManager
                        .beginTransaction()
                        .replace(R.id.topContainer, loadingFragment,"toploadingFragment")
                        .commit();
            }else{
                if(fragmentManager.findFragmentByTag("toploadingFragment")!=null)
                    fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("toploadingFragment")).commit();

                fragmentManager
                        .beginTransaction()
                        .replace(R.id.container, loadingFragment,"loadingFragment")
                        .commit();
            }

        }
H Raval
  • 1,903
  • 17
  • 39
  • Thanks for your answer. Yours does work to some degree (Though it doesn't take care of removing the fragment when `loading = false`). I've had a mess around myself using your code as a base and I have found that the primary difference which causes this to work is that you're creating a new `LoadingFragment` instance each time whereas I was trying to use the same instance. My code uses `findFragmentById` instead of `findFragmentByTag` and it uses `add`/`remove` rather than `replace`. Everything still works as expected. See my edit for details. – Jonathon Mar 01 '16 at 09:56
  • you have to create new instance each time because if you use same instance than you cant attach same fragment with different id...it will throw an exception....check this http://stackoverflow.com/questions/9906254/illegalstateexception-cant-change-container-id-of-fragment – H Raval Mar 01 '16 at 10:03