ChatGPT解决这个技术问题 Extra ChatGPT

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

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?

what about ((YourActivity) getActivity()).onBackPressed();
@ViswanathLekshmanan your comment answer is useful for me.. 1 Upvote from me
@AbhishekSingh Good to hear :)
@ViswanathLekshmanan :)

M
Manfred Moser

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...


While this approach works, if you are using addToBackStack(null) it will leave the back button handler +1. So you'll have to press it twice.
"pop" the fragment from the FragmentManager.
I have tried the above procedure but it is giving this error "java-lang-illegalstateexception-can-not-perform-this-action-after-onsaveinstance". So where exactly i have to remove the fragment
This answer is a bad practice and should not be getting up votes. Fragments are not meant to be self-aware like this. It kills reusability, which is the point of fragments! The fragment should signal the activity to remove it through any number of means. The callback interface method is a popular choice. developer.android.com/training/basics/fragments/…
@ManfredMoser I disagree. This is very much the point of the question. He has an entire sequence of fragments to remove. This code has no null checks or checks for whether the activity is attached. It will break in production because it depends upon too many things a fragment does not know.
C
Carsten

You can use the approach below, it works fine:

getActivity().getSupportFragmentManager().popBackStack();

This answer is like 10 times better than the accepted one - straight to the point.
It's also 10 times worse with regards to design than the accepted one. A fragment is supposed to be a small "helper" to an activity and should never bei in control over itself or other fragments
The solution is not correct as @avalancha pointed out. Have a look at developer.android.com/guide/components/…
I am using this method onActivityResult and getting error "Can not perform this action after onSaveInstanceState". How can I resolve it?
popBackStack() is the only solution which works when you want to set the action bar title back to the previous state after you delete the fragment. Otherwise I wouldn't need to combine the both high rated solutions from stackoverflow.com/questions/13472258/… and this solution here to always set the proper action bar title after various use cases. like back pressing, deleting, adding replacing, and so on.
C
CommonsWare

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.


But in my Honeycomb version there is no D, that's my difficultly. There is simply an activiy A which loads fragment Bf, which loads Cf, which loads Df using FragmentTransaction.
@PJL: Sorry, I meant A. This is one reason to use a listener interface, so multiple activities can all respond to the "we got the OK click" event from Df.
As I'm currently porting I called a listener method from fragments Df's onActivityResult method into the activity whereupon I then called popBackStack on the FragmentManager. However, this results in an exeption "IllegalStateException: Can not perform this action after onSaveInstanceState'. Any ideas as to how I can overcome this?
@DiegoPalomar: finish() should suffice.
@user3364963: It has been a while since I investigated that, but IIRC, it is destroyed when it is popped off the back stack. Add an onDestroy() method to your fragment and see if it gets called.
B
Blundell

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());
            }
        });
    }
}

Suicide much emo style? How about "SelfClosing" or "AutoClose" or "SmartClose"(r)
it's not closing it's DYING FOREVER ;-(
This is a much cleaner approach than the other answers. The activity creates and presents the fragment, and should control its lifecycle. When something happens that indicates the fragment should no longer be in view, it should tell the Activity that and let the activity remove it.
Technically, if we are to supposed to have the activity kill the fragment, then the fragment isn't suicidal. The activity is homicidal.
C
Codeversed

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();

G
Gastón Saillén

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

findNavController().popBackStack()

It will do all the FragmentTransaction in behind for you.


V
Vasudev

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


E
Eliasz Kubala

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!


I don't get the purpose of your method. The original popBackStack seems completely adequate.
C
CodeSi

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();

x
xarlymg89

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 ;)


I have tested 'popBackStack(0, "A")' and my app comes back to fragment A, but I want only that fragment would be removed from Back Stack...How can I remove fragment from Stack without showing in screen??
D
Dino Sunny

To Close a fragment while inside the same fragment

getActivity().onBackPressed();

kotlin -

requireActivity().onBackPressed()

N
Nicholas Chen
parentFragmentManager.apply {
    val f = this@MyFragment
    beginTransaction().hide(f).remove(f).commit()
}

Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes.
W
Will

Why not just:

getActivity().finish();


This will finish the whole activity not just the fragment.