Android change fragment of navigation view - android

I have an Android app with 1 base activity and a few fragments. They can be changed using the NavigationView inside the DrawerLayout. Users can change the language of the application in one of the fragments and when I relaunch the application, I want users to go back to that specific fragment.
=====DrawerLayout=====
1. Fragment Home -> This is the starting fragment
2. Fragment One
3. Fragment Settings -> Users change the language here.
When users change the language, a method in the base activity is called and I change the Locale, and call recreate(). This will refresh the app with the Fragment Home being displayed in the new language. I want to programatically change to Fragment Settings.
navigationView.<METHOD?>

In order to solve your problem, you can save the special state and recreate the activity. When the activity is recreated, it will know that it needs to move to the settings page using the saved state.
Try this in your activity:
static final String SHOW_SETTINGS = "SHOW_SETTINGS";
private boolean showSettings = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
createLayoutAndDoOtherOnCreateThings();
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
if (savedInstanceState.getBoolean(SHOW_SETTINGS, false)) {
showSettingsFragment();
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean(SHOW_SETTINGS, showSettings);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
private void callRecreateWithSettingsWhenRecreating() {
showSettings = true;
recreate();
}

Related

How can I add something to the savedinstancestate of an already destroyed fragment?

Simple question. If a fragment is still existing then I can set the fields of that fragment so when it gets destroyed they will be put into the savedInstanceState. But if the fragment was already destroyed, is there a way to update the savedInstanceState for that fragment so it can get picked up when the fragment is re-created?
For example, I'm launching some other activity, while the user is in that other activity the fragment gets destroyed but I want to signal the fragment to display some notification to the user when the fragment is next created again (after the user comes back from the other activity).
One solution, albeit a bit ugly, would be to just store them with IDs in some static map like:
private static int fragmentInstance = 0;
private static Map<Integer,Map<String,String>> sisExtras = new HashMap<>();
#Override
public void onSaveInstanceState(Bundle bundle) {
// ...
sisExtras.put(++fragmentInstance, new HashMap<>());
}
public static void addToSavedInstanceState(String key, String value) {
if (sisExtras.containsKey(fragmentInstance)) sisExtras.get(fragmentInstance).put(key,value);
}
#Override
public void onCreate(Bundle savedInstanceState) {
if (sisExtras.containsKey(fragmentInstance)) {
for (Map.Entry<String,String> sisExtra : sisExtras.get(fragmentInstance).entrySet()) {
savedInstanceState.putString(sisExtra.getKey(), sisExtra.getValue());
}
sisExtras.remove(fragmentInstance);
}
// continue to handle the savedInstanceState as before...
}
I am hoping that there is a better way than this to put things into the savedInstanceState of a destroyed fragment.

Android - Saved Activity/Fragment State Never Clears

I have an Android app, composed of Fragments, that I have saving state correctly. The problem is it works a bit too well ;-). I'll enter some input into a couple of EditText elements that I then save via SharedPrefs in the "onSaveInstanceState()" method, and then hit the "Task Manager" or "Switch App" button on the phone (there's two overlapping rectangles as the icon) and swipe left to close my application. If I then go to the App Drawer and re-run the application, that saved input will still be there. I am clearing the saved instance state in the "onDestroy()" method too, but apparently that is not being called when "closing" the app from that Task Manager (confirmed via logging).
Any suggestions here? Other apps I have do not exhibit this behavior. I'd like for the saved input to be cleared when a user closes the app via Task Manager as well as probably after a set amount of time. Any ideas of what the standard practice for state handling is here?
I tested some apps I have and noticed the default Contacts app actually saves a new contact if you start a new one and switch to another app before explicitly saving. I guess I could do this but I'd rather not.
Below is some relevant code for a particular Fragment; thank you very much in advance for any assistance.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Log.v(Tag, "onSaveInstanceState()");
saveInstanceState();
}
#Override
public void onResume() {
super.onResume();
Log.v(Tag, "onResume()");
restoreInstanceState();
}
#Override
public void onDestroy() {
super.onDestroy();
Log.v(Tag, "onDestroy()");
clearInstanceState();
}
private void saveInstanceState() {
Log.v(Tag, "saveInstanceState()");
// get entered data
String name = mTxtName.getText().toString();
String notes = mTxtNotes.getText().toString();
// save data in Shared Prefs
PreferenceManager.getDefaultSharedPreferences(mContext)
.edit()
.putInt(KeyAmmunitionId, mAmmunitionId)
.putString(KeyName, name)
.putString(KeyNotes, notes)
.putString(StringUtils.CurrentFragmentKey, Tag)
.commit();
}
private void restoreInstanceState() {
Log.v(Tag, "restoreInstanceState()");
mTxtName = (EditText)getActivity().findViewById(R.id.frag_manage_ammunition_txtName);
mTxtNotes = (EditText)getActivity().findViewById(R.id.frag_manage_ammunition_txtNotes);
if (PreferenceManager.getDefaultSharedPreferences(mContext).contains(KeyName)) {
String ammunitionName = PreferenceManager.getDefaultSharedPreferences(mContext).getString(KeyName, StringUtils.EMPTY_STRING);
mTxtName.setText(ammunitionName);
}
if (PreferenceManager.getDefaultSharedPreferences(mContext).contains(KeyNotes)) {
String ammunitionNotes = PreferenceManager.getDefaultSharedPreferences(mContext).getString(KeyNotes, StringUtils.EMPTY_STRING);
mTxtNotes.setText(ammunitionNotes);
}
}
private void clearInstanceState() {
Log.v(Tag, "clearInstanceState()");
PreferenceManager.getDefaultSharedPreferences(mContext)
.edit()
.remove(KeyAmmunitionId)
.remove(KeyName)
.remove(KeyNotes)
.commit();
}
instead of using SharedPrefs, I've found that an internal headless fragment that retains its state is much easier to implement/cleaner.
Inside your fragment class you have this class....
/**
* "Headless" Fragment that retains state information between
* configuration changes.
*/
public class RetainedFragment extends Fragment {
/**
* internal storage to be kept put here
*/
// assuming the only 'view' in the parent fragment is this editText with some string value.
String editTextValue;
public RetainedFragment(){
editTextValue = ""; //init values on first time in.
}
/**
* Hook method called when a new instance of Fragment is
* created.
*
* #param savedInstanceState
* object that contains saved state information.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Ensure the data survives runtime configuration changes.
setRetainInstance(true);
}
// have getter/setter methods
Then in your outer fagment just 'create' the UI based off the values stored in the inner headless fragment.
OR...
You can use the RetainedFragmentManager that we figured out. Its a bit wonky and can be a bit confusing to figure out at first, but its a headless fragment that allows you to store java objects in hashmap-like manner, and they will persist across configuration changes, but won't exist if your app is fully closed, etc.
https://github.com/douglascraigschmidt/POSA-15/blob/master/ex/ImageDownloads/src/vandy/mooc/common/RetainedFragmentManager.java

Activity retain instance of Fragment on onSaveInstance

I'm writing an application where using onSaveInstance(..) to retain values on device config change(say device font, local).
Here application Activity using multiple Fragment to display. After chnage in config change when coming back to app then onCreate(..) execute and app checked if Bundle object is null. Now till this state app didn't set any Fragment child back but but still last set Fragment (before change in config) child started to execute it's life cycle method.
How can prevent it, one way to check Same Bundle object from Fragment child, same as Activity and return. But is there other way to remove child from Activity on device-config change!
Activity reference code to handle re-calling:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.reading_activity);
if(savedInstanceState == null) {
init(savedInstanceState);
} else {
// Don't do anything
}
}
Here init(..) responsible to set Fragment child.

onCreate being called on Activity A in up navigation

So I have an Activity A and an Activity B. I want Activity A to be able to navigate to Activity B with the press of a button. That works, but when I use the up navigation(the home button in the action bar) to navigate back to Activity A, onCreate() is called again and the old information that the user typed in is lost.
I've seen: onCreate always called if navigating back with intent, but they used Fragments, and I'm hoping not to have to redesign the entire app to use fragments. Is there any way I can stop onCreate() from being called every time Activity A becomes active again?
This behavior is totally fine and wanted.
The system might decide to stop Activities which are in background to free some memory.
The same thing happens, when e.g. rotating the device.
Normally you save your instance state (like entered text and stuff) to a bundle and fetch these values from the bundle when the Activity is recreated.
Here is some standard code I use:
private EditText mSomeUserInput;
private int mSomeExampleField;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO inflate layout and stuff
mSomeUserInput = (EditText) findViewById(R.id.some_view_id);
if (savedInstanceState == null) {
// TODO instanciate default values
mSomeExampleField = 42;
} else {
// TODO read instance state from savedInstanceState
// and set values to views and private fields
mSomeUserInput.setText(savedInstanceState.getString("mSomeUserInput"));
mSomeExampleField = savedInstanceState.getInt("mSomeExampleField");
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// TODO save your instance to outState
outState.putString("mSomeUserInput", mSomeUserInput.getText().toString());
outState.putInt("mSomeExampleField", mSomeExampleField);
}
You can make the up button behave like pressing back, by overriding onSupportNavigateUp()
#Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
If you want to navigate from child to parent without recreating the parent (calling onCreate method), you may set the android:launchMode="singleTop" attribute for the parent activity in your AndroidManifest.xml

onBackPressed() behaviour with ActivityGroups

Quick question: I have an activitygroup. Within that Activitygroup I have an activity. If I press back while inside this activity. the onBackPressed method of the activity is called - Not the Activitygroups onBackPressed - Why is that ?
EDIT: Got my answer but the problem remains. Here follows code and explanation of my original issue:
I am using ActivityGroups within a TabHost and as such have been "forced" into overriding onBackPressed. I can navigate through my application without issue by pressing back on my phone and by pressing the tabs on my tabhost. But I cannot interact with the interface after pressing Back.
Once I press one of the tabs on the tabhost again I can interact with everything like normal. Why is this happening? Do I need to override onResume?
Relevant code
SettingsActivityGroup :
public class SettingsActivityGroup extends ActivityGroup
{
// Keep this in a static variable to make it accessible for all the nested activities, lets them manipulate the view
public static SettingsActivityGroup group;
// Need to keep track of the history if you want the back-button to work properly, don't use this if your activities requires a lot of memory.
private ArrayList<View> history;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Allocate history
this.history = new ArrayList<View>();
// Set group
group = this;
// Start root (first) activity
Intent myIntent = new Intent(this, SettingsActivity.class); // Change to the first activity of your ActivityGroup
myIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
ReplaceView("SettingsActivity", myIntent);
}
/*
* Replace the activity with a new activity and add previous one to history
*/
public void ReplaceView(String pId, Intent pIntent)
{
Window window = getLocalActivityManager().startActivity(pId, pIntent);
View view = (window != null) ? window.getDecorView() : null;
// Add the old activity to the history
history.add(view);
// Set content view to new activity
setContentView(view);
}
/*
* Go back from previous activity or close application if there is no previous activity
*/
public void back()
{
if(history.size() > 1)
{
// Remove previous activity from history
history.remove(history.size()-1);
// Go to activity
View view = history.get(history.size() - 1);
Activity activity = (Activity) view.getContext();
// "Hack" used to determine when going back from a previous activity
// This is not necessary, if you don't need to redraw an activity when going back
activity.onWindowFocusChanged(true);
// Set content view to new activity
setContentView(view);
}
else
{
// Close the application
finish();
}
}
/*
* Overwrite the back button
*/
#Override
public void onBackPressed()
{
// Go one back, if the history is not empty
// If history is empty, close the application
SettingsActivityGroup.group.back();
return;
}
}
Arbitrary child of SettingsActivityGroup(CallForwardActivity)
public class CallForwardActivity extends ListActivity
{
....
#Override
public void onBackPressed()
{
// Go one back, if the history is not empty
// If history is empty, close the application
SettingsActivityGroup.group.back();
return;
}
}
Because I believe calling onBackPressed() of the currently selected activity is the desired behavior.
It's also worth noting that ActivityGroup is deprecated, but I assume you are coding for <3.0 and don't fancy working with the support libraries.
Regarding your edited question:
Another question on this site cites this article as a good ActivityGroup example, and I would agree http://ericharlow.blogspot.com/2010/09/experience-multiple-android-activities.html
This example just calls finish() on the current activity when back is pressed, and lets the os restart the previous activity, which is simpler than what you are doing, and will hopefully work! You can just call getParent() in your child activities too to avoid using that static reference (just seems easier to read to me that way!).

Categories

Resources