How to implement onBackPressed() in Fragments?
Asked Answered
E

58

635

Is there a way in which we can implement onBackPressed() in Android Fragment similar to the way in which we implement in Android Activity?

As the Fragment lifecycle do not have onBackPressed(). Is there any other alternative method to over ride onBackPressed() in Android 3.0 fragments?

Eastman answered 27/3, 2011 at 10:30 Comment(8)
IMHO, a fragment should neither know nor care about the BACK button. An activity could care about the BACK button, though with fragments that is normally handled by FragmentManager. But since the fragment does not know about its larger environment (e.g., whether or not it is one of several in the activity), it is not really safe for a fragment to be trying to determine what proper BACK functionality is.Haworth
How about when all the fragment does it to display a WebView, and you want the WebView to "go back" to the previous page when the back button is pressed?Neddra
Michael Herbig answer was perfect for me. But for those who cant use getSupportFragmentManager(). Use getFragmentManager() instead.Metasomatism
Dmitry Zaitsev's answer on [Similar Question][1] works just fine. [1]: https://mcmap.net/q/65164/-android-fragment-handle-back-button-press-duplicateSillsby
to answer mharper may be you can do something like webview.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack()) { webview.goBack(); return true; } return false; } });Flurry
I know this post is very old but see my answer: https://mcmap.net/q/63955/-how-to-implement-onbackpressed-in-fragmentsMaher
Visit this thread, it may fulfill your need. #20340803Heteromorphic
Hi, there is a much much better and cleaner approach. Also documented by the official documentation: developer.android.com/guide/navigation/navigation-custom-backVanzandt
A
352

Override onBackPressed in the Activity. All the FragmentTransaction are addToBackStack before commit:

@Override
public void onBackPressed() {

    int count = getSupportFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();
        //additional code
    } else {
        getSupportFragmentManager().popBackStack();
    }

}
Anchorite answered 22/7, 2014 at 8:4 Comment(9)
same issue #32133123Chartreuse
count == 1 will allow to close the first fragment on single back button press. But otherwise the best solutionErnestoernestus
If you're using the support v7 library and your Activity extends from FragmentActivity (or a subclass, such as AppCompatActivity) this will happen by default. See FragmentActivity#onBackPressedEmmenagogue
but you can't use variables, classes & functions from fragment's class in this codeLycurgus
@Prabs if you are using support v4 fragment then make sure you are using getSupportFragmentManager().getBackStackEntryCount();Resect
I know this post is very old. but see my answer: https://mcmap.net/q/63955/-how-to-implement-onbackpressed-in-fragmentsMaher
Only if do not fragments have a focusable view (such as edit text), let fragments handle back button itself as the second answer does. Otherwise, let activity containing fragments do it by OnBackPress() as this answer is written; since focusable view will steal fragment's focus. However, we can regain focus for parent view by clear focus all focusable views by clearFocus() method and re-focus to the fragment.Bewray
@Lycurgus Couldn't you just check the current id or tag of the fragment on the backstack, and then use that to determine which method to call of the Fragment in question?Theroid
Thank you so much Sir. Your answer working to me .You save my time.Catarrhine
O
222

In my opinion the best solution is:

JAVA SOLUTION

Create simple interface :

public interface IOnBackPressed {
    /**
     * If you return true the back press will not be taken into account, otherwise the activity will act naturally
     * @return true if your processing has priority if not false
     */
    boolean onBackPressed();
}

And in your Activity

public class MyActivity extends Activity {
    @Override public void onBackPressed() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
       if (!(fragment instanceof IOnBackPressed) || !((IOnBackPressed) fragment).onBackPressed()) {
          super.onBackPressed();
       }
    } ...
}

Finally in your Fragment:

public class MyFragment extends Fragment implements IOnBackPressed{
   @Override
   public boolean onBackPressed() {
       if (myCondition) {
            //action not popBackStack
            return true; 
        } else {
            return false;
        }
    }
}

KOTLIN SOLUTION

1 - Create Interface

interface IOnBackPressed {
    fun onBackPressed(): Boolean
}

2 - Prepare your Activity

class MyActivity : AppCompatActivity() {
    override fun onBackPressed() {
        val fragment =
            this.supportFragmentManager.findFragmentById(R.id.main_container)
        (fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let {
            super.onBackPressed()
        }
    }
}

3 - Implement in your target Fragment

class MyFragment : Fragment(), IOnBackPressed {
    override fun onBackPressed(): Boolean {
        return if (myCondition) {
            //action not popBackStack
            true
        } else {
            false
        }
    }
}
Outsail answered 26/9, 2017 at 11:34 Comment(13)
What is R.id.main_container? Is that the ID for the FragmentPager?Chalcopyrite
I answered my own question by testing, it is the FragmentPager's ID.Chalcopyrite
@MaximeJallu Is there a way to make this solution work when you're using Jetpack? AFAIK the equivalent of the FragmentPager when using Jetpack's Navigation Architecture Component is the NavHostFragment, but that doesn't provide a way to get at the fragment itself. (see #50689706)Pigmy
Shouldn't it be .onBackPressed()?.takeIf { !it }?.let{...}? .not() just returns the inverse.Orbicular
I think you should add ?: super.onBackPressed() after (fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let blockInsurer
val fragment = this.supportFragmentManager.findFragmentById(R.id.flContainer) as? NavHostFragment val currentFragment = fragment?.childFragmentManager?.fragments?.get(0) as? IOnBackPressed currentFragment?.onBackPressed()?.takeIf { !it }?.let{ super.onBackPressed() } This is for people who use Kotlin and NavigationControllerDeviled
@Maxime Jallu , Why do you need an interface here? Why can't you just write a public method inside the fragment and call it like fragment.onBackPressed() ?Literator
@MidhunVijayakumar The interface serves to limit the access of the object. Otherwise you would have to cast in "BaseFragment" for example, or worse in "MyFeatureFragment". You do not want to give access to any of the public methods, just the onBackPressed, and the perfect interface to do that.Outsail
@MaximeJallu, I want to restrict only 3 fragments in my stack to implement, backPress to exit strategy. Do I want to implement your solution in all fragments with a true/false logic? OR only at those 3 fragments ?Liner
@Liner Hello, you just need to implement your strategy only on the 3 desired fragments. (fragment as? IOnBackPressed)Outsail
@MaximeJallu, i did your solution on the fragments but it seems backbutton is freezed for all fragments, like a circular reference kind. Does Dagger will interefer with it. I use MVVMLiner
If you don't want to implement it for all the fragments in the activity. override fun onBackPressed() { val fragment = this.supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment val currentFragment = fragment?.childFragmentManager?.fragments?.get(0) as? FragmentOnBackPressListener if (currentFragment != null) { currentFragment.onBackPressed().takeIf { !it }?.let { super.onBackPressed() } } else { super.onBackPressed() } }Kauppi
is there somebody to explain what is myCondition and write this line simpler "(fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let{ super.onBackPressed() } " ?Confess
A
211

If you're using androidx.appcompat:appcompat:1.1.0 or above then you can add an OnBackPressedCallback to your fragment as follows

requireActivity()
    .onBackPressedDispatcher
    .addCallback(this, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            Log.d(TAG, "Fragment back pressed invoked")
            // Do custom work here    

            // if you want onBackPressed() to be called as normal afterwards
            if (isEnabled) {
                isEnabled = false
                requireActivity().onBackPressed()
            }
        }
    }
)

See https://developer.android.com/guide/navigation/navigation-custom-back

