Android Fragment handle back button press

AndroidAndroid Fragments

Android Problem Overview


I have some fragments in my activity

[1], [2], [3], [4], [5], [6]

And on Back Button Press I must to return from [2] to [1] if current active fragment is [2], or do nothing otherwise.

What is the best practise to do that?

EDIT: Application must not return to [2] from [3]...[6]

Android Solutions


Solution 1 - Android

When you are transitioning between Fragments, call addToBackStack() as part of your FragmentTransaction:

FragmentTransaction tx = fragmentManager.beginTransation();
tx.replace( R.id.fragment, new MyFragment() ).addToBackStack( "tag" ).commit();

If you require more detailed control (i.e. when some Fragments are visible, you want to suppress the back key) you can set an OnKeyListener on the parent view of your fragment:

//You need to add the following line for this solution to work; thanks skayred
fragment.getView().setFocusableInTouchMode(true);
fragment.getView().requestFocus();
fragment.getView().setOnKeyListener( new OnKeyListener()
{
	@Override
	public boolean onKey( View v, int keyCode, KeyEvent event )
	{
		if( keyCode == KeyEvent.KEYCODE_BACK )
		{
			return true;
		}
		return false;
	}
} );

Solution 2 - Android

I'd rather do something like this:

private final static String TAG_FRAGMENT = "TAG_FRAGMENT";
	
private void showFragment() {
	final Myfragment fragment = new MyFragment();
	final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
	transaction.replace(R.id.fragment, fragment, TAG_FRAGMENT);
	transaction.addToBackStack(null);
	transaction.commit();
}

@Override
public void onBackPressed() {
	final Myfragment fragment = (Myfragment) getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT);

	if (fragment.allowBackPressed()) { // and then you define a method allowBackPressed with the logic to allow back pressed or not
		super.onBackPressed();
	}
}

Solution 3 - Android

if you overide the onKey method for the fragment view you're gonna need :

    view.setFocusableInTouchMode(true);
    view.requestFocus();
    view.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                Log.i(tag, "keyCode: " + keyCode);
                if( keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
                    Log.i(tag, "onKey Back listener is working!!!");
                    getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                    return true;
                } 
                return false;
            }
        });

Solution 4 - Android

Use addToBackStack method when replacing one fragment by another:

getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment).addToBackStack("my_fragment").commit();

Then in your activity, use the following code to go back from a fragment to another (the previous one).

@Override
public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() > 0) {
        getFragmentManager().popBackStack();
    } else {
        super.onBackPressed();
    }
}

Solution 5 - Android

If you want to handle hardware Back key event than you have to do following code in your onActivityCreated() method of Fragment.

You also need to check Action_Down or Action_UP event. If you will not check then onKey() Method will call 2 times.

Also, If your rootview(getView()) will not contain focus then it will not work. If you have clicked on any control then again you need to give focus to rootview using getView().requestFocus(); After this only onKeydown() will call.

getView().setFocusableInTouchMode(true);
getView().requestFocus();

getView().setOnKeyListener(new OnKeyListener() {
		@Override
		public boolean onKey(View v, int keyCode, KeyEvent event) {
				if (event.getAction() == KeyEvent.ACTION_DOWN) {
					if (keyCode == KeyEvent.KEYCODE_BACK) {
						Toast.makeText(getActivity(), "Back Pressed", Toast.LENGTH_SHORT).show();
					return true;
					}
				}
				return false;
			}
		});

Working very well for me.

Solution 6 - Android

Create interfaces:

BackButtonHandlerInterface

public interface BackButtonHandlerInterface {
    void addBackClickListener (OnBackClickListener onBackClickListener);
    void removeBackClickListener (OnBackClickListener onBackClickListener);
}

OnBackClickListener

public interface OnBackClickListener {
     boolean onBackClick();
}

In Activity:

public class MainActivity extends AppCompatActivity implements BackButtonHandlerInterface {

    private ArrayList<WeakReference<OnBackClickListener>> backClickListenersList = new ArrayList<>();

    @Override
    public void addBackClickListener(OnBackClickListener onBackClickListener) {
        backClickListenersList.add(new WeakReference<>(onBackClickListener));
    }

