Save the Tab state during orientation change - android

I have 2 tabs , for example Tab1 & Tab2 which is displayed on the screen. Let the tabs be displayed on the PORTRAIT orientation.
Tab1 displays Activity1 & Tab2 displays Activity2.
Currently , the selected tab state is Tab2 . Now , I change the orientation for PORTRAIT to LANDSCAPE . On changing the orientation to LANDSCAPE mode , instead of displaying Tab2 , currently Tab1 is displayed.
Basically , I want to save the Tab state when there is orientation change.
In order to perform the objective of saving the tab state , I am writing the following code:
protected void onPause() {
super.onPause();
saveCurrentTabState(getSelectedTab());
}
private void saveCurrentTabState(int value) {
PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(
"tabState", value).commit();
}
#Override
protected void onResume() {
super.onResume();
setCurrentTab(PreferenceManager.getDefaultSharedPreferences(this)
.getInt("tabState", 0));
}
I wanted to know , is my approach correct or not & whether the above code is a proper way of saving the tab state on changing the orientation.

That's not the way it should be done... instead use the:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tabState", getSelectedTab());
}
Then, on the onCreate method:
public void onCreate(Bundle state){
// do the normal onCreate stuff here... then:
if( state != null ){
setCurrentTab(state.getInt("tabState"));
}
}
The Robby's solution will work too and involves using the onRetainNonConfigurationInstance method. I actually like and prefer that method over onSaveInstanceState since it allows you save a complex object that represents the state of the app, not only parceables inside a Bundle.
So when to use one of the other? It depends on the data you need to save/restore the state of the app. For simple things like saving the tab state, it's almost the same.

That's not the best way. You should use onRetainNonConfigurationInstance() and getLastNonConfigurationInstance() to retain the state between config changes. Those methods are specifically for saving state during config changes.
public Object onRetainNonConfigurationInstance() {
return mTabHost.getCurrentTab();
}
public void onCreate() {
...
Integer lastTab = (Integer) getLastNonConfigurationInstance();
if(lastTab != null) {
mTabHost.setCurrentTab(lastTab);
}
...
}

Related

Android change fragment of navigation view

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();
}

RecyclerView restore items and managing pagination after rotation

I'm wondering if there is a way to restore, after rotation, my list of results without retaining the fragment.
Basically I have a fragment which calls, through a presenter, some api (using RxJava and Retrofit). I added pagination so I can make a call only when I need more data scrolling down.
I'm in the following scenario:
- I scroll the list down in order to call the second page from the web
- I rotate the screen
In this case what I would need is to show all the items, from page 1 and 2, and then scrolling to the correct position (for this I can use the onSaveInstanceState of the LayoutManager).
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FwApplication.component(getActivity()).inject(this);
if (savedInstanceState != null) {
mRxRunning = savedInstanceState.getBoolean(EXTRA_RX);
mQuery = savedInstanceState.getString(QUERY_ARG);
mCurrentPage = savedInstanceState.getInt(CURRENT_PAGE);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(QUERY_ARG, mQuery);
outState.putInt(CURRENT_PAGE, mCurrentPage);
outState.putParcelable(LAYOUT_STATE, mRecyclerView.getLayoutManager().onSaveInstanceState());
super.onSaveInstanceState(outState);
}
#Override
public void onResume() {
super.onResume();
mPresenter.onResume(mRxRunning, mCurrentPage);
}
Is there a way to save the items without calling setRetainInstance? Moreover I would avoid to call the api in order to get all the items back.
The items are POJOs so it won't be a list of simple strings.
Try to edit the Manifest file. Add this line to block with activity:
android:configChanges="keyboardHidden|orientation|screenSize">
It will prevent Activity restart on such events (specially on orientation change)

Save state of TextView and Spinner inside of CustomView

I have 3 custom views.
The first one works great. It contains an EditText when I launch an intent and come back whatever the user entered is restored.
The 2nd contains a TextView and the 3rd a Spinner. They do not save when I launch my intent and return.
I think know how to preserve the data using onSaveInstanceState and onRestoreInstanceState in my custom views, However when the activity containing the custom views is not killed (meaning it is only paused), and I return onRestoreInstanceState is not called.
This is what I'm calling in my custom views when I need to save them.
#Override
public Parcelable onSaveInstanceState() {
textValue = editText.getText().toString();
Bundle bundle = new Bundle();
bundle.putParcelable("instanceState", super.onSaveInstanceState());
bundle.putString(TEXT_VALUE_KEY, this.textValue);
return bundle;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
textValue = bundle.getString(TEXT_VALUE_KEY);
editText.setText(textValue);
super.onRestoreInstanceState(bundle.getParcelable("instanceState"));
return;
}
super.onRestoreInstanceState(state);
}
I'm unsure I what I should do since onRestoreInstanceState is not called. I think the EditText customView works because default android behavior saved them temporarily, but it doesn't save spinners or TextViews.
You should change your onCreate() method in order to check if the Activity has already called onSavedInstanceState like this:
#Override
public void onCreate(Bundle savedInstanceState) {
if(savedInstanceState == null) {
// The activity haven't called onSavedInstanceState(), which means you should initialize your objects and UI here
}
else {
// Whatever states you saved onSavedInstanceState() are stored in savedInstaceState, so use them to reconstruct your customViews
}
}

Do something right before Screen Rotation?