Appetizer answered 10/10, 2019 at 12:5 Comment(9)
If you are using androidx-core-ktx, you can use requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { /* code to be executed when back is pressed */ }Unclinch
It's important to note that the LifecycleOwner param should be added as in this example. Without it, any fragments started afterwards will call handleBackPressed() if the back button is pressed.Bounce
is this method must with the navigation library?Meaty
That's what i'm talking about!! So many ancient answers that make me go "you're kidding me right?" now that google recommends fragments over activities.Mallorca
change "this" for "viewLifecycleOwner" for the first param of addCallback method and you are done! This way you dont have to be worried about unregister the callback cause the callback would be attached to the activity lifecycle.Sideswipe
This is the simplest solution for the problem.Bellebelleek
It works only if classic onBackPressed method is NOT used!Ronnyronsard
Make sure to disable handler before calling requireActivity().onBackPressed(), otherwise you will stack in infinite loop. isEnabled = falseThain
@HaykNahapetyan isEnabled = false will disable the callback.Seadon
O
104

According to @HaMMeRed answer here is pseudocode how should it works. Lets say that your main activity is called BaseActivity which has child fragments (like in SlidingMenu lib example). Here are the steps:

First we need create interface and class which implements its interface to have generic method

  1. Create class interface OnBackPressedListener

    public interface OnBackPressedListener {
        public void doBack();
    }
    
  2. Create class which implements skills of OnBackPressedListener

    public class BaseBackPressedListener implements OnBackPressedListener {
        private final FragmentActivity activity;
    
        public BaseBackPressedListener(FragmentActivity activity) {
            this.activity = activity;
        }
    
        @Override
        public void doBack() {
            activity.getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
    }
    
  3. Since now, we will work on our code BaseActivity and its fragments

  4. Create private listener on top of your class BaseActivity

    protected OnBackPressedListener onBackPressedListener;
    
  5. create method to set listener in BaseActivity

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }
    
  6. in override onBackPressed implement something like that

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
    
  7. in your fragment in onCreateView you should add our listener

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        activity = getActivity();
    
        ((BaseActivity)activity).setOnBackPressedListener(new BaseBackPressedListener(activity));
    
        View view = ... ;
    //stuff with view
    
        return view;
    }
    

Voila, now when you click back in fragment you should catch your custom on back method.

Outcross answered 2/10, 2013 at 9:9 Comment(6)
If more than one fragment is a listener, how do you determine, in onBackPressed(), which fragment was being displayed when the back button was pressed?Simard
you can customize click listener instead new baseBackPressedListener and call there as anonymous to set own behaviuor, something like this: ((BaseActivity)activity).setOnBackPressedListener(new OnBackpressedListener(){ public void doBack() { //...your stuff here }});Outcross
Thanks, I have changed my position on this though, I'd recommend decoupling with local broadcast messages now though. I think it would yield higher quality components that are highly decoupled.Raneeraney
isue here #32133123Chartreuse
This worked perfectly for me, I do restart the ActivityExplication
Don't do this. You will leak your activity. The Fragment is maintaining reference to the Activity via the listener.Ario
G
101

This worked for me: https://mcmap.net/q/65168/-fragment-pressing-back-button

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

    if(getView() == null){
        return;
    }

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {

            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
                // handle back button's click listener
                return true;
            }
            return false;
        }
    });
}
Giffer answered 20/3, 2015 at 12:53 Comment(4)
Worked flawlessly! But then you also have to handle the default action, by writing one more IF condition probably. Since, I was lowering down my slideUpPanel if it was Expanded. So, I first had to mark a check if(panel is up) then.....Actinotherapy
The problem with this solution is that the getView() returns only the main view (and not the subview, if they gain focus). So if you have an EditText and type some text then press "back" once to hide the keyboard, the second press on "back" is initiated from the "EditText" (a view itself) and this method does not seem to catch the "back" button press then (since it is only catching for the main view). As I understand it, you could catch the keypress events on your "EditText" as well as any other views, but if you have a "complex" form then this isn't as clean and maintainable as it could be.Cobble
This is a great simple solution for the nightmare that is fragments. If you have a 'complex' edittext form then you should probably delete your project and start over. Return false when you want to use the standard behaviour. Return true when you have intercepted the back press and have done something with itGodric
has some minuses, but still NICE solution.Agrostology
R
67

If you wanted that sort of functionality you would need to override it in your activity, and then add a YourBackPressed interface to all your fragments, which you call on the relevant fragment whenever the back button is pressed.

Edit: I'd like to append my previous answer.

If I were to do this today, I'd use a broadcast, or possibly a ordered broadcast if I expected other panels to update in unison to the master/main content panel.

LocalBroadcastManager in the Support Library can help with this, and you just send the broadcast in onBackPressed and subscribe in your fragments that care. I think that Messaging is a more decoupled implementation and would scale better, so it would be my official implementation recommendation now. Just use the Intent's action as a filter for your message. send your newly created ACTION_BACK_PRESSED, send it from your activity and listen for it in the relevant fragments.

Raneeraney answered 10/8, 2011 at 19:20 Comment(7)
Pretty nifty idea! some additional thoughts: + it's important to realize this logic with the broadcast going from activity > fragments (not the other way round). OnBackPressed is only available at the activity level, so trapping the event there and broadcasting it to all "listening" fragments is key here. + Using an EventBus (like Square's Otto) makes the implementation for a case like this trivial!Premolar
I haven't used Square's EventBus yet, but I will be in future products. I think it's a better pure-java solution, and if you can knock the Android out sections of your architecture, all the better.Raneeraney
How do you let only the top fragments to handle the event? for example, you would want to destroy the currently showing fragmentAborning
The broadcast receiver should be bound to the life cycle if you are using that, so when it's not visible it shouldn't receive the event.Raneeraney
Also note that LocalBroadcastManager can't do ordered broadcastsEmmenagogue
@Raneeraney What if the onBackPressed event should be consumed depending on a variable in the fragment that listens for the broadcast? Then the fragment would need to communicate back to the activity, which is not really possible with your approach.Standup
The broadcast approach wouldn't work well if you needed to know immediately whether a fragment's WebView could go back or not so as to quit the application.Amaryl
Q
56

None of that is easy to implement nor will it function in an optimal way.

Fragments have a method call onDetach that will do the job.

@Override
    public void onDetach() {
        super.onDetach();
        PUT YOUR CODE HERE
    }

THIS WILL DO THE JOB.

Quiles answered 14/4, 2014 at 4:38 Comment(5)
but the issue here is that you do not know whether the fragment was detached due to a back press or due to some other action, which the led the user go so some other activity or fragment.Guillot
onDetach() is called when the fragment is no longer attached to its activity. This is called after onDestroy(). Although you would get to this code when you press the back button you would also get this code when you change from fragment to fragment. You could do something nifty to make this work though...Claro
Works fine for a DialogFragment.Winger
Don't forget to add isRemoving() as described in https://mcmap.net/q/63955/-how-to-implement-onbackpressed-in-fragments.Winger
this is wrong. It can be called for several reasonsHeckle
A
56

Google has released a new API to deal with onBackPressed in Fragment:

activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {

            }
        })
Anabelanabella answered 12/11, 2020 at 14:44 Comment(3)
This is the real answer on these days.Impressionable
How to call a super.onBackPressed in this approach?Suppurative
this post explains it better medium.com/@debuggingisfun/…Landslide
H
34

New and better approach: Following piece of code in a Fragment will help you to capture the back-press event.

JAVA

