Closing android application from fragment - android

I have only one activity in my application with multiple fragments. Whenever a user opens the app, it starts with startupFragment. Whenever user navigate through the fragments and presses back, it takes him back to startupFragment. But when in the startupFragment, I want user, when clicked back, to be able to close the application. Now, here is the code, when the application is started for creating the fragment.:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Set starting fragment
StartupFragment startupFragment = new StartupFragment();
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().add(R.id.content_main, startupFragment, "startupFragmentTag").commit();
}
As you can see, I have added the tag "startupFragmentTag" to be able to identify it. This is my code onBackPressed:
#Override
public void onBackPressed()
{
Fragment startup = getFragmentManager().findFragmentByTag("startupFragmentTag");
if(startup == null) {
android.support.v4.app.FragmentManager manager = getSupportFragmentManager();
StartupFragment startupFragment = new StartupFragment();
manager.beginTransaction().replace(R.id.content_main, startupFragment, "startupFragmentTag").commit();
} else {
finish();
}
}
So basically what I tried here, is when user is in another fragment, when the user presses back, it will take him/her back to startupFragment. But when in that fragment, when pressing back again, I want the user to be able to exit the application, which won't happen given the code now.
I find the startupFragment by its tag and check if it exists. If not, it will take back the user to it. But if it exists, user should be able to quit, that why finish() is called.
Does the previous fragments not get destroyed? But that shouldn't be the case, because even if I open the app and instantly press back, it won't exit the app.
What am I doing here wrong?

It looks like all you need to do is add each Fragment to the back stack on each FragmentTransaction, and then pop each Fragment from the back stack in onBackPressed().
First, modify onCreate() so that on each transaction, it calls addToBackStack():
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Set starting fragment
StartupFragment startupFragment = new StartupFragment();
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.content_main, startupFragment, "startupFragmentTag")
.addToBackStack(null)
.commit();
}
Then, in onBackPressed(), just pop from the back stack if you're not on the starting Fragment. If you're on the starting Fragment, and back is pressed, just call super.onBackPressed(), which will exit the app:
#Override
public void onBackPressed() {
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() > 1) {
//Go back to previous Fragment
fragmentManager.popBackStackImmediate();
} else {
//Nothing in the back stack, so exit
super.onBackPressed();
}
}

You're working with fragments, so to finish your app you need to finish the parent activity.
Try this to finish the activity from startupfragment:
getActivity().finish();

Probably because you've used beginTransaction().add() to add the Fragments including startupFragment and other Fragments on top of it. Then you will always be able to find it using getFragmentManager().findFragmentByTag(), because all Fragments added will be in the Fragment stack (while the find method is actually to find the Fragment with the tag in the stack).
Tips based on what you want to impl:
beginTransaction().replace() will replace instead of adding a Fragment, this way you will only be able to "find" one existing Fragment if you always replace it. i.e. always one Fragment in the stack.
You may want to use getFragmentManager().findFragmentById() to get current showing Fragment (on top of the Fragment stack), instead of findFragmentByTag, if there're several Fragments in the stack which are added not replaced as mentioned above.
When using beginTransaction().add(), you may want to use fragmentManager.addOnBackStackChangedListener() to monitor the Fragment stack changes. Then you probably don't have to handle onBackPressed(). Then in the in the listener you only need to retrieve current Fragment on top stack and see what it is and add your logic there.

Related

How to determine if fragment at top of other fragment is removed

I have fragment A and adding fragment B in same container (not replacing). I am adding this transaction on backstack also. Now, when device back is pressed, fragment B will be removed and fragment A will become visible. I want to do something when fragment A becomes visible. I searched lot but could not find anything helpful.
Note - I don't want to add backstackchangelistner and call onResume on that fragment.
You can override onHiddenChanged(boolean hidden) in a Fragment.
This will be called when its shown/hidden.
I use the same approach in my app, where i add a fragment and hide the old one, and then use the onHiddenChanged callback when the user presses Back, and the old fragment is shown again.
As you have 2 entries in Back stack, you can check back stack count on
Back Pressed method.
FragmentManager mFragmentManager = getSupportFragmentManager();
int count= mFragmentManager.getBackStackEntryCount();
if(count==1){
// do your work
}
you can try
#Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
}
or
`fragment.isVisible();`

