0

I'm trying to handle orientation changes in my activity. I have a ListView that gets populated by a custom adapter. The adapter accepts an ArrayList<Listing> where Listing is custom object I have created. I know the ListView works but on an orientation change it loses my data.

I might be missing something obvious but after following all the tutorials and SO posts I still can't get this to work. I'm not trying to pass this to a new intent. I'm trying to handle orientation changes.

Here's my custom object that implements Parcelable:

package xx.xx.xx.xxxxxxx;

import android.os.Parcel;
import android.os.Parcelable;

public class Listing implements Parcelable {
    private String name;
    private String rating;
    private String cuisines;

    public Listing(){

    }

    public Listing(String name, String rating, String cuisines) {
        this.name = name;
        this.rating = rating;
        this.cuisines = cuisines;
    }



    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRating() {
        return rating;
    }

    public void setRating(String rating) {
        this.rating = rating;
    }

    public String getCuisines() {
        return cuisines;
    }

    public void setCuisines(String cuisines) {
        this.cuisines = cuisines;
    }



    private Listing(Parcel in) {
        // Recreate Object using from the Parcelled String array
        String[] data = new String[3];

        in.readStringArray(data);

        this.name = data[0];
        this.rating = data[1];
        this.cuisines = data[2];
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // Parcel using a String array
        dest.writeStringArray(new String[] {
                this.name,
                this.rating,
                this.cuisines
        });
    }

    public static final Parcelable.Creator<Listing> CREATOR = new Parcelable.Creator<Listing>() {
        @Override
        public Listing createFromParcel(Parcel in) {
            return new Listing(in);
        }

        @Override
        public Listing[] newArray(int size) {
            return new Listing[size];
        }
    };
}

In my MainActivity I have the following to try and handle configuration changes:

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

        // Save the result ArrayList<Listing>
        savedInstanceState.putParcelableArrayList("result", result);

    }

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

        // Try and restore the List results
        result = savedInstanceState.getParcelableArrayList("result");
        adapter.notifyDataSetChanged();
    }

I don't get any obvious errors but when I debug and add a break point in my code I can see that savedInstanceState has mParcelledData = null. I take that to mean that I'm not using putParcelableArrayList correctly.

What would help massively is if someone can tell me how to debug this in a better way so I can provide better information in the question or solve this myself.

Thanks

Update:

When I look in savedInstanceState I see that mMap has java.lang.ClassNotFoundException exceptions where I am hoping key value pairs are. Is this of interest to solving the question?

mez.pahlan
  • 1,053
  • 2
  • 11
  • 25
  • You can debug easily by setting a breakpoint in your IDE and running the debug configuration for your module/project. Or log all the things. – petey Nov 12 '14 at 14:34
  • Thanks @petey. I know how to do that, but clearly I don't know what to look for when I do this. I have assigned a breakpoint just before the onSaveInstanceState and step through the code line by line checking the variables. Whilst it tells me that the mParcelledData is null, I still don't know why. – mez.pahlan Nov 12 '14 at 14:37

2 Answers2

1

why can't you do like this? :

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(name);
    dest.writeString(rating);
    dest.writeString(cuisines);
}

private Listing(Parcel in) {
    this.name = in.readString();
    this.rating = in.readString();
    this.cuisines = in.readString();
}

order in Parcel in is important! also you use

putParcelableArrayList

but you use array, not ArrayList

edit: yes, you can put multiple values same type (multiple Strings, ints, other Parcelables, etc.), you only need to remember order when recreating (no keys in Parcel)

edit 2 - ans for comment, with some coee: so maybe that way:

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeList(new ArrayList<String>(){...});
}

public Listing(Parcel in) {
    ArrayList<Strings> sArrList= new ArrayList<Strings>();
    in.readList(sArrList, getClass().getClassLoader());
    // now sArrListis already filled
}

it might be an ArrayList of another your class which implements Parcelable and proper Parcel saving/restoring/creator etc. result Object is an ArrayList yes? also lot of Google answers suggest creating separated parcelable class with this arrayList passed only and write this object (which saving inside owned ArrayList like above) to instance Bundle instead of use plain method writeParcelableArrayList (dunno why, name is so obvius...)

snachmsm
  • 17,866
  • 3
  • 32
  • 74
  • To reply to your update: I've definitely defined result as an ArrayList in my MainActivity. Should I be looking somewhere else? Thanks for the help. – mez.pahlan Nov 12 '14 at 14:47
  • To answer your question in your edit: results in MainActivity is of type ArrayList. Listing is my custom class I've created that implements Parcelable. I don't see where your update to add ArrayList fits into all of this. – mez.pahlan Nov 12 '14 at 16:45
  • 1
    String might be your custom class containing inside this ArrayList, then this object may be stored with putParcelable. But glad you found your solution (below). Cheers! – snachmsm Nov 13 '14 at 07:16
0

I figured this out. There was actually two things at play here.

  1. The state was being stored and restored correctly. Why the debugger showed the nulls and the java.lang.ClassNotFoundException I do not know. But printing the variables to the console proved to me that the state could be recovered from the Bundle correctly.
  2. On a rotate the configuration changes and the (effectively) a new Activity is kicked off. The lifecycle methods all occur again. Originally my adapter was created with a reference to the current Activity (this) within the onCreate() method. Of course, when the device rotates and a configuration change occurs the lifecycles start up again and my original reference to adapter no longer applies to this new Activity. Calling notifyDataSetChanged() is making updates to the old Activity and not the new one. By moving the adapter creation and binding to onResume() I was able to recreate the ListView on rotation change! I also no longer need the notifyDataSetChanged() onResume().

Hope this helps someone else!

mez.pahlan
  • 1,053
  • 2
  • 11
  • 25