@Override
public void onAttach(@NonNull Context context) {
    super.onAttach(context);

    OnBackPressedCallback callback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            Toast.makeText(mContext, "back pressed", Toast.LENGTH_SHORT).show();
            
            // And when you want to go back based on your condition
            if (yourCondition) {
                this.setEnabled(false);
                requireActivity().onBackPressed();
            }
        }
    };

    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}

Kotlin

activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {

    }
})
Hagiology answered 27/1, 2021 at 12:30 Comment(0)
S
31

Just add addToBackStack while you are transitioning between your fragments like below:

fragmentManager.beginTransaction().replace(R.id.content_frame,fragment).addToBackStack("tag").commit();

if you write addToBackStack(null) , it will handle it by itself but if you give a tag , you should handle it manually.

Stylolite answered 20/10, 2013 at 13:33 Comment(5)
Is there anything else that needs to be done or does adding the addToBackStack method is sufficient?Confucian
if you just add that it should works, if it still not working there should be something in your code. Leave the code somewhere to see please @SreecharanDesabattulaStylolite
@Stylolite can you tell #32133123Chartreuse
Best answer. Day saved!Impressionable
Best answer under this topic.Melendez
D
28

Using Navigation component you can do it like this:

Java

public class MyFragment extends Fragment {

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // This callback will only be called when MyFragment is at least Started.
    OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
        @Override
        public void handleOnBackPressed() {
            // Handle the back button event
        }
    });
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);

    // The callback can be enabled or disabled here or in handleOnBackPressed()
}
...
}

Kotlin

class MyFragment : Fragment() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // This callback will only be called when MyFragment is at least Started.
    val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
        // Handle the back button event
    }

    // The callback can be enabled or disabled here or in the lambda
}
...
}
Defoliate answered 25/2, 2020 at 0:20 Comment(1)
easiest method to do itLadner
C
21

since this question and some of the answers are over five years old, let me share my solution. This is a follow-up and modernization to the answer from @oyenigun

UPDATE: At the bottom of this article, I added an alternative implementation using an abstract Fragment extension that won't involve the Activity at all, which would be useful for anyone with a more complex fragment hierarchy involving nested fragments that require different back behavior.

I needed to implement this because some of the fragments I use have smaller views that I would like to dismiss with the back button, such as small information views that pop up, etc, but this is good for anyone who needs to override the behavior of the back button inside fragments.

First, define an Interface

public interface Backable {
    boolean onBackPressed();
}

This interface, which I call Backable (I'm a stickler for naming conventions), has a single method onBackPressed() that must return a boolean value. We need to enforce a boolean value because we will need to know if the back button press has "absorbed" the back event. Returning true means that it has, and no further action is needed, otherwise, false says that the default back action still must take place. This interface should be it's own file (preferably in a separate package named interfaces). Remember, separating your classes into packages is good practice.

Second, find the top fragment

I created a method that returns the last Fragment object in the back stack. I use tags... if you use ID's, make the necessary changes. I have this static method in a utility class that deals with navigation states, etc... but of course, put it where it best suits you. For edification, I've put mine in a class called NavUtils.

public static Fragment getCurrentFragment(Activity activity) {
    FragmentManager fragmentManager = activity.getFragmentManager();
    if (fragmentManager.getBackStackEntryCount() > 0) {
        String lastFragmentName = fragmentManager.getBackStackEntryAt(
                fragmentManager.getBackStackEntryCount() - 1).getName();
        return fragmentManager.findFragmentByTag(lastFragmentName);
    }
    return null;
}

Make sure the back stack count is greater than 0, otherwise an ArrayOutOfBoundsException could be thrown at runtime. If it isn't greater than 0, return null. We'll check for a null value later...

Third, Implement in a Fragment

Implement the Backable interface in whichever fragment where you need to override the back button behavior. Add the implementation method.

public class SomeFragment extends Fragment implements 
        FragmentManager.OnBackStackChangedListener, Backable {

...

    @Override
    public boolean onBackPressed() {

        // Logic here...
        if (backButtonShouldNotGoBack) {
            whateverMethodYouNeed();
            return true;
        }
        return false;
    }

}

In the onBackPressed() override, put whatever logic you need. If you want the back button to not pop the back stack (the default behavior), return true, that your back event has been absorbed. Otherwise, return false.

Lastly, in your Activity...

Override the onBackPressed() method and add this logic to it:

@Override
public void onBackPressed() {

    // Get the current fragment using the method from the second step above...
    Fragment currentFragment = NavUtils.getCurrentFragment(this);

    // Determine whether or not this fragment implements Backable
    // Do a null check just to be safe
    if (currentFragment != null && currentFragment instanceof Backable) {

        if (((Backable) currentFragment).onBackPressed()) {
            // If the onBackPressed override in your fragment 
            // did absorb the back event (returned true), return
            return;
        } else {
            // Otherwise, call the super method for the default behavior
            super.onBackPressed();
        }
    }

    // Any other logic needed...
    // call super method to be sure the back button does its thing...
    super.onBackPressed();
}

We get the current fragment in the back stack, then we do a null check and determine if it implements our Backable interface. If it does, determine if the event was absorbed. If so, we're done with onBackPressed() and can return. Otherwise, treat it as a normal back press and call the super method.

Second Option to not involve the Activity

At times, you don't want the Activity to handle this at all, and you need to handle it directly within the fragment. But who says you can't have Fragments with a back press API? Just extend your fragment to a new class.

Create an abstract class that extends Fragment and implements the View.OnKeyListner interface...

import android.app.Fragment;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;

public abstract class BackableFragment extends Fragment implements View.OnKeyListener {

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.setFocusableInTouchMode(true);
        view.requestFocus();
        view.setOnKeyListener(this);
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_UP) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                onBackButtonPressed();
                return true;
            }
        }

        return false;
    }

    public abstract void onBackButtonPressed();
}

As you can see, any fragment that extends BackableFragment will automatically capture back clicks using the View.OnKeyListener interface. Just call the abstract onBackButtonPressed() method from within the implemented onKey() method using the standard logic to discern a back button press. If you need to register key clicks other than the back button, just be sure to call the super method when overriding onKey() in your fragment, otherwise you'll override the behavior in the abstraction.

Simple to use, just extend and implement:

public class FragmentChannels extends BackableFragment {

    ...

    @Override
    public void onBackButtonPressed() {
        if (doTheThingRequiringBackButtonOverride) {
            // do the thing
        } else {
            getActivity().onBackPressed();
        }
    }

    ...
}

Since the onBackButtonPressed() method in the super class is abstract, once you extend you must implement onBackButtonPressed(). It returns void because it just needs to perform an action within the fragment class, and does not need to relay the absorption of the press back to the Activity. Make sure you do call the Activity onBackPressed() method if whatever you're doing with the back button doesn't require handling, otherwise, the back button will be disabled... and you don't want that!

Caveats As you can see, this sets the key listener to the root view of the fragment, and we'll need to focus it. If there are edit texts involved (or any other focus-stealing views) in your fragment that extends this class, (or other inner fragments or views that have the same), you'll need to handle that separately. There's a good article on extending an EditText to lose focus on a back press.

I hope someone finds this useful. Happy coding.

Courland answered 19/1, 2017 at 11:41 Comment(7)
Thanks, it's a nice solution. Can you say, why do you call super.onBackPressed(); twice?Winger
It's only called once per scenario regarding the null state of currentFragment. If the fragment is not null and the fragment implements the Backable interface, no action is taken. If the fragment is not null and it does NOT implement Backable, we call the super method. If the fragment is null, it skips to the last super call.Courland
Sorry, I didn't understand. In the case when a currentFragment is not null and is an instance of Backable and it is detached (we pressed back button and close the fragment) the first occurence of super.onBackPressed(); is called, then the second one.Winger
Excellent SolutionDevotional
Maybe "Closeable" sounds a little better than "Backable"Caton
Closeable is already an interface in the java.io package, plus, this is just an example. The implementing party can choose their own interface name.Courland
Just a sidenote. instanceof implicitly does a null check, so no need to explicitly check for null prior to thatAswan
C
16