    @Override
    public void removeBackClickListener(OnBackClickListener onBackClickListener) {
        for (Iterator<WeakReference<OnBackClickListener>> iterator = backClickListenersList.iterator();
         iterator.hasNext();){
            WeakReference<OnBackClickListener> weakRef = iterator.next();
            if (weakRef.get() == onBackClickListener){
                iterator.remove();
            }
        }
    }

    @Override
    public void onBackPressed() {
        if(!fragmentsBackKeyIntercept()){
            super.onBackPressed();
        }
    }

    private boolean fragmentsBackKeyIntercept() {
        boolean isIntercept = false;
        for (WeakReference<OnBackClickListener> weakRef : backClickListenersList) {
            OnBackClickListener onBackClickListener = weakRef.get();
            if (onBackClickListener != null) {
                boolean isFragmIntercept = onBackClickListener.onBackClick();
                if (!isIntercept) isIntercept = isFragmIntercept;
            }
        }
        return isIntercept;
    }
}

In Fragment:

public class MyFragment extends Fragment implements OnBackClickListener{

    private BackButtonHandlerInterface backButtonHandler;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        backButtonHandler = (BackButtonHandlerInterface) activity;
        backButtonHandler.addBackClickListener(this);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        backButtonHandler.removeBackClickListener(this);
        backButtonHandler = null;
    }

    @Override
    public boolean onBackClick() {
        //This method handle onBackPressed()! return true or false
        return false;
    }

}

Update

Provide custom back navigation

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
    }

}

Solution 7 - Android

The most ideal way of doing this is found here: https://stackoverflow.com/questions/9703498/fragment-which-callback-invoked-when-press-back-button-customize-it/10100254#10100254

public class MyActivity extends Activity
{
    //...
    //Defined in Activity class, so override
    @Override
    public void onBackPressed()
    {
        super.onBackPressed();
        myFragment.onBackPressed();
    }
}

public class MyFragment extends Fragment
{
    //Your created method
    public static void onBackPressed()
    {
        //Pop Fragments off backstack and do your other checks
    }
}

Solution 8 - Android

 @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){

                    if (mDrawerLayout.isDrawerOpen(GravityCompat.START)){
                        mDrawerLayout.closeDrawer(GravityCompat.START);
                    }
                    return true;
                }

                return false;
            }
        });
    }

Solution 9 - Android

After looking at all solutions, I realised there is a much simpler solution.

In your activity's onBackPressed() that is hosting all your fragments, find the fragment that you want to prevent back press. Then if found, just return. Then popBackStack will never happen for this fragment.

  @Override
public void onBackPressed() {
   
        Fragment1 fragment1 = (Fragment1) getFragmentManager().findFragmentByTag(“Fragment1”);
        if (fragment1 != null)
            return;

        if (getFragmentManager().getBackStackEntryCount() > 0){
            getFragmentManager().popBackStack();
            
        }
}

Solution 10 - Android

We created tiny library for handling back press across multiple fragments and/or in Activity. Usage is as simple as adding dependency in your gradle file:

compile 'net.skoumal.fragmentback:fragment-back:0.1.0'

Let your fragment implement BackFragment interface:

public abstract class MyFragment extends Fragment implements BackFragment {

    public boolean onBackPressed() {

        // -- your code --

        // return true if you want to consume back-pressed event
        return false;
    }

    public int getBackPriority() {
        return NORMAL_BACK_PRIORITY;
    }
}

Notify your fragments about back presses:

public class MainActivity extends AppCompatActivity {

    @Override
    public void onBackPressed() {
        // first ask your fragments to handle back-pressed event
        if(!BackFragmentHelper.fireOnBackPressedEvent(this)) {
            // lets do the default back action if fragments don't consume it
            super.onBackPressed();
        }
    }
}

For more details and other use-cases visit GitHub page:

https://github.com/skoumalcz/fragment-back

Solution 11 - Android

Or you could use getSupportFragmentManager().getBackStackEntryCount() to check what to do:

@Override
    public void onBackPressed() {

        logger.d("@@@@@@ back stack entry count : " + getSupportFragmentManager().getBackStackEntryCount());

        if (getSupportFragmentManager().getBackStackEntryCount() != 0) {

            // only show dialog while there's back stack entry
            dialog.show(getSupportFragmentManager(), "ConfirmDialogFragment");

        } else if (getSupportFragmentManager().getBackStackEntryCount() == 0) {

            // or just go back to main activity
            super.onBackPressed();
        }
    }

Solution 12 - Android

If you manage the flow of adding to back stack every transaction, then you can do something like this in order to show the previous fragment when the user presses back button (you could map the home button too).

@Override
public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() > 0)
        getFragmentManager().popBackStack();
    else
        super.onBackPressed();
}

Solution 13 - Android

Working Code:

package com.example.keralapolice;

import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentManager.OnBackStackChangedListener;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

public class ChiefFragment extends Fragment {
    View view;

    // public OnBackPressedListener onBackPressedListener;

    @Override
	public View onCreateView(LayoutInflater inflater,
            ViewGroup container, Bundle args) {

		view = inflater.inflate(R.layout.activity_chief, container, false);
    	getActivity().getActionBar().hide();
	    view.setFocusableInTouchMode(true);
		view.requestFocus();
    	view.setOnKeyListener(new View.OnKeyListener() {
	    	@Override
		    public boolean onKey(View v, int keyCode, KeyEvent event) {
			    Log.i(getTag(), "keyCode: " + keyCode);
				if (keyCode == KeyEvent.KEYCODE_BACK) {
    				getActivity().getActionBar().show();
	    			Log.i(getTag(), "onKey Back listener is working!!!");
		    		getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
				    // String cameback="CameBack";
					Intent i = new Intent(getActivity(), home.class);
    				// i.putExtra("Comingback", cameback);
	    			startActivity(i);
		    		return true;
			    } else {
				    return false;
			    }
		    }
	    });
	    return view;
    }
}

Solution 14 - Android

I think the easiest way is to create an interface, and in the Activity check if the fragment is of the interface type, and if so, call its method to handle the pop. Here's the interface to implement in the fragment.

public interface BackPressedFragment {

    // Note for this to work, name AND tag must be set anytime the fragment is added to back stack, e.g.
    // getActivity().getSupportFragmentManager().beginTransaction()
    //                .replace(R.id.fragment_container, MyFragment.newInstance(), "MY_FRAG_TAG")
    //                .addToBackStack("MY_FRAG_TAG")
    //                .commit();
    // This is really an override. Should call popBackStack itself.
    void onPopBackStack();
}

Here's how to implement it.

public class MyFragment extends Fragment implements BackPressedFragment
    @Override
    public void onPopBackStack() {
        /* Your code goes here, do anything you want. */
        getActivity().getSupportFragmentManager().popBackStack();
}

And in your Activity, when you handle the pop (likely in both onBackPressed and onOptionsItemSelected), pop the backstack using this method:

public void popBackStack() {
    FragmentManager fm = getSupportFragmentManager();
    // Call current fragment's onPopBackStack if it has one.
    String fragmentTag = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 1).getName();
    Fragment currentFragment = getSupportFragmentManager().findFragmentByTag(fragmentTag);
    if (currentFragment instanceof BackPressedFragment)
        ((BackPressedFragment)currentFragment).onPopBackStack();
    else
        fm.popBackStack();
}

Solution 15 - Android

I'm working with SlidingMenu and Fragment, present my case here and hope helps somebody.

Logic when [Back] key pressed :

  1. When SlidingMenu shows, close it, no more things to do.

  2. Or when 2nd(or more) Fragment showing, slide back to previous Fragment, and no more things to do.

  3. SlidingMenu not shows, current Fragment is #0, do the original [Back] key does.

    public class Main extends SherlockFragmentActivity
    {
      private SlidingMenu menu=null;
      Constants.VP=new ViewPager(this);
    
      //Some stuff...
    
      @Override
      public void onBackPressed()
      {
        if(menu.isMenuShowing())
        {
          menu.showContent(true); //Close SlidingMenu when menu showing
          return;
        }
        else
        {
          int page=Constants.VP.getCurrentItem();
          if(page>0)
          {
            Constants.VP.setCurrentItem(page-1, true); //Show previous fragment until Fragment#0
            return;
          }
          else
          {super.onBackPressed();} //If SlidingMenu is not showing and current Fragment is #0, do the original [Back] key does. In my case is exit from APP
        }
      }
    }
    

