Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a Fragment to remove itself, i.e. its equivalent of finish()?

Tags:

android

I'm converting an app to use fragments using the compatibility library. Now currently I have a number of activities (A B C D) which chain onto one another, D has a button 'OK' which when pressed calls finish which then bubbles up through onActivityResult() to additionally destroy C and B.

For my pre Honycomb fragment version each activity is effectively a wrapper on fragments Af Bf Cf Df. All activities are launched via startActivityForResult() and onActivityResult() within each of the fragments can happily call getActivity().finish()

The problem that I am having though is in my Honeycomb version I only have one activity, A, and fragments Bf, Cf, Df are loaded using the FragmentManager.

What I don't understand is what to do in Df when 'OK' is pressed in order to remove fragments Df, Cf, and Bf?

I tried having the fragment popping itself off the stack but this resulted in an exception. onActivityResult() is useless because I have not loaded up the fragment using startActivityForResult().

Am I thinking about this completely the wrong way? Should I be implementing some sort of listener that communicates with either the parent fragment or activity in order to do the pop using the transaction manager?

like image 547
PJL Avatar asked May 05 '11 16:05

PJL


People also ask

Does popBackStack remove fragment?

popBackStack() will remove current fragment and replace it with the last one from the stack. For this to work you need to do addToBackstack() on the last fragment transaction.

How do I remove a fragment from a transaction?

To remove a fragment from the host, call remove() , passing in a fragment instance that was retrieved from the fragment manager through findFragmentById() or findFragmentByTag() . If the fragment's view was previously added to a container, the view is removed from the container at this point.

How do you stop a fragment from recreating?

Calling setRetainInstance(true) will prevent the Fragment from being re-created.


10 Answers

While it might not be the best approach the closest equivalent I can think of that works is this with the support/compatibility library

getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();

or

getActivity().getFragmentManager().beginTransaction().remove(this).commit();

otherwise.

In addition you can use the backstack and pop it. However keep in mind that the fragment might not be on the backstack (depending on the fragmenttransaction that got it there..) or it might not be the last one that got onto the stack so popping the stack could remove the wrong one...

like image 129
Manfred Moser Avatar answered Oct 01 '22 03:10

Manfred Moser


You can use the approach below, it works fine:

getActivity().getSupportFragmentManager().popBackStack();
like image 37
Eric Yuan Avatar answered Oct 01 '22 04:10

Eric Yuan


What I don't understand is what to do in Df when 'OK' is pressed in order to remove fragments Df, Cf, and Bf?

Step #1: Have Df tell D "yo! we got the OK click!" via calling a method, either on the activity itself, or on an interface instance supplied by the activity.

Step #2: Have D remove the fragments via FragmentManager.

The hosting activity (D) is the one that knows what other fragments are in the activity (vs. being in other activities). Hence, in-fragment events that might affect the fragment mix should be propagated to the activity, which will make the appropriate orchestration moves.

like image 40
CommonsWare Avatar answered Oct 01 '22 05:10

CommonsWare


You should let the Activity deal with adding and removing Fragments, as CommonsWare says, use a listener. Here is an example:

public class MyActivity extends FragmentActivity implements SuicidalFragmentListener {

    // onCreate etc

    @Override
    public void onFragmentSuicide(String tag) {
        // Check tag if you do this with more than one fragmen, then:
        getSupportFragmentManager().popBackStack();
    }
}

public interface SuicidalFragmentListener {
    void onFragmentSuicide(String tag);
}

public class MyFragment extends Fragment {

    // onCreateView etc

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
           suicideListener = (SuicidalFragmentListener) activity;
        } catch (ClassCastException e) {
           throw new RuntimeException(getActivity().getClass().getSimpleName() + " must implement the suicide listener to use this fragment", e);
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // Attach the close listener to whatever action on the fragment you want
        addSuicideTouchListener();
    }

    private void addSuicideTouchListener() {
        getView().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              suicideListener.onFragmentSuicide(getTag());
            }
        });
    }
}
like image 25
Blundell Avatar answered Oct 01 '22 04:10

Blundell


In the Activity/AppCompatActivity:

@Override
public void onBackPressed() {
    if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
        // if you want to handle DrawerLayout
        mDrawerLayout.closeDrawer(GravityCompat.START);
    } else {
        if (getFragmentManager().getBackStackEntryCount() == 0) {
            super.onBackPressed();
        } else {
            getFragmentManager().popBackStack();
        }
    }
}

and then call in the fragment:

getActivity().onBackPressed();

or like stated in other answers, call this in the fragment:

getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
like image 27
Codeversed Avatar answered Oct 01 '22 05:10

Codeversed


See if your needs are met by a DialogFragment. DialogFragment has a dismiss() method. Much cleaner in my opinion.

like image 31
Vasudev Avatar answered Oct 01 '22 04:10

Vasudev


If you are using the new Navigation Component, is simple as

findNavController().popBackStack()

It will do all the FragmentTransaction in behind for you.

like image 45
Gastón Saillén Avatar answered Oct 01 '22 04:10

Gastón Saillén


I create simple method for that

popBackStack(getSupportFragmentManager());

Than place it in my ActivityUtils class

public static void popBackStack(FragmentManager manager){
        FragmentManager.BackStackEntry first = manager.getBackStackEntryAt(0);
        manager.popBackStack(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

It's work great, have fun!

like image 32
Eliasz Kubala Avatar answered Oct 01 '22 04:10

Eliasz Kubala


OnCreate:

//Add comment fragment
            container = FindViewById<FrameLayout>(Resource.Id.frmAttachPicture);
            mPictureFragment = new fmtAttachPicture();

            var trans = SupportFragmentManager.BeginTransaction();
            trans.Add(container.Id, mPictureFragment, "fmtPicture");
            trans.Show(mPictureFragment); trans.Commit();

This is how I hide the fragment in click event 1

//Close fragment
    var trans = SupportFragmentManager.BeginTransaction();
    trans.Hide(mPictureFragment);
    trans.AddToBackStack(null);
    trans.Commit();

Then Shows it back int event 2

var trans = SupportFragmentManager.BeginTransaction();
            trans.Show(mPictureFragment); trans.Commit();
like image 1
CodeSi Avatar answered Oct 01 '22 04:10

CodeSi


If you need to popback from the fourth fragment in the backstack history to the first, use tags!!!

When you add the first fragment you should use something like this:

getFragmentManager.beginTransaction.addToBackStack("A").add(R.id.container, FragmentA).commit() 

or

getFragmentManager.beginTransaction.addToBackStack("A").replace(R.id.container, FragmentA).commit()

And when you want to show Fragments B,C and D you use this:

getFragmentManager.beginTransaction.addToBackStack("B").replace(R.id.container, FragmentB, "B").commit()

and other letters....

To return to Fragment A, just call popBackStack(0, "A"), yes, use the flag that you specified when you add it, and note that it must be the same flag in the command addToBackStack(), not the one used in command replace or add.

You're welcome ;)

like image 1
Diego Ramírez Vásquez Avatar answered Oct 01 '22 03:10

Diego Ramírez Vásquez