How to restore fragment back stack with in an activity (After application is killed in background)

In an Android app-
Say I am in an Activity - MyActivity which holds one Fragment at a time.
First I loaded Fragment A to it (With no tags I added it to back stack of the FragmentManager)
Then at some point I loaded Fragment B (Again with no tags I added it to back stack of the FragmentManager)
Then at some point i loaded Fragment C (Again with no tags I added it to back stack of the FragmentManager)
I am using popBackStack() to enable back button behavior so whenever I press back from Fragment C the flow is like:
Fragment C -> Fragment B -> Fragment A -> Close MyActivity..
Everything is perfect :-)
But if I am in Fragment C and the app gets killed in background (I used "do not keep activity flag" from Settings)
and come back online Fragment C is loaded in MyActivity
but the FragmentManager's back stack contains only Fragment C..
The Back button is messing it up
Fragment C -> Close MyActivity..
Why is it so?
How to properly restore FragmentManager's back stack within an Activity?
Try using alwaysRetainTaskState on your root activity. Android automatically clears the Activity backstack because it assumes that it has been a long time since you used the app and that the user wants to start again from the start.
<activity android:alwaysRetainTaskState="true"/>
This setting will prevent that behaviour and it may follow that the behaviour is inherited by the Fragment Manager.
While developing your app, I recommend you to test restore/saved states of the activities, fragments with ADB:
Open app
Navigate between activities
Press home
ADB -> Kill (stop) app
Press the application stack (menu button from the device) and resume the application
This way you can debug the saved/restore states.
If you don't have a complex application, I suggest you to handle the saved/restore state in the activity:
private Fragment1 mFragment;
#Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
// ...
if (savedState == null) {
mFragment = new Fragment1();
getFragmentManger().beginTransacation().add(mFragment, TAG).addToBackStack(TAG).commit();
}
else {
mFragment = getFragmentMananager().findFragmentByTag(TAG);
}
}
If you have several Fragments or a ViewPager or nested fragments, then things can get really complicated. I suggest you to restart the whole application:
#Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
if (savedState != null) {
Intent intent = new Intent(ActivityMain.this, ActivityMain.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
return;
}
}
If you want to handle each saved/restore state, please read this post: http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
try this method
public void setRetainInstance (boolean retain)
Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). This can only be used with fragments not in the back stack. If set, the fragment lifecycle will be slightly different when an activity is recreated:
from developer website
http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance%28boolean%29
Fragment C should never be loaded after your application dies. Do you have an Init Fragment in your application ? Ideally when you are implementing a pop of fragments there should be an Init screen. If the application dies or is killed for memory reasons you application should start from Fragment A (Init Fragment). Not from Fragment C.
If your problem demands this solution, then you should have to save each fragment persistently when a new fragment come on top. Which ideally means you are persisting your backstack in a preference or a database to achieve this.

How to deal with Activity and Fragment when returning to app after they're removed from memory?

I'm having an issue of having two instances of the same fragment being attached to the activity. I have ActivityA attaching FragmentA on onCreate. When I leave the app while being on this Activity, browse other apps for a while, and return to the app, I see that the system is trying to re-create the activity. My log shows the code from the Fragment being ran TWICE. My guess is the Fragment is already attached but then the Activity attempts to create a new instance of FragmentA.
What happens to the Activity/Fragment when the system removes them from memory, and what's the best way to handle this? Any links would be helpful.
Will provide code if needed.
The best way to handle this is to check in your onCreate() method if your activity if being recreated from a previous state or not. I'm assuming you add your fragment on the onCreate() method of your activity. You can do something like this:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState == null)
{
// Add the fragment here to your activity
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.content, new YourFragment());
ft.commit();
}
}
By doing this, you are basically saying that if a previous state is not found, you add your fragment. Otherwise you automatically get back the fragment that already exists.

Recreate Fragments on BackKey Pressed