Solution 16 - Android

This is a very good and reliable solution: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/

The guy has made an abstract fragment that handles the backPress behaviour and is switching between the active fragments using the strategy pattern.

For some of you there maybe a little drawback in the abstract class...

Shortly, the solution from the link goes like this:

// Abstract Fragment handling the back presses

public abstract class BackHandledFragment extends Fragment {
    protected BackHandlerInterface backHandlerInterface;
    public abstract String getTagText();
    public abstract boolean onBackPressed();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(!(getActivity()  instanceof BackHandlerInterface)) {
            throw new ClassCastException("Hosting activity must implement BackHandlerInterface");
        } else {
            backHandlerInterface = (BackHandlerInterface) getActivity();
        }
    }

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

        // Mark this fragment as the selected Fragment.
        backHandlerInterface.setSelectedFragment(this);
    }

    public interface BackHandlerInterface {
        public void setSelectedFragment(BackHandledFragment backHandledFragment);
    }
}   

And usage in the activity:

// BASIC ACTIVITY CODE THAT LETS ITS FRAGMENT UTILIZE onBackPress EVENTS 
// IN AN ADAPTIVE AND ORGANIZED PATTERN USING BackHandledFragment

public class TheActivity extends FragmentActivity implements BackHandlerInterface {
    private BackHandledFragment selectedFragment;

    @Override
    public void onBackPressed() {
        if(selectedFragment == null || !selectedFragment.onBackPressed()) {
            // Selected fragment did not consume the back press event.
            super.onBackPressed();
        }
    }

    @Override
    public void setSelectedFragment(BackHandledFragment selectedFragment) {
        this.selectedFragment = selectedFragment;
    }
}

Solution 17 - Android

For Those Who Use Static Fragment

In a case if you have a static fragment then It would be preferable. Make an instance object of your fragment

private static MyFragment instance=null;

in onCreate() of MyFragment initialize that instance

  instance=this;

also make a function to get Instance

 public static MyFragment getInstance(){
   return instance;
}

also make functions

public boolean allowBackPressed(){
    if(allowBack==true){
        return true;
    }
    return false;
}


 //allowBack is a boolean variable that will be set to true at the action 
 //where you want that your backButton should not close activity. In my case I open 
 //Navigation Drawer then I set it to true. so when I press backbutton my 
 //drawer should be get closed

public void performSomeAction(){
    //.. Your code
    ///Here I have closed my drawer
}

In Your Activity You can do

@Override
public void onBackPressed() {

    if (MyFragment.getInstance().allowBackPressed()) { 
        MyFragment.getInstance().performSomeAction();
    }
    else{
        super.onBackPressed();
    }
}

Solution 18 - Android

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


                Fragment NameofFragment = new NameofFragment;

                FragmentTransaction  transaction=getFragmentManager().beginTransaction();
                transaction.replace(R.id.frame_container,NameofFragment);

                transaction.commit();

                return true;
            }
            return false;
        }
    });

    return rootView;

Solution 19 - Android

You can use from getActionBar().setDisplayHomeAsUpEnabled() :

@Override
public void onBackStackChanged() {
	int backStackEntryCount = getFragmentManager().getBackStackEntryCount();
	
	if(backStackEntryCount > 0){
		getActionBar().setDisplayHomeAsUpEnabled(true);
	}else{
		getActionBar().setDisplayHomeAsUpEnabled(false);
	}
}

Solution 20 - Android

Add addToBackStack() to fragment transaction and then use below code for Implementing Back Navigation for Fragments

getSupportFragmentManager().addOnBackStackChangedListener(
    new FragmentManager.OnBackStackChangedListener() {
        public void onBackStackChanged() {
            // Update your UI here.
        }
    });

Solution 21 - Android

