I have a simple activity with a theme set via setTheme(), the theme id is stored in SharedPreferences, I get this data and setTheme() before super.OnCreate() in the main activity. On pressing the menu button I can launch a preferences activity. On updating preferences and pressing the back button to return to the main activity the theme does not update to the new setting. Only closing the app and reopening fixes this.
What's the best way to make the main activity update and reload the theme after the back button is pressed in the preferences activity? I tried putting setTheme() in OnResume but to no avail.
Any help is greatly appreciated,
Thanks Ric
See this page:
According to Dianne Hackborn, Android framework engineer:
You can only set the theme during
creation. To apply a theme, the
entire UI need to be reinflated and
rebuilt from its resources.
If there is no other way, you could do something like this in your activity, after updating the preferences:
Intent i = new Intent(this, YourActivity.class);
startActivity(i);
finish();
It's not too elegant, and I do hope there is a simpler way.
and setTheme() before super.OnCreate() in the main activity
You shouldn't ever put any code before super.onCreate() or super.onResume(), all your code should be placed after these calls. Maybe this is the problem.
register onsharedpreferencechange listener in your activity
Here is the way I did it:
I have an app that has multiple themes to choose from on a page. Normally, when a theme is selected and then the back button is pressed, the theme reverts back to the previous one. This is an issue.
I used the #Override trick for onBackPressed() to do the trick. Following is the code (I also modified the code for the first activity in the stack [my Navigation Drawer]).
#Override
public void onBackPressed()
{
Intent intent1 = new Intent(this, NavigationActivity.class);
startActivity(intent1);
super.onBackPressed(); // optional depending on your needs
}
That code in the Theme Switch activity made it so the normal back button code wasn't called to destroy the current activity and revert to the old theme. It just passes to the previous activity that the user was on, which is my NavigationActivity. Customize this how you will.
Another thing to note is that the back button can still plague you even after this, because once it passes me to NavigationActivity, if I press the back button again the theme is reverted. I called the same method as before, but I took out the super.onBackPressed(); statement which is what caused the reverting. This is what it looks like:
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
}
}
I hope this does the trick for you!
If you use Navigation component please try as below mentioned. It is not required to call intent or finishActivity.
override fun onBackPressed() {
when (navController.currentDestination?.id){
R.id.settingsFragment -> super.onSupportNavigateUp()
else -> super.onBackPressed()
}
}
super.onSupportNavigateUp() will be solved your back navigation problem.
Related
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.
I am developing application in which I have three fragment A,B,C, when I press on some button in A it navigate to b Thats fine ,and when I press device back button application closes instead of going to A,How can I prevent this problem .
// Your Main Activity
// Override OnBackPressed Event Which as below
#Override
public void onBackPressed() {
if (getSupportFragmentManager()
.getBackStackEntryCount() > 0) {
super.onBackPressed();
} else {
UIUtils.showAlertDialog(this, getString(R.string.app_name), "Are you sure want to Exit App?", false);
}
}
Just override the activity's onBackPressed() function according to your need.
You should override onBackPressed() method. There you can choose what action to do when this happens. If you still want to finish the activity in some cases, you can call finish() method.
Please add
addToBackStack(null);
to your FragmentTransaction object if you not added when replacing or
adding fragments.
it will autamatically maintain backstacks on Backpress.
Hope it will help you !
I'm using this template https://github.com/kanytu/android-material-drawer-template just to try out material design so I've implemented a few fragments some have webviews some have not.
My problem is when switching between the fragments I can see them being successfully added to the backstack
getFragmentManager().beginTransaction().replace(R.id.container, new FAQ()).addToBackStack("FAQ").commit();
But when I press the back button it just closes the app.
When I change it to use Activity instead of ActionBarActivity the navigation works fine but I lose some other functionality.
There is an override on the back button
#Override
public void onBackPressed() {
if (mNavigationDrawerFragment.isDrawerOpen())
mNavigationDrawerFragment.closeDrawer();
else
super.onBackPressed();
}
but even if that's removed it still happens. I think the problem lies somewhere in the super.onBackPressed
Is there any reason ActionBarActivity would break the back button?
I recently read a post about this, sorry I can't find it anymore... But basically, it explained that the primary function of the back button is to finish the current Activity.
In fact, according to the onBackPressed() official documentation:
Called when the activity has detected the user's press of the back key. The default implementation simply finishes the current activity, but you can override this to do whatever you want.
And it would appear that even though the back button used to pop the backstack before 5.0, Google would have changed this behaviour with the new ActionBarActivity.
For my part, I used some workarround that works for me but that might not work for everybody, depending on your navigation implementation.
But in case it could be helpful to somebody, here it is :
#Override
public void onBackPressed()
{
if (mDrawerLayout.isDrawerOpen()) {
mDrawerLayout.closeDrawer();
} else if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
This way, ActionBarActivity.onBackPressed() is only called when the backstack is empty, in which case it destroys the ActionBarActivity.
You should check "getFragmentManager" & "getSupportFragmentManager" is matched your activity & actionbaractivity or not.
Because, in Activity:
public void onBackPressed() {
if (!mFragments.popBackStackImmediate()) {
finish();
}
}
in FragmentActivity:
public void onBackPressed() {
if (!mFragments.popBackStackImmediate()) {
finish();
}
}
We can see the same code which already handled pop fragments backstatck.
In my situation, I used actionbaractivity(extends FragmentAcvitiy), but I also used "getFragmentManager" , so I got the same error as you. After I have replaced "getFragmentManager" to "getSupportFragmentManager", that's ok! You also can replace "actionbaractiviy" to "Activity" to fix this problem.
Must ensure "getFragmentManager" match "Activity", "getSupportFragmentManager" match "FragmentActivity(ActionbarActivity)".
If you want add actionbar On API level 11 or higher, You can see below:
https://developer.android.com/guide/topics/ui/actionbar.html#Adding
On API level 11 or higher
The action bar is included in all activities that use the Theme.Holo theme (or one of its descendants), which is the default theme when either the targetSdkVersion or minSdkVersion attribute is set to "11" or higher. If you don't want the action bar for an activity, set the activity theme to Theme.Holo.NoActionBar.
I have searched but cant find a solution to my problem.
What I need to do is set a random theme and then have all other views adopt this theme. The randomising of the theme isn't the problem, I know it's woking. Whats the issue is refreshing the views already in the stack.
I call
int theme = Constants.THEMES[randomInt];
setTheme(theme);
in an activity somewhere in the stack and then call invalidate() on that activity. Nothing happens in that activity but when I go to other activities the new theme applies.
Also when I go 'back' to my mainActivity I can't figure out how to get the View to redraw.
I'm calling
#Override
protected void onResume() {
super.onResume();
if(refreshNeeded){
getWindow().getDecorView().findViewById(
android.R.id.content).getRootView().invalidate();
}
}
but nothing again. I cant figure out how to get it to redraw with the new theme.
Am I missing something obvious?
Call this at Button click
getApplication().setTheme(R.style.Theme_Black);
setTheme(R.style.Theme_Black);
Intent n = new Intent(activityA.this , activityB.class);
startactivity(n);
It will work. But you must have all widget styles in your themes.xml in values folder
Call setTheme(R.style.Theme) before super.onCreate and setContentView.
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.