问题
I'm confused about the best practice for saving the states of Views that live inside of fragments in Android.
I thought that setRetainInstance(true)
would do the trick; however, while this does retain the old global View
references declared in the Fragment
, those Views will not be used when inflating the layout. Thus, I have to manually transfer the properties from the old global View references to the new Views in the inflated layout. See the code below for how I'm doing this.
public static class MyFragment extends Fragment {
// I need to save the state of two views in the fragment
ProgressBar mProgress;
TextView mText;
@Override public void onCreate(Bundle savedInstanceState) {
// savedInstanceState == null if setRetainInstance == true
super.onCreate(savedInstanceState);
// Retain the instance between configuration changes
setRetainInstance(true);
}
@Override public View onCreateView(LayoutInflater i, ViewGroup c, Bundle b) {
// Inflate xml layout for fragement (orientation dependent)
View v = i.inflate(R.layout.fragment_main, c, false);
// Grab Views from layout
ProgressBar tmpProgress = (ProgressBar) v.findViewById(R.id.progress);
TextView tmpText = (TextView) v.findViewById(R.id.text);
// If View References exist, transfer state from previous Views
if(mProgress != null){
tmpProgress.setProgress(mProgress.getProgress());
tmpText.setText(mText.getText());
}
// Replace View references
mProgress = tmpProgress;
mText = tmpText;
return v;
}
}
It's my feeling that setRetainInstanceState(true)
should mainly be used for keeping a fragment alive so that background processes can maintain a connection to it. If that's the case should I not be using this method to retain the states of my View
s?
More specifically:
- if
setRetainInstanceState
istrue
, is the above code the best way to retain theView
states between orientation calls? - If I'm not interacting with background tasks, should I just use
setRetainInstanceState(false)
and useBundle
s to maintain the View states? (Note: bundles cannot be used ifsetRetainInstance == true
)
回答1:
Use setRetainInstance
DO NOT keep your view in memory just keep same instance as a kind of Singleton
based on tag
or ID
that you give to your fragment. It's good because when you Fragment
goes to background your view is destroyed and not unecessary memory.
Answering your two questions:
Do not keep your view created! Recreate your view every time onCreateView is called. To keep states and more use global variables.
No, once you set it your
Fragment
, Android OS will keep same instance for same tag or ID so when it goes to background keep in mind to save yourBundle
with view state atonSaveInstanceState
to recover view status at next time.
Read more about Fragments lifecycle here: http://developer.android.com/guide/components/fragments.html#Lifecycle
And about onSaveInstanceState
, see more here: http://developer.android.com/reference/android/app/Fragment.html#onSaveInstanceState(android.os.Bundle)
For example you could do something like that:
package com.example.stackoverflowsandbox;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MyFragment extends Fragment {
public static String VIEW_STATE_A = "a";
public static String VIEW_STATE_B = "b";
public static String VIEW_STATE_C = "c";
private String currentState = MyFragment.VIEW_STATE_A;
private View view;
public void changeStateTo( final String newState ) {
if ( this.currentState != newState ) {
this.currentState = newState;
this.updateViewState( this.currentState );
}
}
@Override
public View onCreateView( final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState ) {
this.setRetainInstance( true );
this.view = inflater.inflate( R.layout.my_view, null );
this.updateViewState( this.currentState );
return this.view;
}
@Override
public void onDestroyView() {
this.view = null; // release the reference to GC
super.onDestroyView();
}
private void updateViewState( final String state ) {
// do what you want to do with your view here...
}
}
Change your fragment
state by changeStateTo
.
回答2:
Here are some good reasons why she choose to setRetainInstance:Handling Configuration Changes with Fragments
public class SomeFragment extends Fragment {
/**
* This method will only be called once when the retained
* Fragment is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
}
}
Then in your parent Activity
, check for an existing instance of your Fragment
before instantiating a new one.
If the Activity’s onCreate(Bundle)
is being called again due to a configuration change, FragmentManager
will have an existing instance of your Fragment
.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_priceindex);
// create a new Fragment only if we can't find an existing one
if (getSupportFragmentManager.findFragmentById(R.id.containerId) == null){
getSupportFragmentManager.beginTransaction().add(R.id.containerId,
new SomeFragment()).commit();
}
}
Edited:
Sorry for the bad comprehension of the exposed question, but I believe that even if there is no interaction with running tasks, there are still good reasons not to recreate a view.
Let’s assume a typical scenario – an expensive operation during Activity creation (onCreate(Bundle)), while at the same time showing a loding screen, while your app is doing the heavy lifting. You did not however, handled screen rotation. This will result in the following user experience: your user loads the app, sees a loading screen, rotates the device, sees the same loading screen again, rotates it again, sees yet another loading screen… well, you get the picture. Oh, wait, there’s also the potential of your app crashing :)
来源:https://stackoverflow.com/questions/24830917/fragment-setretaininstancetrue-save-view-properties