Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving and restoring ListView with Custom List Adapter

I'm currently using a custom list adapter and modifying the rows of the listview at runtime (changing text, buttons etc).

I want to find a way to save/restore the changes made to the listview at runtime when I start another fragment and then come back to the fragment containing the listview.

Currently the listview is being reloaded every time and all the runtime changes are lost.

The following shows how I am setting the list adapter.

 public void setAdapterToListView(ArrayList<item> list) {
        ItemList = list;
        ItemAdapter adapter = new MenuItemAdapter(list, getActivity(), this);
        ItemListView.setAdapter(adapter);
    }

I am then using a custom list adapter to make the changes (mentioned above) at runtime. Here's a snippet of how I am changing things at runtime:

if (holder.qty.getText().toString().equals("Qty")) {
    relativeLayout.setBackgroundColor(row.getResources().getColor(R.color.navDrawerL ightBlue));
    holder.qty.setText("1");
    holder.qty.startAnimation(anim);
    holder.removeItem.setBackgroundResource(R.drawable.remove_item_red);
    holder.removeItem.setEnabled(true);

   ...

Is there a recommended way to approach this issue?

like image 686
Osborne Cox Avatar asked Mar 23 '15 00:03

Osborne Cox


1 Answers

Adapter

You will want your custom adapter to implement some sort of getter for it's internal data. For instance

public ArrayList<YourDataType> getList() {
    return new ArrayList<YourDataType>(mAdapterData);
}

Activity

Then in your activity/fragment, you'll need to save and restore that data.

private static final String STATE_LIST = "State Adapter Data"

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelableArrayList(STATE_LIST, getAdapter().getList());
}

While there is an onRestoreInstanceState() method you could override, I typically restore during onCreate(). Usually more convenient to when other things are getting instantiated. Either or is viable.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //If restoring from state, load the list from the bundle
    if (savedInstanceState != null) {
        ArrayList<YourDataType> list = savedInstanceState.getParcelableArrayList(STATE_LIST);
        ItemAdapter adapter = new MenuItemAdapter(list, getActivity(), this);
    } else {
          //Else we are creating our Activity from scratch, pull list from where ever you initially get it from
          ArrayList<YourDataType> list = getInitData();
          ItemAdapter adapter = new MenuItemAdapter(list, getActivity(), this);
    }
}

YourDataType

You didn't mention what YourDataType was but I'm assuming it's a custom class. In order to work with the bundled savedstate, it must implement Parcelable. Android's dev link on Parcelable and a StackOverFlow post explaining how to write your own custom class with Parcelable.

Update

Depending on what you are doing with the fragment will dictate if the onSavedInstanceState() method will be called. From the way your question is asked, I'm assuming you are pushing one fragment onto the backstack to load another on top. Then hitting the back button will reloaded that fragment from the backstack...and pass along the bundled state to reload from.

Of course that is just one of many scenarios. It's perfectly possible for the fragment in question to only perform onPause() followed by onStop()...then when redisplaying the fragment, only seeing it do onStart() followed by onResume(). In this case, there would not be a saved state because the fragment was never fully destroyed, so there is nothing to restore. It should be in the same state. If it's not and instead you are seeing it reload the initial data...then you are probably reloading the initial data in or after onStart(). You'll want to move all that initializing data to onCreate() instead.

Another possible case is that you completely destroy the fragment without ever putting it on the backstack. Re-initializing it would not have a saved state to pull from. If you wanted to "restore" this fragment back to the state beforehand, then you can not rely upon onSavedInstanceState(). You will need to persist that information manually somewhere in memory, to disk, or a DB yourself. Then pull from it accordingly.

Life cycles with fragments are unfortunately really complex and depend greatly on usage and even between the support library vs native fragments.

like image 179
Jay Soyer Avatar answered Nov 14 '22 21:11

Jay Soyer