Passing ArreyList between Fragments with Parcelable - android

After consuming a json with Retrofit and using unsynchronous callback I can't pass An ArrayList from The MainActivity to a fragment .
Code from MainActivity:
lFragmentManager = getFragmentManager();
lFragment = lFragmentManager.findFragmentById(R.id.frame_container);
lFragment = new Fragment_Categories();
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("list_categories", Categories_.getCategories());
//for(int a = 0 ; a < Categories_.getCategories().size(); a++)
// Log.d("billy",Categories_.getCategories().get(a).getTitle());
lFragment.setArguments(bundle);
lFragmentManager.beginTransaction().replace(R.id.frame_container ,lFragment ).commit();
Note that the Log inside comments does print the context of the list, so I take the ArrayList in the fragment with this code inside onCreateView:
if(savedInstanceState != null)
categories = savedInstanceState.getParcelableArrayList("list_categories");
/*
* initialize the Recycler view
*/
mRecycler = (RecyclerView)view.findViewById(R.id.categories_list);
mAdapter = new AdapterCategories(categories,getActivity());
mRecycler.setAdapter(mAdapter);
Here is my Class Category :
public class Categories implements Parcelable{
private ArrayList<NavDrawerItem> categories;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(categories);
}
public static final Parcelable.Creator<Categories> CREATOR = new Parcelable.Creator<Categories>() {
public Categories createFromParcel(Parcel in) {
return new Categories(in);
}
public Categories[] newArray(int size){
return new Categories[size];
}
};
private Categories(Parcel in){
categories = in.createTypedArrayList(NavDrawerItem.CREATOR);
}
public ArrayList<NavDrawerItem> getCategories() {
return categories;
}
}
And here is the Class NavDrawerItem:
public class NavDrawerItem implements Parcelable {
private String title;
private String description;
private String image;
private String post_count;
private String id;
private String slug;
private int parent;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(title);
dest.writeString(description);
dest.writeString(image);
dest.writeString(post_count);
dest.writeString(id);
dest.writeString(slug);
dest.writeInt(parent);
}
public static final Parcelable.Creator<NavDrawerItem> CREATOR = new Parcelable.Creator<NavDrawerItem>() {
public NavDrawerItem createFromParcel(Parcel in) {
return new NavDrawerItem(in);
}
public NavDrawerItem[] newArray(int size){
return new NavDrawerItem[size];
}
};
private NavDrawerItem(Parcel in){
title = in.readString();
description = in.readString();
image = in.readString();
post_count = in.readString();
id = in.readString();
slug = in.readString();
parent = in.readInt();
}
The problem is that inside fragment (when trying to pass the list to the Adapter of the RecyclerView) or inside the adapter of the RecyclerView I take a null exception :
java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.ArrayList android.os.Bundle.getParcelableArrayList(java.lang.String)' on a null object reference
Thank you for the help!!

You wouldn't use the savedInstanceState to get your arguments, because they weren't passed as part of a saveInstanceState() operation. You want to use getArguments() instead. So try this...
categories = getArguments().getParcelableArrayList("list_categories");

Related

Pass array in Intent using parcelable

