I have a problem with the back/up button in a Fragment.
I have an Activity in which I have some fragments. In one Fragment, that I call "1", I have a list view. When I click on any item it goes to another fragment "2".
I need functionality such that the back/up button only works in Fragment 2 but not in Fragment 1.
Is there a way this can be done?
I have tried this in the Activity, but I don't understand how can this help:
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
if (count == 0) {
super.onBackPressed();
//additional code
} else {
getFragmentManager().popBackStack();
}
}
When i change from fragment 1 to fragment 2 i have this code:
fragment = new MaterialesFragment();
FragmentManager fragmentManager3 = getFragmentManager();
fragmentManager3.beginTransaction().replace(R.id.frame, fragment).addToBackStack("tagMateriales").commit();
Thanks a lot :-)
Try to use addToBackStack() method on the FragmentTransaction object you use to replace the fragment2 into the screen:
fragmentManager.beginTransaction().replace(R.id.content_frame,fragment2).addToBackStack("tagHere").commit();
You can pass null as the parameter if you don't need the actual tag.
Please use this code for your 'fragment 2'
public void showFragmentWithoutStack(Fragment fragment, String tag, int id) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.add(id, fragment, tag);
transaction.commitAllowingStateLoss();
}
Where tag might be String class name, id of your root view in activity (Because fragment should appear on the top layer).
There are a lot of way to do this job. In my opinion the most elegant is the following. I would isolate the logic in each component. Every fragment implements an interface like "IBackHandler" that allows activity and fragment to communicate. In the activity onBackPressed asks first the fragment (if it is instantiated) what to do and then acts.
The interface:
public interface IBackHandler {
boolean doBack();
}
The fragment that implements that interface and put its logic to handle the back button pressed:
public class YourFragment1 extends YourBaseFragment implements IBackHandler {
// your code
#Override
public boolean doBack() {
// return true means that you have done your stuff
//and you don't want 'super.onBackPressed()' to be called
return true; // this means do nothing
}
}
Fragment 2:
public class YourFragment2 extends YourBaseFragment implements IBackHandler {
// your code
#Override
public boolean doBack() {
return false; //this means: call 'super.onBackPressed()'
}
}
Your Activity:
public class YourActivity extends YourBaseActivity {
//your code
#Override
public void onBackPressed() {
Fragment currentFragment = getFragmentManager().findFragmentById(R.id.yourFragment);
if (currentFragment instanceof IBackHandler) {
if (!((IBackHandler) currentFragment).doBack()) {
super.onBackPressed();
}
} else
super.onBackPressed();
}
}
You could think that this is overengineering but I can reassure you that in this way you'll always be able to control what'happening in your flow.
Hope this help!
In order to use the onBackPressed() method override in the Activity as you have in the question, you also need to add this to your Fragment:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// Assuming MainActivity is the Activity subclass name
if (getActivity() instanceof MainActivity) {
((MainActivity) getActivity()).onBackPressed();
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
In order to show/hide the back button, add these methods to your Activity:
public void showUpButton() {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
public void hideUpButton() {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
}
Then, when you want to show or hide the back/up button from a Fragment, call these methods.
In your case, you could hide the back/up button from onResume() in Fragment 1:
((MainActivity) getActivity()).hideUpButton();
And then show it in onResume() in Fragment 2:
((MainActivity) getActivity()).showUpButton();
I solved this problem use this code when change fragment:
String fragmentIndexString="fragmenttwo";//or fragmentone something
transaction.addToBackStack();
This is full code in Activity:
FirstFragment firstFragment; // my first fragment
SecondFragment secondFragment; // my second fragment
public static String FIRST="asfasgasg"; //first fragment index
public static String SECOND="gsadgsagd"; //second fragment index
...
/* u can change fragment by using setFragment funiction */
public void setFragment(String page){
FragmentTransaction transaction = fragmentManager.beginTransaction();
switch (page){
case FIRST:
if(firstFragment==null) firstFragment= SettingFragment.newInstance();
if (!firstFragment.isAdded()) {
transaction.replace(R.id.fragmentcontainer, firstFragment);
transaction.addToBackStack(INDEX);
transaction.commit();
} else {
transaction.show(firstFragment);
}
break;
case SECOND:
if(secondFragment==null) secondFragment= SettingFragment.newInstance();
if (!secondFragment.isAdded()) {
transaction.replace(R.id.fragmentcontainer, secondFragment);
transaction.addToBackStack(INDEX);
transaction.commit();
} else {
transaction.show(secondFragment);
}
break;
default:
setFragment(INDEX);
}
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN){
if(fragmentManager.getBackStackEntryCount()>1){
fragmentManager.popBackStack();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
Related
How can we get the onBackPressed callback only in the fragment class? Instead ,now it is going to the onBackPressed in the activity classes? I need to restrict sending the callback to Activity on backpress from fragment.
example::
#Override
public void onBackPressed() {
Log.d("SKT", "backPressHere");
}
Implement this in the Activity and restriction part you can handle via the interface.
The fragment itself does not have a return button, but instead you can put this code in the onBackPressed() method in the activity to which the fragment is attached, and with the getSupportFragmentManager() method you can control the examples of running fragments or delete and return do.
here is example :
#Override
public void onBackPressed() {
int count = getSupportFragmentManager().getBackStackEntryCount();
if (count == 0) {
// no fragment attach
super.onBackPressed();
} else {
// fragment attach
getSupportFragmentManager().popBackStack();
}
}
The fragment does not receive the onBackPressed callback. You should implement dispatching the event in the activity.
public interface BackPressedSupport{
/**
* return true if the fragment consumed the event. otherwise false.
*/
boolean onBackPressed();
}
In your Activity :
#Override
public void onBackPressed() {
Fragment f = getActiveFragment();
if (f instanceof BackPressedSupport && (BackPressedSupport f).onBackPressed()){
return;
}
super.onBackPressed();
// Other backPressed handling of activity.
}
public Fragment getActiveFragment() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
String tag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
return getSupportFragmentManager().findFragmentByTag(tag);
}
In your Fragment:
public MyFragment extends Fragment implements BackPressedSupport {
/* Your code */
#Override
public boolean onBackPressed() {
/* Handle backpressed */
Log.d("SKT", "backPressHere");
return true ; // tell the activity to stop handling this backPressed event
// return false; // keep the default behaviour
}
}
I have a bottom navigation bar that contains 4 fragments and when a tab is selected a new instance of that fragment is loaded.
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = HomeFragment.newInstance();
break;
case R.id.navigation_cards:
fragment = CardsFragment.newInstance();
break;
case R.id.navigation_deals:
fragment = DealsFragment.newInstance();
break;
case R.id.navigation_settings:
fragment = SettingsFragment.newInstance();
break;
}
if (fragment != null) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content, fragment);
fragmentTransaction.commit();
}
return true;
}
};
Now in my HomeFragment there is a RecyclerView that On Item Select it opens a new Fragment:
myViewHolder.cardView.setOnClickListener(view -> {
System.out.println("clicked");
Fragment fragment = new TargetDetailsFragment();
FragmentTransaction ft = ((AppCompatActivity) context).getSupportFragmentManager()
.beginTransaction();
ft.replace(R.id.content, fragment).addToBackStack(null);
ft.commit();
});
I want to add a back button to the TargetDetails Fragments that takes you back to the home page when selected and I attempted doing that by implementing OnBackStackChangedListener in the Main activity
#Override
public void onBackStackChanged() {
shouldDisplayHomeUp();
}
public void shouldDisplayHomeUp(){
//Enable Up button only if there are entries in the back stack
boolean canback = getSupportFragmentManager().getBackStackEntryCount()>0;
getSupportActionBar().setDisplayHomeAsUpEnabled(canback);
}
#Override
public boolean onSupportNavigateUp() {
//This method is called when the up button is pressed. Just the pop back stack.
getSupportFragmentManager().popBackStack();
return true;
}
}
but the problem is when I click on it its reloads the HomeFragment Again but I simply want it to go back to the saved instance of that Fragment
Here is my code that I have used for the ViewPager, the idea is to see if the current page number is 0 than to proceed super.onBackPressed(); otherwise go to the previous fragment:
#Override
public void onBackPressed() {
if(vpPager.getCurrentItem()!=0) {
vpPager.setCurrentItem(vpPager.getCurrentItem()-1, true);
} else {
super.onBackPressed();
}
}
By adding below code in your Activity.
The fragment back stack can be managed.
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
if (count == 0) {
super.onBackPressed();
//additional code
} else {
getFragmentManager().popBackStack();
}
}
Hello #Bolu Okunaiya i think you should try this it will help you to manage backstack to desired fragment without loading same fragment again.
For Stop loading previous fragment you should use "add" instead of "replace" with your FragmentTransaction
Inside your MainActivity
#Override
public void onBackStackChanged() {
//shouldDisplayHomeUp();
Fragment currentFragment = getActivity().getFragmentManager()
.findFragmentById(R.id.fragment_container);
if (currentFragment instanceof TargetDetails) {
Log.v(TAG, "your current fragment is TargetDetails");
popBackStackImmediate(); //<<<< immediate parent fragment will open,which is your HomeFragement
}
}
To use popBackStackImmediate() you need to *replace FragmentA with FragmentB and use addToBackstack() before commit().
I am trying to override onbackpressed method inside fragment . But it gives me syntax error that it should override a super method in interface. Why so? I have other methods like ondestroy also in my fragment class but no error. Why for this backpressed alone. I tried onkeydown also. Same error. Pasting below my code.
public TestClass extends Fragment implements Testinterface
{ #Override //error must override or implement supertype method
public void onBackPressed ()
{
if (check)
Do somethin
else
getActivity().finish ()
//super.onBackPressed () // error here if I use this
}
You have to implement on key down in fragment, check for key code. The onBackPress() method can be used in an Activity -- which is the logical parent of your fragment.
Try this:
frag.getView().setFocusableInTouchMode(true);
frag.getView().setOnKeyListener( new OnKeyListener(){
#Override
public boolean onKey( View v, int keyCode, KeyEvent event ){
if( keyCode == KeyEvent.KEYCODE_BACK ){
return true;
}
return false;
}
} );
You can propagate onBackPressed() to all your fragments,for that you need to create two classes with the following methods and then make all your activties and fragments inherit from them:
public class BaseActivity extends ActionBarActivity {
#Override
public void onBackPressed() {
boolean eventConsumed = false;
List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment instanceof BaseFragment) {
eventConsumed = eventConsumed
|| ((BaseFragment) fragment).onBackPressed();
}
}
}
if (!eventConsumed) {
super.onBackPressed();
}
}
}
public class BaseFragment extends Fragment {
public boolean onBackPressed() {
return false;
}
}
Note that this code is using the support library, if you are not using it you need to do the appropriate changes.
Try to avoid using onKey in fragment. There is a better way.
For maintenance, I recommend you to use getBackStackEntryCount()
in Activity
final FragmentMananger fm = new getSupportFragmentManager();
#Override
protected void onCreate(Bundle b){
super.onCreate(b);
setContentView(R.layout.activity_main);
Fragment fragment = new SomeFragment();
// if you don't run on prior to Android 3.0 use getFragmentManager();
fm.beginTransaction().replace(R.id.frame_container, fragment).addToBackStack(null).commit();
// R.id.frame_container is the id of FrameLayout in activity_main.xml
}
#Override
public void onBackPressed(){
if(fm.getBackStackEntryCount() == 0){
finish();
}else{
super.onBackPressed();
}
}
I am working on a project and I need to be able to use the back button in each fragment to navigate between previous fragments, I have methods written to do so by using a back arrow in the action bar, however, I want to be able to use the same functionality on the back button pressed. I don't want to use the back stack. Is there a way to do this?
EDIT
Rather than using the back stack I want to be able to call the go back to previous method below when the user clicks the back button. I need to used the gobackpressed method within fragments. Is this possible? I hope this is clear and concise. Apologies for any confusion caused above.
Go Back to Previous
public void gobackToPreviousFragment(String preFragmentTag, Fragment preFragment){
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.close_slide_in,R.animator.close_slide_out);
ft.show(preFragment);
//**BY REMOVING FRAGMENT, WHEN USER TRIES TO REVISIT, FRAGMENT IS BLACK**
ft.remove(fm.findFragmentByTag(Misc.currentContentFragmentTag));
ft.addToBackStack(null);
ft.commit();
Misc.currentContentFragmentTag = preFragmentTag;
createBar(preFragment);
}
Go Forward
public void gotoNextFragment(String nextTag, Fragment nextFragment){
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.enter_slide_in, R.animator.enter_slide_out);
boolean newlyCreated = false;
if(nextFragment == null){
nextFragment = Fragment.instantiate(this, nextTag);
newlyCreated = true;
}
//hide current fragment
ft.hide(fm.findFragmentByTag(Misc.currentContentFragmentTag));
if(newlyCreated){
ft.add(R.id.content_frame, nextFragment, nextTag);
}
else{
ft.show(nextFragment);
}
ft.addToBackStack(null);
ft.commit();
Misc.currentContentFragmentTag = nextTag;
createBar(nextFragment);
}
These are how I navigate back and forth, and I'd like to be able to implement the go back method on the onBackPressed(). Does this make sense?
I didn't find any good answer about this problem, so this is my solution.
If you want to get backPress in each fragment do the following.
create interface OnBackPressedListener
public interface OnBackPressedListener {
void onBackPressed();
}
That each fragment that wants to be informed of backPress implements this interface.
In parent activity , you can override onBackPressed()
#Override
public void onBackPressed() {
List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
if (fragmentList != null) {
//TODO: Perform your logic to pass back press here
for(Fragment fragment : fragmentList){
if(fragment instanceof OnBackPressedListener){
((OnBackPressedListener)fragment).onBackPressed();
}
}
}
}
Why don't you want to use the back stack? If there is an underlying problem or confusion maybe we can clear it up for you.
If you want to stick with your requirement just override your Activity's onBackPressed() method and call whatever method you're calling when the back arrow in your ActionBar gets clicked.
EDIT: How to solve the "black screen" fragment back stack problem:
You can get around that issue by adding a backstack listener to the fragment manager. That listener checks if the fragment back stack is empty and finishes the Activity accordingly:
You can set that listener in your Activity's onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
fm.addOnBackStackChangedListener(new OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if(getFragmentManager().getBackStackEntryCount() == 0) finish();
}
});
}
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
Log.w("a","")
}
})
In the fragment where you would like to handle your back button you should attach stuff to your view in the oncreateview
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.second_fragment, container, false);
v.setOnKeyListener(pressed);
return v;
}
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if( keyCode == KeyEvent.KEYCODE_BACK ){
// back to previous fragment by tag
myfragmentclass fragment = (myfragmentclass) getActivity().getSupportFragmentManager().findFragmentByTag(TAG);
if(fragment != null){
(getActivity().getSupportFragmentManager().beginTransaction()).replace(R.id.cf_g1_mainframe_fm, fragment).commit();
}
return true;
}
return false;
}
};
This works for me :D
#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;
}
});}
You can try to override onCreateAnimation, parameter and catch enter==false. This will fire before every back press.
#Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
if(!enter){
//leaving fragment
Log.d(TAG,"leaving fragment");
}
return super.onCreateAnimation(transit, enter, nextAnim);
}
For a Fragment you can simply add
getActivity().onBackPressed();
to your code
I found a new way to do it without interfaces. You only need to add the below code to the Fragment’s onCreate() method:
//overriding the fragment's oncreate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//calling onBackPressedDispatcher and adding call back
requireActivity().onBackPressedDispatcher.addCallback(this) {
//do stuff here
}
}
Use this:
#Override
public void onBackPressed() {
int fragments = getFragmentManager().getBackStackEntryCount();
if (fragments == 1) {
finish();
}
super.onBackPressed();
}
I have a tabbed Actionbar/viewpager layout with three tabs say A, B, and C. In tab C tab(fragment),I am adding another fragment say fragment D. with
DFragment f= new DFragment();
ft.add(android.R.id.content, f, "");
ft.remove(CFragment.this);
ft.addToBackStack(null);
ft.commit();
I modify actionbar in DFragment's onResume to add up button:
ActionBar ab = getActivity().getActionBar();
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);
Now in DFragment, when I press hardware(phone) Back button, I return to the original Tabbed(ABC) layout with CFragment selected. How can I achieve this functionality with actionbar up button?
Implement OnBackStackChangedListener and add this code to your Fragment Activity.
#Override
public void onCreate(Bundle savedInstanceState) {
//Listen for changes in the back stack
getSupportFragmentManager().addOnBackStackChangedListener(this);
//Handle when activity is recreated like on orientation Change
shouldDisplayHomeUp();
}
#Override
public void onBackStackChanged() {
shouldDisplayHomeUp();
}
public void shouldDisplayHomeUp(){
//Enable Up button only if there are entries in the back stack
boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
getSupportActionBar().setDisplayHomeAsUpEnabled(canGoBack);
}
#Override
public boolean onSupportNavigateUp() {
//This method is called when the up button is pressed. Just the pop back stack.
getSupportFragmentManager().popBackStack();
return true;
}
I got it. just override onOptionsItemSelected in hosting activity and popup the backstack, e.g.
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: {
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
return true;
}
break;
}
}
return super.onOptionsItemSelected(item);
}
Call getActionBar().setDisplayHomeAsUpEnabled(boolean); and getActionBar().setHomeButtonEnabled(boolean); in onBackStackChanged() as explained in an answer below.
If you have one parent activity and want this up button to work as a back button, you can use this code:
add this to the onCreate in your main activity class
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
int stackHeight = getSupportFragmentManager().getBackStackEntryCount();
if (stackHeight > 0) { // if we have something on the stack (doesn't include the current shown fragment)
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setHomeButtonEnabled(false);
}
}
});
and then add onOptionsItemSelected like so:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getSupportFragmentManager().popBackStack();
return true;
....
}
I generally use this all the time and seems pretty legit
you can go back with up button like back button ;
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
super.onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
I used a combination of Roger Garzon Nieto's and sohailaziz's answers. My app has a single MainActivity, and fragments A, B, C that are loaded into it. My "home" fragment (A) implements OnBackStackChangedListener, and checks the size of the backStack; if it's less than one, then it hides the UP button. Fragments B and C always load the back button (in my design, B is launched from A, and C is launched from B). The MainActivity itself just pops the backstack on UP button tap, and has methods to show/hide the button, which the fragments call:
MainActivity:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
getSupportFragmentManager().popBackStack();
return true;
}
return super.onOptionsItemSelected(item);
}
public void showUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(true); }
public void hideUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(false); }
fragmentA (implements FragmentManager.OnBackStackChangedListener):
public void onCreate(Bundle savedinstanceSate) {
// listen to backstack changes
getActivity().getSupportFragmentManager().addOnBackStackChangedListener(this);
// other fragment init stuff
...
}
public void onBackStackChanged() {
// enable Up button only if there are entries on the backstack
if(getActivity().getSupportFragmentManager().getBackStackEntryCount() < 1) {
((MainActivity)getActivity()).hideUpButton();
}
}
fragmentB, fragmentC:
public void onCreate(Bundle savedinstanceSate) {
// show the UP button
((MainActivity)getActivity()).showUpButton();
// other fragment init stuff
...
}
I know this question is old, but may be someone (like me) also needs it.
If your Activity extends AppCompatActivity, you can use a simpler (two-step) solution:
1 - Whenever you add a non-home fragment just show the up button, right after commiting the fragment transaction. Like this:
// ... add a fragment
// Commit the transaction
transaction.commit();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
2 - Then when UP button is pressed, you hide it.
#Override
public boolean onSupportNavigateUp() {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
return true;
}
That's it.
This worked for me. Override onSupportNavigateUp and onBackPressed, for example (code in Kotlin);
override fun onBackPressed() {
val count = supportFragmentManager.backStackEntryCount
if (count == 0) {
super.onBackPressed()
} else {
supportFragmentManager.popBackStack()
}
}
override fun onSupportNavigateUp(): Boolean {
super.onSupportNavigateUp()
onBackPressed()
return true
}
Now in the fragment, if you display the up arrow
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
Clicking on it takes you back the previous activity.
Kotlin:
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
supportFragmentManager.addOnBackStackChangedListener { setupHomeAsUp() }
setupHomeAsUp()
}
private fun setupHomeAsUp() {
val shouldShow = 0 < supportFragmentManager.backStackEntryCount
supportActionBar?.setDisplayHomeAsUpEnabled(shouldShow)
}
override fun onSupportNavigateUp(): Boolean =
supportFragmentManager.popBackStack().run { true }
...
}
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;
}
}
If you want to go back to your previous activity if this activity has an empty stack of fragments:
This could be useful if you have a MainActivity and you are navigating to e.g. a SettingsActivity with nested prefernceScreens. NavigateUp will pop fragments until you can finish the SettingsActivity to go back to parentActivity/root.
/**
* On actionbar up-button popping fragments from stack until it is empty.
* #return true if fragment popped or returned to parent activity successfully.
*/
#Override
public boolean onSupportNavigateUp() {
//Pop back stack if the up button is pressed.
boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
if (canGoBack) {
getSupportFragmentManager().popBackStack();
} else {
finish();
return super.onSupportNavigateUp();
}
return true;
}
Note: setDisplayHomeAsUpEnabled(true); in fragment activities onCreate()