i want to do_something() whenever the device rotates from Portrait to Landscape.
i have added <activity android:configChanges="orientation" > in my manifest. So onConfigurationChanged() will be called whenever i rotate my device. And it will not re-create the Activity.
In my onConfigurationChanged() function:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
do_something();
}
then_fire_off_the_screen_rotation_as_normal();
// which means re-start the Activity
// i expect it will fire off onSaveInstanceState() -> onPause() -> onStop() -> onDestroy() -> onCreate() -> onStart() -> onRestoreInstanceState() -> onResume()
}
My problem is, i dont know what can i do in then_fire_off_the_screen_rotation_as_normal().
i have tried using setRequestedOrientation() but it seems it is not the function serving this purpose:
When i pass in ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, ActivityInfo.SCREEN_ORIENTATION_SENSOR, or ActivityInfo.SCREEN_ORIENTATION_USER, the Activity is still not re-started. (Just like not calling.)
When i pass in ActivityInfo.SCREEN_ORIENTATION_NOSENSOR, the orientation sensor turned off. (As it said.)
When i pass in ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, the Activity was Force Close with AndroidRuntime:NullPointerException.
(Be specific: It is because there are programmatically added Fragments in the Portrait layout, but not in the Landscape layout. In the do_something() function, i will remove those Fragments, as they are not needed in Landscape mode.)
Well, i guess this simple way can serve what i want to:
Not to add <activity android:configChanges="orientation" >. Means NO NEED OVERRIDE onConfigurationChanged().
In onSaveInstanceState():
#Override
protected void onSaveInstanceState(Bundle outState) {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
do_something();
}
super.onSaveInstanceState(outState);
}
* Be sure you do_something() before super.onSaveInstanceState().
(Because in my case, i remove the Fragments from the Layout, and from the Activity. If it is after super.onSaveInstanceState(), the Layout will already be saved into the Bundle. Then the Fragments will also be re-created after the Activity re-creates. ###)
### I have proved this phenomenon. But the reason of What to determine a Fragment restore upon Activity re-create? is just by my guess. If you have any ideas about it, please answer my another question. Thanks!
Well, further improvement on the method above, which fixes the problem raised in the first comment: (still simple)
#Override
protected void onSaveInstanceState(Bundle outState) {
if (isPortrait2Landscape()) {
do_something();
}
super.onSaveInstanceState(outState);
}
private boolean isPortrait2Landscape() {
return isDevicePortrait() && (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
}
and the isDevicePortrait() would be like:
private boolean isDevicePortrait() {
return (findViewById(R.id.A_View_Only_In_Portrait) != null);
}
* Notice that we cannot use getResources().getConfiguration().orientation to determine if the device is currently literally Portrait. It is because the Resources object is changed RIGHT AFTER the screen rotates - EVEN BEFORE onSaveInstanceState() is called!!
If you do not want to use findViewById() to test orientation (for any reasons, and it's not so neat afterall), keep a global variable private int current_orientation; and initialise it by current_orientation = getResources().getConfiguration().orientation; in onCreate(). This seems neater. But we should be aware not to change it anywhere during the Activity lifecycle.
// which means re-start the Activity
// i expect it will fire off onSaveInstanceState() -> onPause() -> onStop() -> onDestroy() -> onCreate() -> onStart() -> onRestoreInstanceState() -> onResume()
This assumption that onPause(), onStop(), etc. will fire is not correct. By using onConfigurationChanged you are telling Android to not go through those steps and instead you will handle the saving of information on the layout in temp variables and then recall the InitializeUI() and then set the layout back to how you want. I will post an example of something I have that is similar and working for me in one moment.
Edit: Here is the sample
#Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
setContentView(R.layout.main);
Log.i("configChange", "configChange");
String tmp = connect.getText().toString();
boolean onTmp = on.isEnabled();
boolean offTmp = off.isEnabled();
boolean connTmp = reconnect.isEnabled();
InitializeUI();
connect.setText(tmp);
on.setEnabled(onTmp);
off.setEnabled(offTmp);
reconnect.setEnabled(connTmp);
}
now my InitializeUI() method is nothing but a bunch of findViewById's and seetting up the onClickListeners. Also make sure to call InitializeUI() in youronCreate()`.
Hope this helps some.

Fragment saveInstanceState is coming as null after orientation change

I have an activity with action bar tab. Each tab contain a fragment. Now when I rotate my device, bundle in my corresponding fragment is coming as null. This is taken care when I using device post android 3.2, but it is happening when device is Andoird3.0. I am having a headache after working on this issue. I crossed check various link on SO, but no help. Although I have given enough details, still will provide some code snippet as at various cases user ask for code snippet.
In my fragment class I am storing this value
#Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putBoolean("textboxVisible", true);
}
this is storing one boolean variable which it retrived as below.
/**
* Function called after activity is created. Use this
* method to restore the previous state of the fragment
*/
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null)
{
//restore the state of the text box
boolean textboxVisible = savedInstanceState.getBoolean("textboxVisible");
if (textboxVisible)
{
//do some stuff
}
}
}
but after rotation savedInstanceState is coming as null.
I don't what is going wrong. I have read in some document that below 3.2 the onCreateView() of
fragment is not called with bundle value. But to deal with this. Any help will be appreciated.
if you use setRetainInstance(true) the savedInstance bundle is always gonna be null after orientation changed. SO you cannot really save something with it, but what you can do if you need to save something, is to put it in a data member of the fragment, because setRetainInstance(true) preserves the fragment and doesn't destroy it, so after the device was rotated you gonna have the same values.
Try to get the savedInstanceState in onCreate of the Fragment.
Like
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if (savedInstanceState != null) {
// IT MUST NOT BE NULL HERE
}
}
Please try... i hope it will work

Categories

Resources