I would like to send an array of objects between activities. I want to use the parcelable interface and send the data in an intent. However I keep getting errors. I have been stuck for 2 days. Here are some details about my problem.
Class A
private ProjetUI[] mProjects;
private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
Context context = view.getContext();
Intent intent = new Intent(context, ProjetListActivity.class);
intent.putExtra(ProjetListActivity.ARG_PROJECTS, mProjects);
context.startActivity(intent);
}
};
Class B
ProjetUI[] mProjects = getIntent().getParcelableArrayExtra(ARG_PROJECTS);
I get a compilation error "Incompatible types"
After casting to (ProjetUI[]), I get a runtime error "Cannot cast Parcelable[] to ProjetUI[]"
Class Projet
public class ProjetUI implements Parcelable {
private String id;
private String idParent;
private String nom;
private String description;
private List<ProjetColonneUI> colonnes;
private List<VueUI> vues;
private boolean archive;
private String version;
private String commentaire;
private boolean published;
private List<DroitAccesUI> droitAcces;
private String idDossier;
private String typeDossier;
private String idModele;
private List<ProjetDatasetUI> projetDatasets;
protected ProjetUI(Parcel in) {
id = in.readString();
idParent = in.readString();
nom = in.readString();
description = in.readString();
colonnes = in.createTypedArrayList(ProjetColonneUI.CREATOR);
vues = in.createTypedArrayList(VueUI.CREATOR);
archive = in.readInt() == 1;
version = in.readString();
commentaire = in.readString();
published = in.readInt() == 1;
droitAcces = in.createTypedArrayList(DroitAccesUI.CREATOR);
idDossier = in.readString();
typeDossier = in.readString();
idModele = in.readString();
projetDatasets = in.createTypedArrayList(ProjetDatasetUI.CREATOR);
}
public static final Creator<ProjetUI> CREATOR = new Creator<ProjetUI>() {
#Override
public ProjetUI createFromParcel(Parcel in) {
return new ProjetUI(in);
}
#Override
public ProjetUI[] newArray(int size) {
return new ProjetUI[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(getId());
parcel.writeString(getIdParent());
parcel.writeString(getNom());
parcel.writeString(getDescription());
parcel.writeTypedList(getColonnes());
parcel.writeTypedList(getVues());
parcel.writeInt(isArchive() ? 1 : 0);
parcel.writeString(getVersion());
parcel.writeString(getCommentaire());
parcel.writeInt(isPublished() ? 1 : 0);
parcel.writeTypedList(getDroitAcces());
parcel.writeString(getIdDossier());
parcel.writeString(getTypeDossier());
parcel.writeString(getIdModele());
parcel.writeTypedList(getProjetDatasets());
}
}
EDIT
This is the complete stacktrace
The other classes implement parcelable just like ProjeUI class.
Here is an example of another class that has an enum type and an example of an enum that implements parcelable
public class VueRelationUI implements Parcelable {
private String id;
private String idVue;
private String idRelation;
private RelationType typeRelation;
protected VueRelationUI(Parcel in) {
id = in.readString();
idVue = in.readString();
idRelation = in.readString();
typeRelation = in.readParcelable(RelationType.class.getClassLoader());
}
public static final Creator<VueRelationUI> CREATOR = new Creator<VueRelationUI>() {
#Override
public VueRelationUI createFromParcel(Parcel in) {
return new VueRelationUI(in);
}
#Override
public VueRelationUI[] newArray(int size) {
return new VueRelationUI[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(getId());
parcel.writeString(getIdVue());
parcel.writeString(getIdRelation());
parcel.writeParcelable(getTypeRelation(), flags);
}
}
ENUM
public enum RelationType implements Parcelable {
INNER,
OUTER;
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(ordinal());
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<RelationType> CREATOR = new Creator<RelationType>() {
#Override
public RelationType createFromParcel(Parcel parcel) {
return RelationType.values()[parcel.readInt()];
}
#Override
public RelationType[] newArray(int size) {
return new RelationType[size];
}
};
}
Any help would be much appreciated
The problem happens because of the internal implementation of Android's Parcel class. When you start the new activity, all of the intent extras are parceled and then unparceled. When this happens, the Android framework allocates a new Parcelable[], and not a new ProjetUI[]. So you get a ClassCastException when you try to cast it.
Probably the best solution would be to change your code to use ArrayList<ProjetUI> instead of ProjetUI[]. Then you can use Intent.putParcelableArrayListExtra() and getParcelableArrayListExtra() without any problems.
If you can't do that for some reason, then you will have to manually cast the array one element at a time:
Parcelable[] parcelables = getIntent().getParcelableArrayExtra(ARG_PROJECTS);
ProjetUI[] mProjects = new ProjetUI[parcelables.length];
for (int i = 0; i < parcelables.length; ++i) {
mProjects[i] = (ProjetUI) parcelables[i];
}

Parcelable with List only deserialises first element

I have a Parcelable object that has a list of Parcelable objects. I am trying to read that list back after it has been passed from one Activity to the next, but only the first element is "un-bundled"
public class MyBundle implements Parcelable {
private List<Data> dataList;
public static final Parcelable.Creator<MyBundle> CREATOR = new Parcelable.Creator<MyBundle>() {
public MyBundle createFromParcel(Parcel in) {
return new MyBundle(in);
}
public MyBundle[] newArray(int size) {
return new MyBundle[size];
}
};
public MyBundle() {
}
public MyBundle(Parcel in) {
//dataList = new ArrayList<>();
//in.readTypedList(dataList, Data.CREATOR);
dataList = in.createTypedArrayList(Data.CREATOR);
//BOTH have the same result
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if (dataList != null && dataList.size() > 0) {
dest.writeTypedList(dataList);
}
}
}
The data object:
/*BaseObject has the following properties:
UUID uuid;
long databaseId;
createdDate;
modifiedDate;
*/
public class Data extends BaseObject implements Parcelable {
private String name;
private String serial;
private String location;
public Data() {}
private Data(Parcel in) {
String uuidString = in.readString();
if (uuidString == null) return; //this is null!
uuid = UUID.fromString(idString);
databaseId = in.readLong();
createdDate = new Date(in.readLong());
modifiedDate = new Date(in.readLong());
location = in.readString();
name = in.readString();
serial = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(uuid.toString());
dest.writeLong(databaseId);
dest.writeLong(createdDate.getTime());
dest.writeLong(modifiedDate.getTime());
dest.writeString(name);
dest.writeString(serial);
}
public static final Parcelable.Creator<Data> CREATOR
= new Parcelable.Creator<Data>() {
public Data createFromParcel(Parcel in) {
return new Data(in);
}
public Data[] newArray(int size) {
return new Data[size];
}
};
}
What I have tried:
Debugging - I can see the first element is read fine but the rest are return null, and they do have values when they are being written
"Android, How to use readTypedList method correctly in a Parcelable class?"
"how to properly implement Parcelable with an ArrayList?"
So this is the answer: My Data parcelable misses the location element when it creates the parcel. This obviously results in some kind of offset error when READING occurs. So the coded solution is as follows:
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(uuid.toString());
dest.writeLong(databaseId);
dest.writeLong(createdDate.getTime());
dest.writeLong(modifiedDate.getTime());
dest.writeString(location); /*HERE!*/
dest.writeString(name);
dest.writeString(serial);
}
I hope this helps someone else.

Pass parcelable object containing Map between Activity

My class ExpenseFB, which implements Parcelable, contains a Map of UserFB (which implements Parcelable too):
ExpenseFB:
public class ExpenseFB implements Parcelable {
private String id;
private String name;
private String description;
private String whopaidID;
private String whopaidName;
private Double amount;
private Map<String, UserFB> partecipants;
// setters and getters...
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(name);
dest.writeString(description);
dest.writeString(whopaidID);
dest.writeString(whopaidName);
dest.writeMap(partecipants);
}
protected ExpenseFB(Parcel in) {
id = in.readString();
name = in.readString();
description = in.readString();
whopaidID = in.readString();
whopaidName = in.readString();
in.readMap(partecipants,UserFB.class.getClassLoader());
}
public static final Creator<ExpenseFB> CREATOR = new Creator<ExpenseFB>() {
#Override
public ExpenseFB createFromParcel(Parcel in) {
return new ExpenseFB(in);
}
#Override
public ExpenseFB[] newArray(int size) {
return new ExpenseFB[size];
}
};
}
UserFB:
public class UserFB implements Parcelable{
private String id;
private String name;
private String email;
private Map<String, GroupFB> groups;
private Map<String, UserFB> friends;
// setters and getters
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(name);
dest.writeString(email);
dest.writeMap(groups);
dest.writeMap(friends);
}
protected UserFB(Parcel in) {
id = in.readString();
name = in.readString();
email = in.readString();
in.readMap(groups,GroupFB.class.getClassLoader());
in.readMap(friends,UserFB.class.getClassLoader());
}
public static final Creator<UserFB> CREATOR = new Creator<UserFB>() {
#Override
public UserFB createFromParcel(Parcel in) {
return new UserFB(in);
}
#Override
public UserFB[] newArray(int size) {
return new UserFB[size];
}
};
}
I want to pass an ExpenseFB object between two Activities by adding the object
ExpenseFB to the intent:
intent.putExtra("id", expenseFB);
When, in debug mode, I execute getIntent().getParcelableExtra("id") in the second activity it raises the following exception when tries to do the readMap() method on the partecipants map:
... Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.Map.put(java.lang.Object, java.lang.Object)' on a null object reference
I see that the partecipants map in the first activity is filled: I think that the problem is in the writeMap() method.
Does exist a standard or better way to pass a Parcelable object containing a Map?
Have I to call another method to parcel the Map?
I don't want to use Serializable object because I read that they make worse performances.
The problem is that readMap() is used to read data from a Parcel into and existing Map. You haven't created the Map before calling readMap(), so you get the NullPointerException.
You can solve this by initializing the map when you declare it:
private Map<String, GroupFB> groups = new HashMap<String, GroupFB>();
private Map<String, UserFB> friends = new HashMap<String, UserFB>();
Or, you can create the empty Map in the UserFB constructor, like this:
protected UserFB(Parcel in) {
id = in.readString();
name = in.readString();
email = in.readString();
groups = new HashMap<String, GroupFB>();
in.readMap(groups,GroupFB.class.getClassLoader());
friends = new HashMap<String, UserFB>()
in.readMap(friends,UserFB.class.getClassLoader());
}
You got the point but I think you need to know how to write Map<> into parcelable
Pasting writeParcel() method
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.groups.size());
for (Map.Entry<String, GroupFb> entry : this.groups.entrySet()) {
dest.writeString(entry.getKey());
dest.writeParcelable(entry.getValue(), flags);
}
}
protected UserFB (Parcel in) {
int groupsSize = in.readInt();
this.groups = new HashMap<String, GroupFb>(groupsSize);
for (int i = 0; i < groupsSize; i++) {
String key = in.readString();
GroupFb value = in.readParcelable(GroupFb.class.getClassLoader());
this.groups.put(key, value);
}
}
Do the same for another Map<> too.

