bcorso
bcorso

Reputation: 47088

Fragment setRetainInstance(true) save View properties

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 Views?

More specifically:

  1. if setRetainInstanceState is true, is the above code the best way to retain the View states between orientation calls?
  2. If I'm not interacting with background tasks, should I just use setRetainInstanceState(false) and use Bundles to maintain the View states? (Note: bundles cannot be used if setRetainInstance == true)

Upvotes: 2

Views: 5485

Answers (1)

Marcelo Idemax
Marcelo Idemax

Reputation: 2810

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:

  1. Do not keep your view created! Recreate your view every time onCreateView is called. To keep states and more use global variables.

  2. 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 your Bundle with view state at onSaveInstanceState 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.

Upvotes: 5

Related Questions