In kotlin, it's way simplier.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            //
        }
    })
}
Chemistry answered 14/1, 2022 at 14:20 Comment(1)
this one of the best solution for kotlin developers . Thanks man.Insulting
P
15

The solution is simple:

  1. If you have a base fragment class that all fragments extend, then add this code to it's class, otherwise create such a base fragment class
/* 
* called when on back pressed to the current fragment that is returned
*/
public void onBackPressed()
{
    // add code in super class when override
}
  1. In your Activity class, override onBackPressed as follows:
private BaseFragment _currentFragment;

@Override
public void onBackPressed()
{
      super.onBackPressed();
     _currentFragment.onBackPressed();
}
  1. In your Fragment class, add your desired code:
@Override
public void onBackPressed()
{
    setUpTitle();
}
Poirier answered 8/7, 2014 at 10:59 Comment(1)
#32133123Chartreuse
W
11

onBackPressed() cause Fragment to be detach from Activity.

According to @Sterling Diaz answer I think he is right. BUT some situation will be wrong. (ex. Rotate Screen)

So, I think we could detect whether isRemoving() to achieve goals.

You can write it at onDetach() or onDestroyView(). It is work.

@Override
public void onDetach() {
    super.onDetach();
    if(isRemoving()){
        // onBackPressed()
    }
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    if(isRemoving()){
        // onBackPressed()
    }
}
Wreckful answered 24/11, 2014 at 11:36 Comment(0)
P
10

Inside the fragment's onCreate method add the following:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    OnBackPressedCallback callback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            //Handle the back pressed
        }
    };
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}
Polished answered 29/3, 2020 at 23:17 Comment(3)
after adding this myFragment backpress works but now activity backpress didntBuckjump
Inside the callback you can handle the activities back pressed. 'handleOnBackPressed() { //Handle the back pressed requireActivity().onBackPressed(); } ' @SunilChaudharyPolished
You can even remove the callback using callback.remove() if the activity's back pressed needs to be called.Hedge
H
9

You should add interface to your project like below;

public interface OnBackPressed {

     void onBackPressed();
}

And then, you should implement this interface on your fragment;

public class SampleFragment extends Fragment implements OnBackPressed {

    @Override
    public void onBackPressed() {
        //on Back Pressed
    }

}

And you can trigger this onBackPressed event under your activities onBackPressed event like below;

public class MainActivity extends AppCompatActivity {
       @Override
        public void onBackPressed() {
                Fragment currentFragment = getSupportFragmentManager().getFragments().get(getSupportFragmentManager().getBackStackEntryCount() - 1);
                if (currentFragment instanceof OnBackPressed) {  
                    ((OnBackPressed) currentFragment).onBackPressed();
                }
                super.onBackPressed();
        }
}
Halt answered 12/1, 2017 at 11:25 Comment(1)
It's agood method, but be careful, don't call getActivity().onBackPressed(); as it will invoke exception: "java.lang.StackOverflowError: stack size 8MB".Winger
M
8

If you use EventBus, it is probably a far more simpler solution :

In your Fragment :

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    EventBus.getDefault().register(this);
}

@Override
public void onDetach() {
    super.onDetach();
    EventBus.getDefault().unregister(this);
}


// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    getSupportFragmentManager().popBackStack();
}

and in your Activity class you can define :

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    super.onBackPressed();
}

@Override
public void onBackPressed() {
    EventBus.getDefault().post(new BackPressedMessage(true));
}

BackPressedMessage.java is just a POJO object

This is super clean and there is no interface/implementation hassle.

Mercaptopurine answered 1/4, 2015 at 6:54 Comment(0)
G
8

Well I done it like this, and it work for me

Simple interface

FragmentOnBackClickInterface.java

public interface FragmentOnBackClickInterface {
    void onClick();
}

Example implementation

MyFragment.java

public class MyFragment extends Fragment implements FragmentOnBackClickInterface {

// other stuff

public void onClick() {
       // what you want to call onBackPressed?
}

then just override onBackPressed in activity

    @Override
public void onBackPressed() {
    int count = getSupportFragmentManager().getBackStackEntryCount();
    List<Fragment> frags = getSupportFragmentManager().getFragments();
    Fragment lastFrag = getLastNotNull(frags);
    //nothing else in back stack || nothing in back stack is instance of our interface
    if (count == 0 || !(lastFrag instanceof FragmentOnBackClickInterface)) {
        super.onBackPressed();
    } else {
        ((FragmentOnBackClickInterface) lastFrag).onClick();
    }
}

private Fragment getLastNotNull(List<Fragment> list){
    for (int i= list.size()-1;i>=0;i--){
        Fragment frag = list.get(i);
        if (frag != null){
            return frag;
        }
    }
    return null;
}
Gebhart answered 4/9, 2015 at 12:31 Comment(8)
Not working ! super.onbackpressed() gets called always :(Leis
How to pass this event Inner Fragment. I have ViewPager in Activity and that ViewPager has Fragments, Now all those Fragment have child fragment. How to pass back button event to that child fragments.Skurnik
@KishanVaghela well I didn't touched android since then but I have one idea. Give me 24th to update my answer.Leaden
Ok no problem, One option is that I need to pass listener event to every child fragment from parent fragment. but It's not an ideal way because I need to do this for every fragments.Skurnik
@KishanVaghela I was thinking about manager in activity. You could add/remove fragments with that interface. But with this approach you have to implement manager in every fragment that have subfragments. So maybe better solution will be creating manager as singleton. Then you will have to add/remove fragments/objects and in 'onBackPressed' you call method 'onBackPressed' of this manager. That method will call method 'onClick' in every object that was added to manager. Is that more or less clear to you?Leaden
@Dudi Is Fragment manager work with singleton? Activity has its own fragment manager, Fragments that are in activity have child fragment manager.Skurnik
@KishanVaghela I forgot about it. I think you could extend fragment manager with this mechanism. But I don't know android and his api that much well to tell you how to do it.Leaden
While adding fragment to activity always to add the transaction to back stack for using getSupportFragmentManager().getBackStackEntryCount();Sepia
M
8

this is my solution:

in MyActivity.java:

public interface OnBackClickListener {
        boolean onBackClick();
    }

    private OnBackClickListener onBackClickListener;

public void setOnBackClickListener(OnBackClickListener onBackClickListener) {
        this.onBackClickListener = onBackClickListener;
    }

@Override
    public void onBackPressed() {
        if (onBackClickListener != null && onBackClickListener.onBackClick()) {
            return;
        }
        super.onBackPressed();
    }

and in Fragment:

((MyActivity) getActivity()).setOnBackClickListener(new MyActivity.OnBackClickListener() {
    @Override
    public boolean onBackClick() {
        if (condition) {
            return false;
        }

        // some codes

        return true;
    }
});
Maher answered 1/8, 2017 at 6:3 Comment(0)
M
7
public class MyActivity extends Activity {

    protected OnBackPressedListener onBackPressedListener;

    public interface OnBackPressedListener {
        void doBack();
    }

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
    } 

    @Override
    protected void onDestroy() {
        onBackPressedListener = null;
        super.onDestroy();
    }
}

in your fragment add the following, dont forget to implement mainactivity's interface.

public class MyFragment extends Framgent implements MyActivity.OnBackPressedListener {
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
         ((MyActivity) getActivity()).setOnBackPressedListener(this);
    }

@Override
public void doBack() {
    //BackPressed in activity will call this;
}

}
Malone answered 2/8, 2016 at 7:6 Comment(0)
S
7

