I have an app with multiple activities, each of which may employ multiple fragments.
One activity (called IBAsTabsActivity) extends SherlockFragmentActivity and I am using some example code where a different fragment gets loaded up depending on which tab is selected. One of the possible fragments is called "SearchableListFragment". This is all working fine. I have a "home" activity with two buttons, one to take me to IBAsTabsActivity and another to take me to AnAlternativeActivity. If I press the button to take me to IBAsTabsActivity and look at several tabs - then if I press the back button I jump straight to "home".
Now I want to get AnAlternativeActivity up and running. It has no tabs and so does not employ any Sherlock code. Instead I have this:
public class AnAlternativeActivity extends FragmentActivity implements OnClickListener
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.blank_fragment_holder);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
SearchableListFragment fb = new SearchableListFragment();
ft.replace(R.id.fragment_holding_layout, fb);
ft.addToBackStack("replacingFragmentA");
ft.commit();
}
This all works fine in as much as if I click on the button on the home screen to call AnAlternativeActivity, then I see the SearchableListFragment() (inside fragment_holding_layout) and its contents correctly. But now if I click the back button the fragment within fragment_holding_layout disappears, but leaves AnAlternativeActivity still running. I have to press the back button again in order to exit AnAlternativeActivity and get back to my home activity.
I could probably cook up some ugly code to resolve this issue - but I suspect that there is a standard way to cope with this scenario. Any ideas?
You should be able to use the notion of parent-activity to resolve this easily. All you need to do is to tell Android that HomeActivity is the parent of your AnAlternativeActivity and the navigation will be handled by you. In your AndroidManifest.xml file add android:parentActivityName attribute for AnAlternativeActivity like so:
<activity
android:name="com.mick.AnAlternativeActivity"
android:label="#string/activity_title"
android:parentActivityName="com.mick.HomeActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.mick.HomeActivity" />
</activity>
You need the regular attribute for new devices and meta-data tag for older devices. More information can be found here: http://developer.android.com/training/basics/firstapp/starting-activity.html (look under Add it to the manifest subheading).
Within AnAlternativeActivity's onPause you could call finish() to end it.
public void onPause() {
super.onPause();
finish();
}
Your call to ft.addToBackStack("replacingFragmentA"); seems to be the cause for the need for tapping 'Back' twice. Adding the transaction to the back stack will have that effect: adding a layer in the history stack.
So when you press 'Back' the first time that transaction will be 'undone' so to speak, and will replace your SearchableListFragment with the R.id.fragment_holding_layout. The second time you press 'Back' will move from the AnAlternativeActivity back to your 'home' activity.
Related
I'm so very confused and have been reading on this topic for a while.
I have a MainActivity that has multiple possible contents (switched between via navigation drawer), which I've set via multiple fragments (lets call them Fragment1, Fragment2 and Fragment3).
That works fine.
One of my fragments, Fragment3, is a View that can segue to a new activity, View2.
View2 has a back button. When I press on the View2 back button I want to see Fragment3 on my MainActivity, not Fragment1, which is what I currently get. This is because OnCreate, by default, loads Fragment1.
What is the best way to do this?
Thanks so much in advance! (vent: iOS makes this so much easier).
The official documentation for Fragments states that you should make sure to call addToBackStack() before commiting your fragment transaction on your first activity holding the 3 fragments.
In order to go back to the last used fragment from the second activity , you should override the onBackPressed() method in this activity :
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
The documentation is very complete on the subject : Read it here
Update:
I just added this to the back button code:
finish();
return true;
I had to do it within onOptionsItemSelected(MenuItem item)... for some reason onBackPressed is not fired.
I'm using a navigation drawer with fragments similar to the gmail app. But I am encountering two issues:
1: Suppose I select item x from the nav drawer. The corresponding fragment(fragment x) is displayed with no problem. However, when I change the orientation, the activity is recreated and fragment 1 (default fragment) is displayed , even though the navigation drawer shows item x as checked.
2: Again Suppose I select item x. Again the corresponding fragment(fragment x) is displayed with no problem. Now I close the app by pressing the home button and open up 10 other apps. After some time when I re-open my app, the activity is recreated and fragment 1 (default fragment) is displayed, even though the navigation drawer shows item x as checked.
Both the problems are similar and probably require the the same solution. How do I solve this?
In the Android Manifest, add
android:configChanges="keyboardHidden|orientation|screenSize"
This will stop the Activity from refreshing on such changes, including the case you mentioned, which is an orientation change.
For the Activity to resume after restoring from a minimised position, override the onPause and onResume methods in the Activity.
#Override public void onPause() {
super.onPause(); // Always call the superclass method first }
#Override public void onResume() {
super.onResume();
//Do the things you want to do }
For reference: https://developer.android.com/training/basics/activity-lifecycle/pausing.html
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.
There are two activities.
Activity A has a button that can switch to Activity B.
Activity B also has a button that can switch to Activity A.
here is my code,
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
Log.e("current", context.getClass().toString());
Log.e("changeto", tab.getTag().toString());
if(context.getClass()==tab.getTag())
return;
Intent intent = new Intent(new Intent(context,(Class<?>) tab.getTag()));
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
context.startActivity(intent);
}
I want to remove the animation when i switch the activities, but it doesn't work.
However if I remove
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
it works perfectly. Why?
Sorry for my bad English.
In the Activity that you're switching to, try using overridePendingTransition(0,0); either in onResume or in onCreate.
If you're calling startActivity in a tab switch, you're doing it wrong. Tabs are for switching views within the current activity, and switching tabs should never create navigation history. Consider switching a fragment or replacing your view hierarchy with the newly selected tab's content instead.
The more you pursue a path of switching activities for tab selection, the more you will find yourself playing whack-a-mole with subtle user experience bugs that make your app simply feel "wrong."
With your proposed implementation above, the Back button will return to the previously selected tab, breaking the, "never creates navigation history" rule. You may think that finish()ing the current Activity as you start the next can solve this, but you'll still have a host of other issues. Users expect subtle elements of state such as scroll position to persist across tabs. As of Android 4.0 there is an expectation that users should be able to swipe horizontally between tabs (http://developer.android.com/design/building-blocks/tabs.html) which you will not be able to accomplish if you are using separate activities for each tab's content.
This is only a small sample, the list just goes on. Tabs should not be used to switch between different Activities.
I have implemented a ActivityGroup in my TabView.
I can successfully add a view to the ActivityGroup from an activity in a tab.
How do I pop this new view off the stack to go back to the original view??
In the ActivityGroup I handle the BackButton with a Finsih() but the entire app goes away and the home screen is displayed. Note that the app is not killed, it's still running.
(Written in .NET)
public override void OnBackPressed ()
{
int length = mIdList.Count;
if (length > 1)
{
Activity current = LocalActivityManager.GetActivity (mIdList [length - 1]);
current.Finish();
}
base.OnBackPressed ();
}
(I'm coming from an iOS background)
First, I agree with aneal that you should try Fragments rather than activity groups. If you do, the following text from the Android on-line training class appears to address the issue:
Keep in mind that when you perform fragment transactions, such as replace or remove one, it's often appropriate to allow the user to navigate backward and "undo" the change. To allow the user to navigate backward through the fragment transactions, you must call addToBackStack() before you commit the FragmentTransaction.
When you add the view, call addToBackStack() before committing your "add fragment" transaction.