Im trying to Solve a problem where when a user press back key, the fragment should be recreated rather than loading from back stack. I have a single Main activity with a frame layout and i replace the fragments within the single frame dynamically. the code below works when the user go from fragment within fragment. but when the user select from navigation drawer, the replaced fragment is going on to the top of backstack which is causing problems.
Right now the code i wrote in BackKey Pressed Event
public override void OnBackPressed()
{
Android.Support.V4.App.FragmentManager.IBackStackEntry entry =
SupportFragmentManager.GetBackStackEntryAt(SupportFragmentManager.BackStackEntryCount - 1);
string str = entry.Name;
if (SupportFragmentManager.BackStackEntryCount == 0)
{
this.Finish();
}
else
{
Fragment fr = (Fragment)MagicallyCreateInstance(str);
SupportFragmentManager.BeginTransaction().Replace(Resource.Id.content_frame, fr).Commit();
SupportFragmentManager.PopBackStack();
}
base.OnBackPressed();
}
i also have a replace fragment method which i use to replace fragments. but in this process the back key is getting disabled by default somehow (Not sure how) but whenever there is existing fragment in the backstack, the old UI is getting loaded. Can i refresh the layout here?
public void ReplaceFragment(Fragment fragment, FragmentManager fragmentManager)
{
string backStateName = fragment.Class.SimpleName;
bool fragmentPopped = fragmentManager.PopBackStackImmediate(backStateName, 0);
if (!fragmentPopped && fragmentManager.FindFragmentByTag(backStateName) == null)
{
fragmentManager.BeginTransaction()
.Replace(Resource.Id.content_frame, fragment).SetTransitionStyle(FragmentTransaction.TransitFragmentFade)
.AddToBackStack(backStateName)
.Commit();
}
}
Can anyone please help me solve this any one of the above?
I hope I'm understanding your problem correctly. But it sounds like you want to make sure that when the user selects a destination from your main navigation you want to make sure the back stack is cleared or reset.
Ex:
Stack looks like this:
A->B->C
User selects D from main navigation, stack should look like:
D
NOT
A->B->C->D
If this is the case, you should clear the back stack before navigating to any top-level destinations. This can be done like so:
FragmentManager.PopBackStack(null, FragmentManager.PopBackStackInclusive);
The documentation for this method is not great, but it will pop everything off the back stack. A discussion can be found here: https://groups.google.com/d/msg/android-developers/0qXCA9rW7EI/M9riRM0kl9QJ

Why is back button closing activity instead of reversing fragment transaction?

The main activity opens the main_fragment with this transaction:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.fragment_main_container, new MainFragment())
.commit();
}
Then I replace that fragment with another one like this:
// method to handle Conversions button click
public void addConversionsFragment (View v) {
// replace the main fragment with the conversion fragment
UnitConversionFragment newFragment = new UnitConversionFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
transaction.replace(R.id.fragment_main_container, newFragment);
// and add the transaction to the back stack so the user can navigate back
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
Originally, I was using support.v4.app.fragment, and everything was working as expected (back button in the second fragment would pop that one out and return to the MainFragment. But then I decided to implement a PreferenceFragment elsewhere, which the support library didn't seem to like. So I converted the whole project to the regular app.fragment by deleting all the support imports, replacing with the regular imports, then editing all the getSupportFragmentManager() with getFragmentManager(), etc.
Good news is thePreferenceFragment works well, however any time I hit the back button in a fragment, it closes the hosting activity rather than reversing the transaction.
I did many searches and it seems that I am implementing the code correctly, but it is just not responding as I am expecting. Is there more involved in converting away from the support library? Or am I missing something else obvious? I saw a lot of answers out there overriding the onBackPressed(), but I really don't want to do that.
Is there some fundamental difference between the v4 support library and the regular library that requires me to handle the fragment transactions differently?
Preference Fragment has a bit of extra logic to handle hierarchical preferences. You can configure it to launch sub fragment screens, and navigate back, as demonstrated here.
<PreferenceScreen android:title="Sub Preferences"
android:fragment="com.example.SettingsDemo.SubPrefFragment"/>
For normal fragments , fragment back-stack pops first before reaching Activity back stack. This is what is documented.
The code in both android.app.Activity and android.support.v4.app.FragmentActivity is exactly same:
/**
* Take care of popping the fragment back stack or finishing the activity
* as appropriate.
*/
public void onBackPressed() {
if (!mFragments.popBackStackImmediate()) {
finish();
}
}
The only reason it is not happening as expected is that something else consumes "back press", this may happen when there are 3 levels of components:
The Activity is inside an ActivityGroup.
Fragment is inside another Fragment. (for this, there are some bugs with v4 fragments).

Categories

Resources