This is just a small code that will do the trick:

 getActivity().onBackPressed();

Hope it helps someone :)

Sphingosine answered 19/1, 2017 at 9:2 Comment(2)
He asked to override the behavior, not to simply invoke the activity method.Courland
I did read it carefully, thanks. Your solution simply relays the onBackPressed method back to the Activity, when he asked how to override.Courland
E
6

UPDATE: OnBackPressedDispatcher should be used.

Guide how to use available at developer.android.com/guide/navigation/navigation-custom-back


You can register fragment in activity to handle back press:

interface BackPressRegistrar {
    fun registerHandler(handler: BackPressHandler)
    fun unregisterHandler(handler: BackPressHandler)
}

interface BackPressHandler {
    fun onBackPressed(): Boolean
}

usage:

In Fragment:

private val backPressHandler = object : BackPressHandler {
    override fun onBackPressed(): Boolean {
        showClosingWarning()
        return false
    }
}

override fun onResume() {
    super.onResume()
    (activity as? BackPressRegistrar)?.registerHandler(backPressHandler)
}

override fun onStop() {
    (activity as? BackPressRegistrar)?.unregisterHandler(backPressHandler)
    super.onStop()
}

In Activity:

class MainActivity : AppCompatActivity(), BackPressRegistrar {


    private var registeredHandler: BackPressHandler? = null
    override fun registerHandler(handler: BackPressHandler) { registeredHandler = handler }
    override fun unregisterHandler(handler: BackPressHandler) { registeredHandler = null }

    override fun onBackPressed() {
        if (registeredHandler?.onBackPressed() != false) super.onBackPressed()
    }
}
Eliason answered 19/4, 2019 at 19:32 Comment(0)
W
6

You can use onBackPressedDispatcher of parent activity like this:

val backpress = requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, true) {
            // here dispatcher works for any action when back pressed
}

you can also enable/disable backpress button from fragment any time like this:

backpress.isEnabled = true/false
Worry answered 5/9, 2020 at 8:42 Comment(0)
D
6
 requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
        //your code 
    }
Disport answered 24/5, 2021 at 10:24 Comment(0)
S
5
@Override
public void onResume() {
    super.onResume();

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
                // handle back button
                replaceFragmentToBackStack(getActivity(), WelcomeFragment.newInstance(bundle), tags);

                return true;
            }

            return false;
        }
    });
}
Spiers answered 21/3, 2018 at 10:20 Comment(1)
Only if do not fragments have a focusable view (such as edit text), let fragments handle back button itself as this answer does. Otherwise, let activity containing fragments do it by OnBackPress() as the first answer is written; since focusable view will steal fragment's focus. However, we can regain focus for fragment by clear focus all focusable views by clearFocus() method and re-focus to fragment.Bewray
U
5

According to the AndroidX release notes, androidx.activity 1.0.0-alpha01 is released and introduces ComponentActivity, a new base class of the existing FragmentActivity and AppCompatActivity. And this release brings us a new feature:

You can now register an OnBackPressedCallback via addOnBackPressedCallback to receive onBackPressed() callbacks without needing to override the method in your activity.

Uam answered 7/11, 2018 at 5:33 Comment(3)
Can you show an example of this? I'm not getting that method to resolve.Scully
@BryanBryce I haven't find an example but the official doc: developer.android.com/reference/androidx/activity/…Uam
The method won't resolve b/c AppCompatActivity is actually extending from androidx.core.app.ComponentActivity instead of androidx.activity.ComponentActivity.Suzerainty
M
5

Providing custom back navigation by handling onBackPressed is now more easy with callbacks inside the fragment.

class MyFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val onBackPressedCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (true == conditionForCustomAction) {
                    myCustomActionHere()
                } else  NavHostFragment.findNavController(this@MyFragment).navigateUp();    
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(
        this, onBackPressedCallback
    )
    ...
}

If you want the default back action based on some condition, you can use:

 NavHostFragment.findNavController(this@MyFragment).navigateUp();
Marimaria answered 4/11, 2019 at 6:48 Comment(0)
C
4

How about using onDestroyView()?

@Override
public void onDestroyView() {
    super.onDestroyView();
}
Cheri answered 23/5, 2015 at 17:15 Comment(2)
what about going to another fragment from actual fragment, what will happen using your method? :)Hay
Most useless answer. It is the same as writing i = i or if (1 < 0) {}.Winger
E
4

Just follow these steps:

Always while adding a fragment,

fragmentTransaction.add(R.id.fragment_container, detail_fragment, "Fragment_tag").addToBackStack(null).commit();

Then in the main activity, override onBackPressed()

if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
    getSupportFragmentManager().popBackStack();
} else {
    finish();
}

To handle the back button in your app,

Fragment f = getActivity().getSupportFragmentManager().findFragmentByTag("Fragment_tag");
if (f instanceof FragmentName) {
    if (f != null) 
        getActivity().getSupportFragmentManager().beginTransaction().remove(f).commit()               
}

That's it!

Entrench answered 21/7, 2016 at 14:43 Comment(0)
E
4

Very short and sweet answer:

getActivity().onBackPressed();

Explanation of whole scenario of my case:

I have FragmentA in MainActivity, I am opening FragmentB from FragmentA (FragmentB is child or nested fragment of FragmentA)

 Fragment duedateFrag = new FragmentB();
 FragmentTransaction ft  = getFragmentManager().beginTransaction();
 ft.replace(R.id.container_body, duedateFrag);
 ft.addToBackStack(null);
 ft.commit();

Now if you want to go to FragmentA from FragmentB you can simply put getActivity().onBackPressed(); in FragmentB.

Elisha answered 23/12, 2016 at 9:55 Comment(4)
OP has asked about how to handle back press I guessAriella
Also whoever downvoted doesn't think you addressed the problem rightly and it's his right you are on a public forum and no one has obligation to upvote you or think you are correct , go to Meta and appeal if you think it's wrong . Learn to behave in public forums dude.Ariella
@user31231234124 some persons told me that this answer is not related to question....if you think this answer will help other visitors in future to implement onbackpressed() in fragment in easiest way, please upvote my answer so that more users can get benefit..........Elisha
some people are blind and dumb enough to realise the perfection in your answer... just ignore those kiddos/scrip kiddies... it has solved my problem 100% percent... Thanks a lot...Bebebebeerine
T
4

In my solution (Kotlin);

I'm using onBackAlternative function as a parameter on BaseActivity.

BaseActivity

abstract class BaseActivity {

    var onBackPressAlternative: (() -> Unit)? = null

    override fun onBackPressed() {
        if (onBackPressAlternative != null) {
            onBackPressAlternative!!()
        } else {
            super.onBackPressed()
        }
    }
}

I have a function for set onBackPressAlternative on BaseFragment.

BaseFragment

abstract class BaseFragment {

     override fun onStart() {
        super.onStart()
        ...
        setOnBackPressed(null) // Add this
     }

      //Method must be declared as open, for overriding in child class
     open fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
         (activity as BaseActivity<*, *>).onBackPressAlternative = onBackAlternative
     }
}

Then my onBackPressAlternative is ready to use on fragments.

Sub Fragments

override fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
    (activity as BaseActivity<*, *>).onBackPressAlternative = {
        // TODO Your own custom onback function 
    }
}
Triad answered 8/10, 2019 at 13:59 Comment(0)
E
3

Do not implement ft.addToBackStack() method so that when you pressed back button your activity will be finished.

proAddAccount = new ProfileAddAccount();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, proAddAccount);
//fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Exedra answered 4/1, 2014 at 15:25 Comment(2)
tell me #32133123Chartreuse
Better overwrite the onBackPressed function as following: @Override public void onBackPressed(){ if (getSupportFragmentManager().getBackStackEntryCount() == 0){ finish(); } else { FragmentManager fm = getSupportFragmentManager(); fm.popBackStack(); } } If no fragments to popBackStack finish the activity, else popBack whats there.Andre
P
3

In activity life cycle, always android back button deals with FragmentManager transactions when we used FragmentActivity or AppCompatActivity.

To handle the backstack we don't need to handle its backstack count or tag anything but we should keep focus while adding or replacing a fragment. Please find the following snippets to handle the back button cases,

    public void replaceFragment(Fragment fragment) {

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
        }
        transaction.replace(R.id.activity_menu_fragment_container, fragment).commit();
    }

Here, I won't add back stack for my home fragment because it's home page of my application. If add addToBackStack to HomeFragment then app will wait to remove all the frament in acitivity then we'll get blank screen so I'm keeping the following condition,

if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
}

Now, you can see the previously added fragment on acitvity and app will exit when reaching HomeFragment. you can also look on the following snippets.

@Override
public void onBackPressed() {

    if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
        closeDrawer();
    } else {
        super.onBackPressed();
    }
}
Palm answered 7/5, 2017 at 12:47 Comment(0)
F
3

Fragment: Make a BaseFragment placing a method:

 public boolean onBackPressed();

Activity:

@Override
public void onBackPressed() {
    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    if (fragments != null) {
        for (Fragment fragment : fragments) {
            if (!fragment.isVisible()) continue;

            if (fragment instanceof BaseFragment && ((BaseFragment) fragment).onBackPressed()) {
                return;
            }
        }
    }

    super.onBackPressed();
}

Your activity will run over the attached and visible fragments and call onBackPressed() on each one of them and abort if one of them returns 'true' (meaning that it has been handled, so no further actions).

Flinty answered 30/11, 2017 at 10:2 Comment(1)
Sir, this one is the most elegant way and works perfect!Jacksmelt
D
2

Best solution,

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    public void onBackPressed() {

        FragmentManager fm = getSupportFragmentManager();
        for (Fragment frag : fm.getFragments()) {
            if (frag == null) {
                super.onBackPressed();
                finish();
                return;
            }
            if (frag.isVisible()) {
                FragmentManager childFm = frag.getChildFragmentManager();
                if (childFm.getFragments() == null) {
                    super.onBackPressed();
                    finish();
                    return;
                }
                if (childFm.getBackStackEntryCount() > 0) {
                    childFm.popBackStack();
                    return;
                }
                else {

                    fm.popBackStack();
                    if (fm.getFragments().size() <= 1) {
                        finish();
                    }
                    return;
                }

            }
        }
    }
}
Deirdredeism answered 10/8, 2016 at 7:26 Comment(0)
H
2

From your fragment, just use

 requireActivity().onBackPressed();

For handling click add binding.actionABack.setOnClickListener(v -> requireActivity().onBackPressed());

Harbird answered 13/6, 2021 at 19:11 Comment(0)
J
1

Its just simple if you have An Activity A and you make 3 fragments like B ,C and D.Now if you are in fragment B or C and onBackPressed you want to move on Fragment D every time .Then you have to just Override the onBackPressed() method in main Activity A and also when you jump to any fragment then pass a TAG or name of that fragment by which you recognized that fragment in main Activity A.

I am giving the example of that one by which you can easily understand that....

if (savedInstanceState == null) {

   getSupportFragmentManager().beginTransaction().add(R.id.container, new C_fragment(),"xyz").commit();

}

or if you are moving from fragment B to fragment C..and on back press you want to come on Fragment D...like below

btn.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View view) {

        getActivity().getSupportFragmentManager().beginTransaction().add(R.id.container, new C_frament(), "xyz").commit();
        ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle("Fragment C");
    }
});

Now you have to just override the onBackPressed() method in main activity....like below..

@Override
public void onBackPressed() {
    FragmentManager fragmentManager =getSupportFragmentManager();
    if (((C_fragment) getSupportFragmentManager().findFragmentByTag("xyz")) != null && ((C_fragment) getSupportFragmentManager().findFragmentByTag("xyz")).isVisible()) {
        Fragment fragment = new D_Fragment();
        fragmentManager.beginTransaction().replace(R.id.container, fragment).commit();
        getSupportActionBar().setTitle("D fragment ");
    } else {
        super.onBackPressed();
    }
}
Jellyfish answered 4/5, 2015 at 7:4 Comment(0)
U
1

I have used another approach as follows:

  • An Otto event bus to communicate between the Activity and its Fragments
  • A Stack in the Activity containing custom back actions wrapped in a Runnable that the calling fragment defines
  • When onBackPressed is called in the controlling Activity, it pops the most recent custom back action and executes its Runnable. If there's nothing on the Stack, the default super.onBackPressed() is called

The full approach with sample code is included here as an answer to this other SO question.

Unrest answered 23/11, 2015 at 12:39 Comment(0)
G
1

I know it's too late but I had the same problem last week. None of the answers helped me. I then was playing around with the code and this worked, since I already added the fragments.

In your Activity, set an OnPageChangeListener for the ViewPager so that you will know when the user is in the second activity. If he is in the second activity, make a boolean true as follows:

    mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
    // Set up the ViewPager with the sections adapter.
    mViewPager = (ViewPager) findViewById(R.id.pager);
    mViewPager.setAdapter(mSectionsPagerAdapter);
    mViewPager.setCurrentItem(0);
    mViewPager.addOnPageChangeListener(new OnPageChangeListener() {

        @Override
        public void onPageSelected(int position) {
            // TODO Auto-generated method stub
                mSectionsPagerAdapter.instantiateItem(mViewPager, position);
                if(position == 1)
                    inAnalytics = true;
                else if(position == 0)
                    inAnalytics = false;
        }

        @Override
        public void onPageScrolled(int position, float arg1, int arg2) {
            // TODO Auto-generated method stub
        }

        @Override
        public void onPageScrollStateChanged(int arg0) {
            // TODO Auto-generated method stub

        }
    });

Now check for the boolean whenever back button is pressed and set the current item to your first Fragment:

@Override
public void onBackPressed() {
    if(inAnalytics)
        mViewPager.setCurrentItem(0, true);
    else 
        super.onBackPressed();
}
Ghislainegholston answered 31/5, 2016 at 10:16 Comment(0)
A
1

Ok guys I finally found out a good solution.

In your onCreate() in your activity housing your fragments add a backstack change listener like so:

    fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            List<Fragment> f = fragmentManager.getFragments();
            //List<Fragment> f only returns one value
            Fragment frag = f.get(0);
            currentFragment = frag.getClass().getSimpleName();
        }
    });

