Fragment setRetainInstance(true) save View properties

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-22 15:09:53

问题


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)

回答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:

  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.




回答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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!