In my activity, I have:
<activity
android:name=".ChildActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.ParentActivity" />
</activity>
ChildActivity can be accessed via a standard launcher icon on the device.
My problem is when the user accesses ChildActivity via the launcher icon, then presses the Up button in the action bar, the app exits rather than going up to ParentActivity. This is because ParentActivity hasn't been instantiated.
Not sure if it's the best way, but I am trying to solve the problem by overriding onSupportNavigateUp(). However, I don't know how I can detect if the parent activity has been instantiated:
#Override
public boolean onSupportNavigateUp() {
boolean hasParentActivityBeenInstantiated = ???;
if (hasParentActivityBeenInstantiated) {
return super.onSupportNavigateUp();
}
else {
Intent upIntent = NavUtils.getParentActivityIntent(this);
startActivity(upIntent);
finish();
return true; // Up navigation completed successfully and this Activity was finished.
}
}
So how can I determine if the parent activity has been instantiated? More importantly, in this case, is overriding onSupportNavigateUp() the right way to navigate to the parent activity?
I would suggest not to guess if parent activity is instantiated or not, but use android:launchMode="singleTop" for parent activity. I think it is also fine to use onSupportNavigateUp unless you refactor your app to be single-activity with single navigation graph. Imo navigation framework leaves no choices for multi activity setup.
If for some fancy reason you need to explicitly know if it's up, you can use various techniques including static field initialization, flagging some field inside the application instances etc.
I did similar implementation (kotlin) and I was using onBackPressed for the purpose but it probably doesn't suite your situation (I was handling all the descendant fragments sys back press) however I would suggest using TaskStackBuilder if your depth of child/parent is bigger then one:
override fun onBackPressed() {
val upIntent: Intent? = NavUtils.getParentActivityIntent(this)
checkNotNull(upIntent) { "No parent activity intent" }
TaskStackBuilder.create(this)
.addNextIntentWithParentStack(upIntent)
.startActivities()
finish()
}
Related
The animation when I am going from a fragment to an activity is working fine but when I click back it returns without the custom animation I insert. The same if I make the navigation from a fragment to another with the same animations works fine. Here is the action code I am using:
<action
android:id="#+id/toTicker"
app:destination="#id/tickerActivity"
app:enterAnim="#anim/slide_bottom_up"
app:exitAnim="#anim/slide_up_bottom"
app:popEnterAnim="#anim/slide_bottom_up"
app:popExitAnim="#anim/slide_up_bottom"/>
As per this issue, you need to call the static ActivityNavigator.applyPopAnimationsToPendingTransition() method in your other activity to get the pop animations to apply - it should be called directly following when you call finish() or as part of callbacks to onBackPressed() (which internally will call finish()):
override fun onBackPressed() {
super.onBackPressed()
ActivityNavigator.applyPopAnimationsToPendingTransition(this)
}
Updating the documentation to specifically call this out is being tracked in this documentation issue.
The previously selected answer doesn't work anymore.
applyPopAnimationsToPendingTransition must be called by overriding Activity.finish().
override fun finish() {
super.finish()
ActivityNavigator.applyPopAnimationsToPendingTransition(this)
}
I have an activity wich has a Toolbar which displays a back button.
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar_about"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:title="#string/app_name"
/>
The back button is enabled like this:
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_about);
setSupportActionBar(toolbar);
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
I call this activity from my main activity like this:
Intent intent = new Intent(this, AboutActivity.class);
startActivity(intent);
The Activity's parent is defined in the manifest
<activity android:name=".AboutActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".EntryActivity" />
</activity>
So far everything works fine, except that the transition animation is wrong when using the back button in the Toolbar.
When I open the activity, it slides in from the right.
When I press the phone's physical back button it slides out to the right again. This is correct.
However when using the Toolbar back button it slides out to the left. This looks wrong. How can I change this, so it duplicates the behaviour of the physical back button?
When you press the Actionbar Up button, AppCompatActivity detects this button press in its onMenuItemSelected() call, and invokes onSupportNavigateUp(). This method determines the "parent activity" Intent and uses that to navigate up. Because it's using an Intent to (re-)open the previous activity, it uses the same animation it would for opening a "new" screen.
Assuming you don't care about the particular niceties of the "Up Navigation" pattern (which it sounds like you do not, as comments have led me to believe you don't have lateral navigation and you can't get to your second activity from anywhere other than your first activity), you can side-step all of this built-in "up" behavior by overriding the onSupportNavigateUp() method.
#Override
public boolean onSupportNavigateUp() {
finish();
return true;
}
This will mean that pressing the Actionbar Up button always simply finish()es your activity, so (like I said before) you lose out on all the smart built-in "up" behavior... but you didn't want that anyway.
You could also handle the Actionbar Up button in onOptionsItemSelected(), but I prefer the other way since I think it's a little more obvious that you're hijacking the system's up behavior.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
With either of these in place, you can remove the "parent" definitions from your manifest, since now they're not used.
Try this:
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
This is because the default launchMode of activities is standard.
The documentation of standard states the following:
The system always creates a new instance of the activity in the target task and routes the intent to it.
You can solve this by using android:launchMode="singleTop" on the parent/starting activity.
If an instance of the activity already exists at the top of the target task, the system routes the intent to that instance through a call to its onNewIntent() method, rather than creating a new instance of the activity.
For more information see Tasks and the back stack and android:launchMode.
In my app, each activity has the same menu in the action bar, which provides access to the parameters activity.
When I am in the parameters activity, I would like to get to my parent activity by clicking on the left-orriented arrow like in my other activities.
The unique parent of others activities is defined in manifest.xml.
But for the parameters it's impossible as it has multiple parents:
mother > child > parameters is possible
mother > parameters is also possible!
We can find this comportment in the gmail app:
main > parameters, in which you can get back to main
main > email > parameters, in which you can get back to the specific email you were looking at.
So my question is how to get this gmail comportment? Can we change dynamically the parent activity of parameters?
There are two ways in which you can accomplish this:
Use a TaskStackBuilder and specify the parent chain in the manifest using android:parentActivityName and the associated support-v4 meta-data tag as described in the corresponding Providing Up Navigation training. This is the recommended method as it obeys the hierarchical task stack.
Provide some extra into the launch intent and navigate up depending on that custom flag. Note that launching activities through this mechanism will not show the back animation of the current activity closing.
Comments mess up well indented answer so I anwser my question :
I finally ended up with a simple custom code,
because I had already visited a few times your link (http://developer.android.com/training/implementing-navigation/ancestral.html#NavigateUp) and it didnt helped me much.
Here's what I did :
(in the else of http://developer.android.com/training/implementing-navigation/ancestral.html#BuildBackStack)
// particular case
// if this activity is the Parameter/Help activity, just finish it
if(this.getClass().equals(ActivityParametre.class) || this.getClass().equals(ActivityAide.class)){
finish();
return true;
}
// general case
// otherwise, use pre defined code
else {
NavUtils.navigateUpTo(this, upIntent);
}
I have an activity that starts another activity.
Is it mandatory that I specify the parent activity in the Android Manifest?
Im asking this because there might be other activities that will start this one as well, so should I specify all of them?
android:parentActivityName="com.example.myfirstapp.MainActivity"
As per docs -> section android:parentActivityName:
The system reads this attribute to determine which activity should be started when the user presses the Up button in the action bar. The system can also use this information to synthesize a back stack of activities with TaskStackBuilder.
So you only need to specify that if you're going to use up-navigation (as opposed to navigation by back button) or TaskStackBuilder. In other cases, you don't need it.
For more information, see the up-navigation docs. (Archived from the original, which now redirects to the Jetpack Navigation docs on designing navigation graphs)
While it should be defined if upward navigation or backstack synthesis is desired, note that the attribute android:parentActivityName was introduced in API Level 16.
For prior releases, parent activity information is accessed from attributes defined inside of a <meta-data> tag that is declared inside of the child <activity> tag.
Example:
<activity
android:name=".DetailActivity"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
Inside of the <meta-data> tag, set the android:name attribute to android.support.PARENT_ACTIVITY, and the android:value attribute to the parent activity class name (i.e. the same class name as that assigned to android:parentActivityName).
Unless API level is known, both the <meta-data> and inline specifications are recommended.
For more on specifying the parent activity, see: https://developer.android.com/training/implementing-navigation/ancestral.html#SpecifyParent
In addition, consider defining the android:launchMode attribute inside of your main <activity> tag to set the desired behavior of upward navigation: https://developer.android.com/guide/topics/manifest/activity-element.html#lmode
You don't necessarily need to define the parentActivity in the AndroidManifest.xml. You can use the below code for the back navigation enabled:
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
And implement this:
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
onBackPressed();
}
return super.onOptionsItemSelected(item);
}
But if you define the parentActivity in the manifest, then the system reads this attribute to determine which activity should be started when the user presses the Up button in the action bar. i.e, it will create a new instance of the parentAcivity, means it will call the onCreate() of the parent activity.
You have to specify every Activity in the manifest which you call via Intent or Launchers, that the system can find it. So, mark one Activity as the Launcher that your App can get started and register every other Activity, which you call in your App.
If you have a BaseActivity like this:
public class BaseActivity extends Activity{}
public class MyActivity extends BaseActivity{}
than you only have to register MyActivity, because BaseActivity is not called by the system but you.
No its not necessary to specify parent activity in manifest like this
android:parentActivityName="com.example.myfirstapp.MainActivity"
for navigationUp you can also use setDisplayHomeAsUpEnabled(true); and onSupportNavigateUp() method Take a Look at this
This is my savedInstaceState code:
#Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
savedInstanceState.putStringArrayList("todo_arraylist", Altodo);
Log.v("bundle", "Saved");
super.onSaveInstanceState(savedInstanceState);
}
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
Altodo = savedInstanceState.getStringArrayList("todo_arraylist");
Log.v("bundle", "Restored");
}
else
{
Log.v("bundle", "null");
}
setContentView(R.layout.main);
}
The logs always show the "bundle save" tag.
But in onCreate method, SavedInstanceState is always null.
I observed the exact same symptoms (reported as issue 133394) in a project with two Activities A and B that extend ActionBarActivity. Activity A is the main activity, and I always receive null for savedInstanceState in onCreate of its list fragment when returning from a detail view activity B. After many hours, this problem exposed itself to me as a navigation issue in disguise.
The following may be relevant to my setup and come from other answers on this page:
Given this answer, I made sure that fragment and activity each have unique IDs set.
There is no override of onSaveInstanceState without super call.
Activity A is specified as acitivy B's parent in AndroidManifest.xml, using both the android:parentActivityName attribute and the corresponding meta-data tag for earlier versions of Android (see "Providing Up Navigation").
Already without any corresponding creation code such as getActionBar() .setHomeButtonEnabled(true), activity B has a functioning back button (<) in its action bar. When this button is tapped, activity A reappears but with (a) all previous instance state lost, (b) onCreate always called, and (c) savedInstanceState always null.
Interestingly, when I tap the back button provided at the bottom edge of the emulator display (an open triangle that points to the left), activity A reappears just as it was left (i.e. its instance state fully retained) without invoking onCreate. So maybe something is wrong with navigation?
After more reading, I implemented my own navigation instructions to run in response to a tap on the back-button in activity B:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home)
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
Nothing related to restoring instance state of activity A changed. NavUtils also provide a method getParentActivityIntent(Activity) and navigateUpTo(Activity, Intent) that allow us to modify the navigation intent to explicitly instruct that activity A is not started fresh (and thus without saved instance state provided) by setting the FLAG_ACTIVITY_CLEAR_TOP flag:
If set, and the activity being launched is already running in the
current task, then instead of launching a new instance of that
activity, all of the other activities on top of it will be closed and
this Intent will be delivered to the (now on top) old activity as a
new Intent.
In my hands, this solves problem of lost instance state and could look like:
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId()== android.R.id.home) {
Intent intent = NavUtils.getParentActivityIntent(this);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
NavUtils.navigateUpTo(this, intent);
return true;
}
return super.onOptionsItemSelected(item);
}
Note that this may not be the complete solution in other cases where a user can switch directly to activity B from within a different task (see here). Also, a possibly identical solution in behavior that does not make use of NavUtils is to simply call finish():
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId()== android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
Both solutions work in my hands. I am only speculating that the original issue is a slightly incorrect default implementation of the back-button, and it may be related to that implementation invoking some kind of navigateUp that misses FLAG_ACTIVITY_CLEAR_TOP.
Did you check if you have an Id set for that view ( if a view it is/has...). onSaveInstanceState() is not called otherwise.
Check this link.
The state saved in this manner is not persisted. If the whole application is killed as you are doing during debugging, the bundle will always be null in onCreate.
This IMO is yet another example of awful Android documentation. It's also why most apps in the marketplace don't implement saving state properly (at all).
in Manifest add this line for activities
android:launchMode="singleTop"
for example:
<activity
android:name=".ActivityUniversity"
android:label="#string/university"
android:launchMode="singleTop"
android:parentActivityName="com.alkhorazmiy.dtm.ActivityChart">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.alkhorazmiy.dtm.ActivityChart" />
</activity>
How do you test it?
Imo the best way to test it is using the "Don't keep activities"-flag in Settings > Developer Options. If you don't have Developer Options in Settings, see Enabling On-device Developer Options.
Open your activity
Long-press home
Go to another application
Long-press home
Go back to your application
Shouldn't super.onSaveInstanceState(savedInstanceState); be the first line in your override?
Edit: War_Hero points out in the comments that the documentation on that topic indicates that no, it shouldn't be the first line.
Check your activity in AndroidManifest.xml and remove android:noHistory property if is true.
<activity
// ....
android:noHistory="false" />
To debug, consider implementing onRestoreInstanceState and placing a call to Log.d in this method. Then, in the emulator, hit ctrl-F11 or whatever to rotate the phone. Your call to Log.d should be hit.
Implement a method of onRestoreInstanceState
and put below code there
Altodo = savedInstanceState.getStringArrayList("todo_arraylist");
I found that when I override onSaveInstanceState() and actually save some data in the Bundle, instance state is restored. Otherwise it's not.
Ive managed same way arround. Instead of handling savedInstanceState Bundle on the onCreateView method, ive handled it on onCreate method and setting the passed value to a globar variable then acessing this variable on the onCreateView method.
Hope it helps.
https://developer.android.com/guide/topics/manifest/activity-element#lmode
From this you can see 'Similarly, if you navigate up to an activity on the current stack, the behavior is determined by the parent activity's launch mode.' Maybe you are in the 'standard' mode.
I was able to solve it with:
#Override public boolean onSupportNavigateUp()
{
onBackPressed();
return true;
}
still had parent set in the manifest. So when you press the up navigation button, now it acts like the back button.