(Also adding my fragmenManager is declared in the activities O Now every time you change fragment the current fragment String will become the name of the current fragment. Then in the activities onBackPressed() you can control the actions of your back button as so:

    @Override
    public void onBackPressed() {

    switch (currentFragment) {
        case "FragmentOne":
            // your code here
            return;
        case "FragmentTwo":
            // your code here
            return;
        default:
            fragmentManager.popBackStack();
            // default action for any other fragment (return to previous)
    }

}

I can confirm that this method works for me.

Abbottson answered 8/7, 2018 at 2:38 Comment(0)
E
1

Simply do it in onKeyUp() :

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {

    if (keyCode == KeyEvent.KEYCODE_BACK) {
        // do something
        return true;  // return true if back handled, false otherwise
    }

    return super.onKeyUp(keyCode, event);
}
Elma answered 13/9, 2018 at 12:42 Comment(1)
There is no onKeyUp for Fragment: developer.android.com/reference/kotlin/androidx/fragment/app/…Briard
P
1

Try this one, if you really want to enable onBackPressed() in Fragment. After wasting an hour of time, I made this solution which exactly fits over the needs, from my prior experiences.

You just need to focus on the value of private int STATUS_FRAGMENT=0; which suffices the need of addToBackStack() in fragments.

import android.view.MenuItem;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;

import com.example.growfast.NavigationItemsFolder.CoreFragments.Cart;
import com.example.growfast.NavigationItemsFolder.CoreFragments.HelpDesk;
import com.example.growfast.NavigationItemsFolder.CoreFragments.Home;
import com.example.growfast.NavigationItemsFolder.CoreFragments.ProfileDetails;
import com.example.growfast.R;
import com.google.android.material.bottomnavigation.BottomNavigationView;

public class BusinessManagement extends AppCompatActivity {

    public BottomNavigationView bottomNavigationView;
    private int STATUS_FRAGMENT=0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_base_layout);
        setBottomNavigationMenu();

    }

    private void setBottomNavigationMenu() {
        bottomNavigationView = findViewById(R.id.navigation);
        bottomNavigationView.setVisibility(View.VISIBLE);

        bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {

            Fragment fragment = null;

            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {

                switch (item.getItemId()) {

                    case R.id.action_home:
                        fragment = new Home();
                        break;

                    case R.id.action_profile:
                        fragment = new ProfileDetails();
                        break;
                    case R.id.action_cart:
                        fragment = new Cart();
                        break;
                    case R.id.action_favourites_menu:
                        fragment = new HelpDesk();
                        break;

                }
                return loadFromFragment(fragment);

            }
        });
        bottomNavigationView.setSelectedItemId(R.id.action_home);
    }

    private boolean loadFromFragment(Fragment fragment) {
        if (fragment != null) {
            getSupportFragmentManager().beginTransaction().replace(R.id.my_container, fragment)
                    .commit();
            STATUS_FRAGMENT=1;
            return true;
        }
        return false;
    }

    @Override
    public void onBackPressed() {
        if (STATUS_FRAGMENT==1) {
            bottomNavigationView.setSelectedItemId(R.id.action_home);
            STATUS_FRAGMENT=0;
            bottomNavigationView.setVisibility(View.VISIBLE);
        }
        else{
            super.onBackPressed();
        }
    }


}```
Pneumoencephalogram answered 8/12, 2020 at 12:1 Comment(0)
F
1

If you need to have the implementation in the Fragment you should use an interface.

On the other hand, if you need to have the implementation in the activity such as eg. perform a simple finish() of the activity do the following:

YourActivity.kt

override fun onBackPressed() {
    Log.d(this.javaClass.simpleName, "onBackPressed() called")

    if (supportFragmentManager.fragments.any { it is YourFragment }) {
        finish()
        return
    }
}
Fungus answered 3/12, 2021 at 16:51 Comment(0)
P
1

No One Mentioned how do I call activity?.onBackPressed inside this logic.

so I created an extension that may be useful for you:

fun Fragment.onBackClick(callback: (onBackPressedCallback: OnBackPressedCallback) -> Unit) {
// This callback will only be called when MyFragment is at least Started.

    activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner) {
        callback.invoke(this)
    }
}

now the exciting part calling onBackPressed when your logic is done:

sample use in a fragment:

onBackClick { 
        if (shouldBackFromFrag){
            //do your logic when pressed back
        }else{
            it.isEnabled = false //the key to disable the dispatcher
            activity?.onBackPressed()
        }
    }
Preferable answered 5/3, 2022 at 15:10 Comment(0)
D
1

if fragment and used NavController simply call

findNavController().popBackStack()
Deal answered 26/7, 2022 at 11:7 Comment(0)
B
0

In your mainActivity implement interface for callback

protected mainActivity.OnBackPressedListener onBackPressedListener;

public interface OnBackPressedListener {
    void doBack();
}

public void setOnBackPressedListener(mainActivity.OnBackPressedListener onBackPressedListener) {
    this.onBackPressedListener = onBackPressedListener;
}

@Override
public void onBackPressed() {
    if (onBackPressedListener != null) {
        onBackPressedListener.doBack();
    } else { 
        super.onBackPressed();
    }
}

on fragment implement intefrace OnBackPressedListener which we write in mainActivity like

implements mainActivity.OnBackPressedListener

mainActivity is my base activity write following code in your fragment onCreateView method

((mainActivity) getActivity()).setOnBackPressedListener(this);

and implement OnBackPressedListener interface method doBack

@Override
public void doBack() {
    //call base fragment 
}

now call fragment which you want to call on back pressed using doBack() method

Billposter answered 29/8, 2017 at 3:30 Comment(0)
S
0

Here is my answer to handle the fragment on the back button pressed. I have the bottom navigation with five items (A, B, C, D,E) to attach to the activity when clicking on an item I attach the fragment to the activity. let suppose click on item(A) I attach fragment(A) and then if click on item(B) then I attach fragment(B) and so on.

The flow of Click item. A->B->C->D->E

The flow of back button pressed. E->D->C->B->A

 @Override
        public void onBackPressed() {
            int count = getSupportFragmentManager().getBackStackEntryCount();
            if (count == 0) {
                super.onBackPressed();
            } else {
                int index = ((getSupportFragmentManager().getBackStackEntryCount()) -1);
                getSupportFragmentManager().popBackStack();
                FragmentManager.BackStackEntry backEntry = getSupportFragmentManager().getBackStackEntryAt(index);
                int stackId = backEntry.getId();
                bottomNavigationView.getMenu().getItem(stackId).setChecked(true);
            }
        }

Here the demo is

Sowell answered 25/3, 2021 at 8:13 Comment(0)
G
0
private boolean isMainFragment = true;

@Override
public void onBackPressed() {

    if (isMainFragment){
        finish();
    }else {
        getSupportFragmentManager().popBackStack();
        isMainFragment = true;
    }
}

When open anther fragment then just add

isMainFragment = false;

it's work with me

Gadolinium answered 21/6, 2021 at 8:46 Comment(0)
S
0

In case of kotlin use this onAttach callback

        override fun onAttach(context: Context) {
          super.onAttach(context)
           val callback: OnBackPressedCallback = object : 
                 OnBackPressedCallback(true) {
                 override fun handleOnBackPressed() {
                 // your onbackpressed code 


            }
          }
      requireActivity().onBackPressedDispatcher.addCallback(this, callback)
      }
Sussex answered 27/8, 2022 at 7:16 Comment(0)
H
-1

I had the same problem and I created a new listener for it and used in my fragments.

1 - Your activity should have a listener interface and a list of listeners in it

2 - You should implement methods for adding and removing the listeners

3 - You should override the onBackPressed method to check that any of the listeners use the back press or not

public class MainActivity ... {

    /**
     * Back press listener list. Used for notifying fragments when onBackPressed called
     */
    private Stack<BackPressListener> backPressListeners = new Stack<BackPressListener>();


    ...

    /**
     * Adding new listener to back press listener stack
     * @param backPressListener
     */
    public void addBackPressListener(BackPressListener backPressListener) {
        backPressListeners.add(backPressListener);
    }

    /**
     * Removing the listener from back press listener stack
     * @param backPressListener
     */
    public void removeBackPressListener(BackPressListener backPressListener) {
        backPressListeners.remove(backPressListener);
    }


    // Overriding onBackPressed to check that is there any listener using this back press
    @Override
    public void onBackPressed() {

        // checks if is there any back press listeners use this press
        for(BackPressListener backPressListener : backPressListeners) {
            if(backPressListener.onBackPressed()) return;
        }

        // if not returns in the loop, calls super onBackPressed
        super.onBackPressed();
    }

}

4 - Your fragment must implement the interface for back press

5 - You need to add the fragment as a listener for back press

6 - You should return true from onBackPressed if the fragment uses this back press

7 - IMPORTANT - You must remove the fragment from the list onDestroy

public class MyFragment extends Fragment implements MainActivity.BackPressListener {


    ...

    @Override
    public void onAttach(Activity activity) {
        super.onCreate(savedInstanceState);

        // adding the fragment to listener list
        ((MainActivity) activity).addBackPressListener(this);
    }

    ...

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

        // removing the fragment from the listener list
        ((MainActivity) getActivity()).removeBackPressListener(this);
    }

    ...

    @Override
    public boolean onBackPressed() {

        // you should check that if this fragment is the currently used fragment or not
        // if this fragment is not used at the moment you should return false
        if(!isThisFragmentVisibleAtTheMoment) return false;

        if (isThisFragmentUsingBackPress) {
            // do what you need to do
            return true;
        }
        return false;
    }
}

There is a Stack used instead of the ArrayList to be able to start from the latest fragment. There may be a problem also while adding fragments to the back stack. So you need to check that the fragment is visible or not while using back press. Otherwise one of the fragments will use the event and latest fragment will not be closed on back press.

I hope this solves the problem for everyone.

Hsu answered 20/3, 2014 at 9:21 Comment(0)
O
-1

I just use findFragmentById and cast to the fragment and call a method that handles onBackpressed. This sample shows checking if this is the first step in order process if it is pop up a dialog fragment to confirm exit order.

@Override
    public void onBackPressed() {
        //check if first fragment in wizard is loaded, if true get abandon confirmation before going back
        OrderFragment frag =(OrderFragment)getSupportFragmentManager().findFragmentById(R.id.fragContainer);
        if (frag==null || (frag!=null && frag.getOrderStage()<1)){
            AlertDialogFragment.show(getSupportFragmentManager(), R.string.dialog_cancel_order,R.string.dialog_cancel_order_msg, R.string.dialog_cancel_order_pos,R.string.dialog_order_quiz_neg, DIALOG_ID_CANCEL);
        }else {
            super.onBackPressed();
        }
    }
Olshausen answered 20/6, 2014 at 9:58 Comment(0)
G
-1

Easy way to handle onBackPressed() in Fragments

Step 1: Create a static boolean in activity.

public static Fragment_one;

Step 2: On MainActivity(Activity that holds fragment) in On Create method, declare

Fragment_one=true;

Step 3: Override onBackPressed() in MainActivity

@Override
public void onBackPressed() {

    if(Fragment_one) {
     //Back key pressed on fragment one

    }else {
      //Back key pressed on fragment two
    }
}

Step 4: On fragment_one onCreateView method declare

MainActivity.Fragment_one=true;

Step 5 On fragment_two onCreateView method declare

MainActivity.Fragment_one=false;

Note: This method can be only applicable to TWO fragments.

Grillwork answered 18/1, 2018 at 6:27 Comment(0)
O
-1

To implement like this Fragment_1 -> Fragment_2 -> Fragment_3

    Button btn = (Button) rootView.findViewById(R.id.your_button_id);
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            Fragment_2 nextFrag= new Fragment_2();
            getActivity().getSupportFragmentManager().beginTransaction()
                    .replace(R.id.content_frame, nextFrag,getTag())
                    .addToBackStack(null)
                    .commit();

        }
    });

Fragment_3 -> Fragment_2 -> Fragment_1

Step_1: Create a publically accessible String in Base Activity

Step_2: Whenever a new Fragment is activated, Change the value of the String in Base Activity

Step_3: Then add onBackPressed() method And pass the String value to another Method in Which The fagments can be replaced

In the Base Activity

public static String currentFragment=null;

@Override
public void onBackPressed() 
{
        displayPreviousFragment(currentFragment);
}

public void displayPreviousFragment(String currentFragment)
{
    //creating fragment object
    Fragment fragment = null;

    //initializing the fragment object which is selected
    switch (currentFragment)
    {
        case "Fragment_2"    :   fragment = new Fargment_1();     break;
        case "Fragment_3"    :   fragment = new Fragment_2();     break;
    }

    //replacing the fragment
    if (fragment != null) {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.frame_to_replace_fragment, fragment);
        ft.commit();
    }
}

In Fragment_2 inside OnCreateView method

BaseActivity.currentFragment="Fragment_2";

In Fragment_3 inside OnCreateView method

BaseActivity.currentFragment="Fragment_3";
Overanxious answered 13/3, 2018 at 11:4 Comment(0)
L
-1

This line of code will do the trick from within any fragment, it will pop the current fragment on the backstack.

getActivity().getSupportFragmentManager().popBackStack();
Laidlaw answered 22/3, 2018 at 20:49 Comment(2)
It pops a fragment, but not defines an event for back press.Winger
Well, you can always override the onBackPress callback on the container activity, and add the call to the super.onBackPress. It will pop the current fragment out of the stack and bring the one next on the queue back to the foreground.Laidlaw
B
-1
public interface IonBackPressInFrag {
    void backPressed();
}

public class FragmentMainActivity extends AppCompatActivity {
    public IonBackPressInFrag backPressInFrag;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        @Override
        public void onBackPressed () {
            backPressInFrag.backPressed();
        }
    }
}

public class FragDetailSearch extends Fragment implements IonBackPressInFrag {
 if(getActivity() !=null){
        ((FragmentMainActivity) getActivity()).backPressInFrag = this;
    }

    @Override
    public void backPressed() {
        Toast.makeText(getContext(), "backkkkkk", Toast.LENGTH_LONG).show();
    }
}
Ben answered 12/5, 2018 at 20:56 Comment(0)
O
-1
   @Override
   public boolean onKeyDown(int keyCode, KeyEvent event)
    {
       if ((keyCode == KeyEvent.KEYCODE_BACK))
  {
           finish();
    }
       return super.onKeyDown(keyCode, event);
    }

Just Comment Any Key down related method now addToBackStack will work . thanks

Omit answered 16/8, 2019 at 15:43 Comment(0)
T
-4

Here is my solution for that issue:

in Activity A:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
{
    super.onActivityResult(requestCode, resultCode, data);

    if(requestCode == REQUEST_CODE)
    {
        if(resultCode == Activity.RESULT_OK)
        {
            tvTitle.setText(data.getExtras().getString("title", ""));
        }
    }
}

in Activity B:

@Override
public void onBackPressed() 
{
    setResult(Activity.RESULT_OK, getIntent());

    super.onBackPressed();
}

activity b holds the fragment.

in fragment:

private void setText(String text)
    {
        Intent intent = new Intent();
        intent.putExtra("title", text);
        getActivity().setIntent(intent);
    }

in that way the Intent Object "data" in activity A will get the string from the fragment

Thimbleful answered 4/12, 2013 at 16:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.