Maintaining fragments instance state between transactions

833 Views Asked by At

From all the searches I have found on SO stating that you should save your instance state in the @Override public void onSaveInstanceState(Bundle outState)

However This is tightly coupled with the activities lifestyle.

How can I save the state of my listview in a fragment that gets swapped out with another fragment. I have one main activity which all the fragments are loaded into.

I have tried this so far:

    @Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    //Save adapter data so that when the fragment is returned to it can be resused.
    ArrayList<CategoryMobileDto> categories = new ArrayList<CategoryMobileDto>();
    for(int i=0; i < adapter.getCount();i++)
    {
        categories.add(adapter.getItem(i));
    }
    String persistData = new Gson().toJson(categories);
    outState.putString("Categories", persistData);        
}

and then in my OnCreate();

   if(savedInstanceState!=null)
    {
        String data =savedInstanceState.getString("Categories");
        Type collectionType = new TypeToken<ArrayList<CategoryMobileDto>>() {
        }.getType();
        adapter.addAll(gson.<Collection<CategoryMobileDto>>fromJson(data, collectionType));
        adapter.notifyDataSetChanged();
    }else{
       // Make request to server
    }

however savedInstanceState is always null. But this makes sense as my activity is not being destroyed and recreated.

This is how I transition from one fragment to another:

    fragment.setArguments(args);
    FragmentManager fragmentManager = getFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.replace(R.id.container, fragment, "ProductListFragment");
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();

Is there a way i can save the state of my listview when the fragment is removed and then restore it again when the fragment is popped from the back-stack?

2

There are 2 best solutions below

3
droidd On

Move this code from onCreate() to onActivityCreated() of Fragment

if(savedInstanceState!=null)
    {
        String data =savedInstanceState.getString("Categories");
        Type collectionType = new TypeToken<ArrayList<CategoryMobileDto>>() {
        }.getType();
        adapter.addAll(gson.<Collection<CategoryMobileDto>>fromJson(data, collectionType));
        adapter.notifyDataSetChanged();
    }else{
       // Make request to server
    }

If you have any query please let me know.

3
Eldhose M Babu On

You can use the Arguments with the Fragment(Only if you have the data to show in fragment before the fragment is loaded means attached). You can setArguments to a fragment which will be persisted when you go to another fragment by fragment transaction and when you come back, load the fragment from the getArguments function.

public void setArguments (Bundle args)

Added in API level 11 Supply the construction arguments for this fragment. This can only be called before the fragment has been attached to its activity; that is, you should call it immediately after constructing the fragment. The arguments supplied here will be retained across fragment destroy and creation.

public final Bundle getArguments ()

Added in API level 11 Return the arguments supplied when the fragment was instantiated, if any.

Please find the sample code below for passing data between fragments :

main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/flContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" >

</FrameLayout>

MainActivity.java

public class MainActivity extends Activity implements IFragContainer {

private static final String FRAG_TAG = "FragTag";
private FragBase mFrag;
private String dataToBePassedBack;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    changeFragment(FragA.class, "Data to Frag A");
}

@Override
public void changeFragment(Class<? extends FragBase> fragClass, String data) {
    try {
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        mFrag = fragClass.newInstance();
        Bundle args = new Bundle();
        args.putString("DATA", data);
        mFrag.setArguments(args);
        ft.replace(R.id.flContainer, mFrag, FRAG_TAG);
        ft.addToBackStack(mFrag.toString());
        ft.commit();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
public void onBackPressed() {
    dataToBePassedBack = mFrag.getDataToPassBack();
    FragmentManager mgr = getFragmentManager();
    mgr.executePendingTransactions();

    boolean doCheckAndExit = true;

    for (int i = mgr.getBackStackEntryCount() - 1; i > 0; i--) {
        BackStackEntry entry = mgr.getBackStackEntryAt(i);

        if (!TextUtils.isEmpty(entry.getName())) {
            mgr.popBackStackImmediate(entry.getId(),
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
            doCheckAndExit = false;
            break;
        }
    }

    if (doCheckAndExit) {
        finish();
    } else {
        mFrag = (FragBase) mgr.findFragmentByTag(FRAG_TAG);
    }
}

@Override
public String getDataToBePassedBack() {
    return dataToBePassedBack;
}

}

IFragContainer.java

public interface IFragContainer {
void changeFragment(Class<? extends FragBase> fragClass, String data);

String getDataToBePassedBack();
}

FragBase.java

public abstract class FragBase extends Fragment {

public String getDataToPassBack(){
    return null;
}

}

FragA.java

public class FragA extends FragBase {

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    Button btn = new Button(getActivity());
    final IFragContainer fragContainer = (IFragContainer) getActivity();
    if (TextUtils.isEmpty(fragContainer.getDataToBePassedBack())) {
        btn.setText(getArguments().getString("DATA"));
    } else {
        btn.setText(fragContainer.getDataToBePassedBack());
    }
    btn.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT));
    btn.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            fragContainer.changeFragment(FragB.class, "Data to Frag B");
        }
    });
    return btn;
}

}

FragB.java

public class FragB extends FragBase {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    Button btn = new Button(getActivity());
    btn.setText(getArguments().getString("DATA"));
    btn.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT));
    btn.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            getActivity().onBackPressed();
        }
    });
    return btn;
}

@Override
public String getDataToPassBack() {
    return "Data from Frag B to A";
}
}