29

I have an android project where I have a class. In that class is an ArrayList<Choices>. I will be getting some XML, parsing it out, then making objects out of it which I will be passing to another activity. I'm choosing Parcelable for this.

Is Parcelable a good choice? Am I doing everything correctly? I'm not familiar really with Parcelable. My ArrayList is of another class that I made within this class. Will it properly pass that ArrayList of objects to the Parcel with it not extending Parcelable and stuff?

import java.util.ArrayList;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.os.ParcelableCompat;

public class Question implements Parcelable{


String id;
String text;
String image;
ArrayList<Choices> CHOICES;


public Question(String id, String text, String image) {
    super();
    this.id = id;
    this.text = text;
    this.image = image;
}

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public String getText() {
    return text;
}

public void setText(String text) {
    this.text = text;
}

public String getImage() {
    return image;
}

public void setImage(String image) {
    this.image = image;
}

@Override
public String toString() {
    return "Question [id=" + id + ", text=" + text + ", image=" + image
            + "]";
}




// Answer Choices class
class Choices {

    boolean isCorrect;
    String choice;

    public Choices(boolean isCorrect, String choice) {
        this.isCorrect = isCorrect;
        this.choice = choice;
    }

    public String getChoice() {
        return choice;
    }

    public boolean getIsCorrect() {
        return isCorrect;
    }

    @Override
    public String toString() {
        return "Choices [isCorrect=" + isCorrect + ", choice=" + choice
                + "]";
    }

}


public static final Parcelable.Creator<Question> CREATOR = new Parcelable.Creator<Question>() {

    @Override
    public Question createFromParcel(Parcel in) {
        return new Question(in);
    }

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

};

@Override
public int describeContents() {
    // TODO Auto-generated method stub
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {

    dest.writeString(id);
    dest.writeString(text);
    dest.writeString(image);
    dest.writeList(CHOICES);

}

private Question(Parcel in) {
    this.id = in.readString();
    this.text = in.readString();
    this.image = in.readString();
    this.CHOICES = in.readArrayList(Choices.class.getClassLoader());
}

}

Thanks for any help!

Kenny
  • 2,124
  • 3
  • 33
  • 63

4 Answers4

43

If you need to pass an ArrayList between activities, then I'd go with implementing Parcelable also, as there is no other way around I guess. However I don't think you will need that much of getters and setters. Here is your Question class which implements Parcelable:

public class Question implements Parcelable {
    public String id;
    public String text;
    public String image;
    public ArrayList<Choice> choices;


    /**
     * Constructs a Question from values
     */
    public Question (String id, String text, String image, ArrayList<Choice> choices) {
        this.id = id;
        this.text = text;
        this.image = image;
        this.choices = choices;
    }

    /**
     * Constructs a Question from a Parcel
     * @param parcel Source Parcel
     */
    public Question (Parcel parcel) {
        this.id = parcel.readString();
        this.text = parcel.readString();
        this.image = parcel.readString();
        this.choices = parcel.readArrayList(null);
    }

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

    // Required method to write to Parcel
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(id);
        dest.writeString(text);
        dest.writeString(image);
        dest.writeList(choices);
    }

    // Method to recreate a Question from a Parcel
    public static Creator<Question> CREATOR = new Creator<Question>() {

        @Override
        public Question createFromParcel(Parcel source) {
            return new Question(source);
        }

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

    };
}
Miro Markaravanes
  • 3,285
  • 25
  • 32
  • What about the ArrayList that I had, can I still have this class within my Question class? And put it in my parcelable? – Kenny Mar 17 '14 at 04:25
  • No. Similar to what is suggested in the other answer, you would have to make a separate `Choice` class and add it as a property to the `Question` class. (Just as `id` or `text`.) – Miro Markaravanes Mar 17 '14 at 04:29
  • Okay, so I'll move the Choice to it's separate class. So then you're saying that I can call the list from that class and add it to the parcelable? How exactly would I "add it as a property?" – Kenny Mar 17 '14 at 04:53
  • @Kenny: I edited the answer to include how to add the `Choice` list to the `Question` class. – Miro Markaravanes Mar 17 '14 at 05:02
  • dest.writeParcelable(choices, null); does give an error. Would I want to use `dest.writeList(choices);`? – Kenny Mar 17 '14 at 06:03
  • @Kenny: Sorry for the mistake. I have edited the code now to handle `ArrayLists`. Let me know if it doesn't work again. – Miro Markaravanes Mar 17 '14 at 06:13
  • 2
    Thanks @kenny. Just adding the following info. It worked for me when I added like "choices = parcel.readArrayList(Choice.class.getClassLoader());". the following didn't work for me "parcel.readArrayList(null)". – arango_86 Oct 23 '16 at 13:22
  • getting null array.when i set this ' this.choices = parcel.readArrayList(null);' – Arbaz.in Jun 21 '19 at 12:38
  • "parcel.readArrayList(null);" did not worked for me instead i have used "this.CHOICES = in.readArrayList(Choices.class.getClassLoader());" which is working fine. – Utkarsh Srivastava Feb 11 '20 at 13:35
5

Use:

in.createTypedArrayList(Product.CREATOR)

In the constructor that takes a Parable object as a param.

In the writeToParcel method use dest.writeTypedList(product);

Xantium
  • 11,201
  • 10
  • 62
  • 89
Derrick
  • 51
  • 1
  • 2
4

You have it almost, but not quite, right. The Question class looks nearly correctly Parcelable. The only thing that won't work is parcelling the array of Choices.

There are two ways that you could do it:

  1. Make Choices Parcelable. You will have to add all the required methods, and the CREATOR. Because Android knows how to parcel ArrayLists of Parcelables, this will work.
  2. Make parceling the Array of Choices part of parcelling the Question. To do this, you'd probably push the size of the array into the Parcel, and then loop over the Choices, pushing their values. On the other end, you'd read the count first, and then read the values for each Choice, creating each and pushing it into the new Question.
G. Blake Meike
  • 6,615
  • 3
  • 24
  • 40
  • Hi successfully used option 2 (storing array list sizes and iterating over during write/read from parcel), worked well especially as I used Inner static classes for array list objects, which had nested array list objects themselves. Quite a long class, but preferred keeping it all together! Would be interesting to see the performance difference between individual objects all using the Parcelable Interface vs one class iterating over array list objects and putting into the parcel. – Mark Jan 01 '16 at 18:36
3

Create a new java file for "Choices" and implement "Parcelable". If you do not implement parcelable you will get run-time exception (Unable to Marshal). So use the code below :

    public class Choices implements Parcelable{

        boolean isCorrect;
        String choice;

        public Choices(boolean isCorrect, String choice) {
            this.isCorrect = isCorrect;
            this.choice = choice;
        }
        //Create getters and setters 

        protected Choices(Parcel in) {
            isCorrect = in.readByte() != 0;
            choice = in.readString();
        }

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

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

        @Override
        public String toString() {
            return "Choices [isCorrect=" + isCorrect + ", choice=" + choice
                    + "]";
        }

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

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeByte((byte) (isCorrect ? 1 : 0));
            dest.writeString(choice);
        }
    }

As mentioned in above answer by @G.Blake you need to make Choices Parcelable and Android knows how to parcel ArrayLists of Parcelables

Hemant Shori
  • 2,463
  • 1
  • 22
  • 20