if you are using FragmentActivity. then do like this

first call This inside your Fragment.

public void callParentMethod(){
    getActivity().onBackPressed();
}

and then Call onBackPressed method in side your parent FragmentActivity class.

@Override
public void onBackPressed() {
  //super.onBackPressed();
  //create a dialog to ask yes no question whether or not the user wants to exit
  ...
}

Solution 22 - Android

Add this code in your Activity

@Override

public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() == 0) {
        super.onBackPressed();
    } else {
        getFragmentManager().popBackStack();
    }
}

And add this line in your Fragment before commit()

ft.addToBackStack("Any name");

Solution 23 - Android

in fragment class put this code for back event:

 rootView.setFocusableInTouchMode(true);
    	rootView.requestFocus();
    	rootView.setOnKeyListener( new OnKeyListener()
    	{
    	    @Override
    	    public boolean onKey( View v, int keyCode, KeyEvent event )
    	    {
    	        if( keyCode == KeyEvent.KEYCODE_BACK )
    	        {
    	        	FragmentManager fragmentManager = getFragmentManager();
    				fragmentManager.beginTransaction()
    						.replace(R.id.frame_container, new Book_service_provider()).commit();

    	        	return true;
    	        }
    	        return false;
    	    }
    	} );

Solution 24 - Android

Checking the backstack works perfectly


@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
	if (keyCode == KeyEvent.KEYCODE_BACK)
	{
		if (getFragmentManager().getBackStackEntryCount() == 1)
		{
			// DO something here since there is only one fragment left
            // Popping a dialog asking to quit the application
			return false;
		}
	}
	return super.onKeyDown(keyCode, event);
}

Solution 25 - Android

In your oncreateView() method you need to write this code and in KEYCODE_BACk condition you can write whatever the functionality you want

View v = inflater.inflate(R.layout.xyz, container, false);
//Back pressed Logic for fragment 
v.setFocusableInTouchMode(true); 
v.requestFocus(); 
v.setOnKeyListener(new View.OnKeyListener() { 
    @Override 
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                getActivity().finish(); 
                Intent intent = new Intent(getActivity(), MainActivity.class);
                startActivity(intent);

                return true; 
            } 
        } 
        return false; 
    } 
}); 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionskayredView Question on Stackoverflow
Solution 1 - AndroidMark AllisonView Answer on Stackoverflow
Solution 2 - AndroidPedro AndradeView Answer on Stackoverflow
Solution 3 - AndroidANematiView Answer on Stackoverflow
Solution 4 - AndroidshimataiView Answer on Stackoverflow
Solution 5 - AndroidTejas MehtaView Answer on Stackoverflow
Solution 6 - AndroidkrawaView Answer on Stackoverflow
Solution 7 - AndroidkakomaView Answer on Stackoverflow
Solution 8 - AndroidRanjit KathiriyaView Answer on Stackoverflow
Solution 9 - AndroidHarry AungView Answer on Stackoverflow
Solution 10 - AndroidgingoView Answer on Stackoverflow
Solution 11 - AndroidRobertView Answer on Stackoverflow
Solution 12 - AndroidJoaquin IurchukView Answer on Stackoverflow
Solution 13 - AndroidAndroidView Answer on Stackoverflow
Solution 14 - AndroidMatt KoalaView Answer on Stackoverflow
Solution 15 - AndroidRRTWView Answer on Stackoverflow
Solution 16 - AndroidAmio.ioView Answer on Stackoverflow
Solution 17 - AndroidMuhammad AdilView Answer on Stackoverflow
Solution 18 - AndroidAshish SoniView Answer on Stackoverflow
Solution 19 - Androiduser4501847View Answer on Stackoverflow
Solution 20 - AndroidLalit SharmaView Answer on Stackoverflow
Solution 21 - AndroidhashView Answer on Stackoverflow
Solution 22 - AndroidRaviView Answer on Stackoverflow
Solution 23 - Androidtej shahView Answer on Stackoverflow
Solution 24 - AndroidDevrathView Answer on Stackoverflow
Solution 25 - AndroidRaj KumarView Answer on Stackoverflow