How to get Parcable Bundle from Activity in a Fragment

I have written following code in my Activity Class,
ArrayList<TXData> data;
data= Map.getDataList();
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("data",data);
Fragment fragment = new MapsFragment();
fragment.setArguments(bundle);
In MapsFragment, I have written following code to get data on onActivityCreated() Method.
ArrayList<TXData> dataList;
dataList=getArguments().getParcelableArrayList("data");
It returns following exception on Fragment class.
Caused by: java.lang.NullPointerException: Attempt to invoke virtual
method java.util.ArrayList
android.os.Bundle.getParcelableArrayList(java.lang.String) on a null
object reference
I added debug points, to find out the cause of problem, It successfully adds the data to Bundle from Activity, but its not getting any data in Fragment.
Kindly guide me what I am doing wrong here.
EDIT
Parceable Class
public class TXData implements Serializable, Parcelable{
public String id;
public String title;
public String pId = "";
public float heading;
public float pitch;
public int totalCount;
int mData;
public TXSData(){
}
protected TXData(Parcel in) {
id = in.readString();
title = in.readString();
pId = in.readString();
heading = in.readFloat();
pitch = in.readFloat();
totalCount = in.readInt();
mData = in.readInt();
minPrice = in.readInt();
maxPrice = in.readInt();
isInFilter = in.readByte() != 0;
isEnabled = in.readByte() != 0;
totalCount = in.readInt();
}
public static final Creator<TXData> CREATOR = new Creator<TXData>() {
#Override
public TXData createFromParcel(Parcel in) {
return new TXData(in);
}
#Override
public TXData[] newArray(int size) {
return new TXData[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mData);
}
The easy solution would be to drop the Parcelable and stay with Serializable.
public class TXData implements Serializable{
public String id;
public String title;
public String pId = "";
public float heading;
public float pitch;
public int totalCount;
int mData;
}
and use putExtra/getSerializableExtera instead of getParcelableArrayList/setParcelableArrayList
For big amounts of data, it is recommended to use Parcelable which is built for speed, see this post or similar...
Replace below class.
public class TXData implements Parcelable {
public String id;
public String title;
public String pId = "";
public float heading;
public float pitch;
public int totalCount;
int mData;
public TXSData(){
}
protected TXData(Parcel in) {
id = in.readString();
title = in.readString();
pId = in.readString();
heading = in.readFloat();
pitch = in.readFloat();
totalCount = in.readInt();
mData = in.readInt();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(title);
dest.writeString(pId);
dest.writeFloat(heading);
dest.writeFloat(pitch);
dest.writeInt(totalCount);
dest.writeInt(mData);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<TXData> CREATOR = new Parcelable.Creator<TXData>() {
#Override
public TXData createFromParcel(Parcel in) {
return new TXData(in);
}
#Override
public TXData[] newArray(int size) {
return new TXData[size];
}
};
}
Can you try that.
ArrayList<TXData> dataList = new ArrayList<>();
dataList.addAll(getArguments().getParcelableArrayList("data"));
Your writeToParcel method is incomplete.
Your writeToParcel should be writing all the properties in the same order as it is in the parameterized contructor: protected TXSectionData(Parcel in)
Check here: tutorial
One very important thing to pay close attention to is the order that you write and read your values to and from the Parcel. They need to match up in both cases. The mechanism that Android uses to read the Parcel is blind and completely trusts you to get the order correct, or else you will run into run-time crashes.
Try getting the Bundle object (getArguments()) in onCreate() or onCreateView()
of the fragment and not onActivityCreated().
Refer to the example from Fragment documentation.

readParcelable returns null

I try to pass a News class to an activity, so I implemented the Parcelable interface. Inside News I have two classes implementing Parcelable too, Image and Date
The matter is that my News object at the end contains null for fields image and date.
Here my code:
News.java
public class News implements Parcelable {
public static final String TAG = "model_news";
private JSONObject object;
private int id;
private String type;
private String title;
private Boolean comment_disabled;
private String category_name;
private String url;
private Image images;
private Date date;
private Boolean is_video;
public News(JSONObject object) {
this.object = object;
try {
id = Integer.parseInt(object.getString("id"));
type = object.getString("type");
title = object.getString("title");
comment_disabled = object.getBoolean("comment_disabled");
category_name = object.getString("category_name");
url = object.getString("url");
if (!object.isNull("images")) {
images = new Image(object.getJSONObject("images"));
}
date = new Date(object.getJSONObject("date"));
is_video = object.getBoolean("is_video");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
}
}
protected News(Parcel in) {
id = in.readInt();
type = in.readString();
title = in.readString();
category_name = in.readString();
url = in.readString();
images = (Image) in.readParcelable(Image.class.getClassLoader());
date = (Date) in.readParcelable(Date.class.getClassLoader());
is_video = in.readByte() != 0;
comment_disabled = in.readByte() != 0;
}
public static final Creator<News> CREATOR = new Creator<News>() {
#Override
public News createFromParcel(Parcel in) {
return new News(in);
}
#Override
public News[] newArray(int size) {
return new News[size];
}
};
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(type);
dest.writeString(title);
dest.writeByte((byte) (comment_disabled ? 1 : 0));
dest.writeString(category_name);
dest.writeString(url);
dest.writeParcelable(images, flags);
dest.writeParcelable(date, flags);
dest.writeByte((byte) (is_video ? 1 : 0));
}
#Override
public int describeContents() {
return 0;
}
}
Image.java
public class Image implements Parcelable {
public static final String TAG = "model_image";
private JSONObject imageObj;
private JSONObject original;
private String source;
private int width;
private Drawable image;
public Image(JSONObject imageObj) {
this.imageObj = imageObj;
try {
original = this.imageObj.getJSONObject("original");
source = original.getString("src");
width = original.getInt("width");
} catch (JSONException e) {
e.getMessage();
}
}
protected Image(Parcel in) {
source = in.readString();
width = in.readInt();
}
public static final Creator<Image> CREATOR = new Creator<Image>() {
#Override
public Image createFromParcel(Parcel in) {
return new Image(in);
}
#Override
public Image[] newArray(int size) {
return new Image[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(source);
dest.writeInt(width);
}
}
what I'm doing wrong ?
There is mistake in the Parcelable implementation.
First of all parcelable implementation states that: the fields passed in News(Parcel in) Constructor should be written in the same sequence in writeToParcel() method. Thats called Marshalling and Unmarshalling.
Corrections:
Drawable cannot be passed a parameter in Parcelable.
News Parcelable implementation.
Missed some of the fields its just for your understanding.
public class News implements Parcelable {
public static final String TAG = "model_news";
private JSONObject object;
private int id;
private String type;
private String title;
private Boolean comment_disabled;
private String category_name;
private String url;
private Image images;
private Date date;
private Boolean is_video;
protected News(Parcel in) {
id = in.readInt();
type = in.readString();
title = in.readString();
category_name = in.readString();
url = in.readString();
}
public static final Creator<News> CREATOR = new Creator<News>() {
#Override
public News createFromParcel(Parcel in) {
return new News(in);
}
#Override
public News[] newArray(int size) {
return new News[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(type);
dest.writeString(title);
dest.writeString(category_name);
dest.writeString(url);
}
}
public class Image implements Parcelable {
public static final String TAG = "model_image";
private JSONObject imageObj;
private JSONObject original;
private String source;
private int width;
private Drawable image;
protected Image(Parcel in) {
source = in.readString();
width = in.readInt();
}
public static final Creator<Image> CREATOR = new Creator<Image>() {
#Override
public Image createFromParcel(Parcel in) {
return new Image(in);
}
#Override
public Image[] newArray(int size) {
return new Image[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(source);
dest.writeInt(width);
}
}

Categories

Resources