How to properly write Parcelable implementation for an Map<String, Object>? - android

I haven't found a proper answer on the stackoverflow on the same and confused on how to achieve the right Parcelable implementation for an Map .
I suppose for a Map< String , String > below is the correct implementation:
public void writeToParcel(Parcel out, int flags){
out.writeInt(map.size());
for(Map.Entry<String,String> entry : map.entrySet()){
out.writeString(entry.getKey());
out.writeString(entry.getValue());
}
}
private MyParcelable(Parcel in){
//initialize your map before
int size = in.readInt();
for(int i = 0; i < size; i++){
String key = in.readString();
String value = in.readString();
map.put(key,value);
}
}
But what about Map < String , Object > ?

You can't. Because you can't serialize an Object into a Parcel. If the Object in question implements Parcelable then you can do something like this:
public void writeToParcel(Parcel out, int flags){
out.writeInt(map.size());
for(Map.Entry<String,Parcelable> entry : map.entrySet()){
out.writeString(entry.getKey());
entry.getValue().writeToParcel(out, flags);
}
}
private MyParcelable(Parcel in){
//initialize your map before
int size = in.readInt();
for(int i = 0; i < size; i++){
String key = in.readString();
Parcelable value = in.readParcelable(getClass().getClassLoader());
map.put(key,value);
}
}
HOWEVER: You don't need to do this, because the Parcel class already knows how to serialize and unserialize Map , provided that the key is a String and the value is a "known" object type (includes Serializable and Parcelable). So you can just do this:
public void writeToParcel(Parcel out, int flags){
out.writeValue(map);
}
private MyParcelable(Parcel in){
map = (Map)in.readValue(getClass().getClassLoader());
}
See https://developer.android.com/reference/android/os/Parcel.html#writeValue(java.lang.Object) for a list of "known" object types.
NOTE: You will get always get a HashMap if you call Parcel.getValue() on a Map. Android is stupid about this and assumes all Maps are HashMaps.

Related

copyToRealm copies an empty list instead of a populated one

I have a RealmObject called "Encounter", which contains a RealmList of other RealmObjects called "SavedCombatant". In my code, I populate the RealmList with the appropriate objects, but when I commit the transaction and retrieve the Encounter-Object later, the RealmList is empty.
I have the following code
public void saveEncounter(){
//create a new key for the encounter
int key = 0;
if(mRealm.where(Encounter.class).count() != 0) {
RealmResults<Encounter> encounters = mRealm.where(Encounter.class).findAll();
Encounter encounter = encounters.last();
key = encounter != null ? encounter.getKey() + 1 : 0;
}
// retrieve the data to populate the realmlist with
// combatants has 1 element
List<SavedCombatant> combatants = mAdapter.getCombatants();
mRealm.beginTransaction();
Encounter e = mRealm.createObject(Encounter.class);
e.setKey(key);
e.setTitle(txtTitle.getText().toString());
RealmList<SavedCombatant> combatantRealmList = new RealmList<>();
for (int i = 0; i < combatants.size(); i++) {
combatantRealmList.add(combatants.get(i));
}
//combatantRealmList also has 1 element. setCombatants is a
//generated Setter with a couple bits of additional logic in it
e.setCombatants(combatantRealmList);
mRealm.copyToRealm(e);
mRealm.commitTransaction();
}
this would be my Encounter class
public class Encounter extends RealmObject {
private int key;
private String title;
private RealmList<SavedCombatant> combatants;
#Ignore
private String contents;
public void setCombatants(RealmList<SavedCombatant> combatants) {
//simple setter
this.combatants = combatants;
//generate summary of the elements in my realmlist. (probably inefficient as hell, but that's not part of the problem)
HashMap<String, Integer> countMap = new HashMap<>();
for (int i = 0; i < combatants.size(); ++i) {
String name = combatants.get(i).getName();
int countUp = 1;
if (countMap.containsKey(name)) {
countUp = countMap.get(name) + 1;
countMap.remove(name);
}
countMap.put(name, countUp);
}
contents = "";
Object[] keys = countMap.keySet().toArray();
for (int i = 0; i < keys.length; ++i) {
contents += countMap.get(keys[i]) + "x " + keys[i];
if (i + 1 < keys.length)
contents += "\r\n";
}
}
// here be more code, just a bunch of getters/setters
}
the class used for the RealmList has the following header (as to verify that I'm using a RealmObject here aswell)
public class SavedCombatant extends RealmObject
As it turns out, you need to explicitly save Objects inside a RealmList.
I needed to copy my SavedCombatant object to realm inside my for loop, using
mRealm.copyToRealm(combatants.get(i));

Assign Integer Value to String

I am developing an app in which i have to assign integer values to different string of words. For Example I want to assign:
John = 2
Good = 3
Person= 7
Now these John, Good and person are strings while 2,3 and 7 are int values. But I am so confused about how to implement that. I read many things about how to convert int to string and string to int but this is different case.
I am giving option to user to enter a text in editText and if for example User enters "Hello John you are a good person" then this line output will be 12 as all the three words John, Good and person are there in the input text. Can you tell me how to achieve that?
I am stuck here is my little code:
String s = "John";
int s_value= 2;
now I want to assign this 2 to John so that whenever user give input and it contains John then the value 2 is shown for John. Please Help as I am just a beginner level programmer
Here is my code (Edited)
String input = "John good person Man";
Map<String, Integer> map = new HashMap<>();
map.put("John", 2);
map.put("Good", 3);
map.put("Person", 7);
//int number = map.get("Good");
String[] words = input.split(" ");
ArrayList<String> wordsList = new ArrayList<String>();
for(String word : words)
{
wordsList.add(word);
}
for (int ii = 0; ii < wordsList.size(); ii++) {
// get the item as string
for (int j = 0; j < stopwords.length; j++) {
if (wordsList.contains(stopwords[j])) {
wordsList.remove(stopwords[j]);//remove it
}
}
}
for (String str : wordsList) {
Log.e("msg", str + " ");
}
As u see i applied code of you and then i want to split my main string so that each word of that string compares with the strings that are in the Map<>. Now i am confused what to write in the for loop ( 'stopwords' will be replaced by what thing?)
You can use a Map<String, Integer> to map words to numbers:
Map<String, Integer> map = new HashMap<>();
map.put("John", 2);
map.put("Good", 3);
map.put("Person", 7);
and then query the number given a word:
int number = map.get("John"); // will return 2
UPDATE
The following code iterates over a collection of words and adds up the values that the words match to:
List<String> words = getWords();
int total = 0;
for (String word : words) {
Integer value = map.get(word);
if (value != null) {
total += value;
}
}
return total;
I would use a Dictionary for this. You can add a string and an int (or anything else actually) value for that string.
Dictionary<string, int> d = new Dictionary<string, int>();
d.Add("John", 2);
d.Add("Good", 3);
d.Add("Person", 7);
You can use String contains to achieve this. Following is the code:
String input = "John you are a good person";
String s1 = "John";
String s2 = "good";
String s3 = "person";
int totScore =0;
if(input.contains(s1)) {
totScore=totScore+2;
}
else if (input.contains(s2)) {
totScore=totScore+3;
}
else if (input.contains(s3)) {
totScore=totScore+7;
}
System.out.print(totScore);
You can use class like.
class Word{
String wordName;
int value;
public Word(String wordName, int value){
this.wordName = wordName;
this.value = value;
}
// getter
public String getWordName(){
return this.wordName;
}
public int getValue(){
return this.value;
}
// setter
public void setWordName(String wordName){
this.wordName = wordName;
}
public void zetValue(int value){
this.value = value;
}
}
You can create an object of the word
Word person = new Word("Person",3);

ClassNotFoundException when unmarshalling list of parcelable

I'm trying to send an object between two activities.
The order Object contains a list of item witch I implemented like this:
OrderItem Object:
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeParcelable(priceTableItem, flags);
dest.writeInt(qunatity);
dest.writeDouble(value); // quantity * unitValue
dest.writeDouble(discount);
}
protected OrderItem(Parcel in) {
id = in.readInt();
priceTableItem = in.readParcelable(PriceTableItem.class.getClassLoader());
quantity = in.readInt();
value = in.readDouble();
discount = in.readDouble();
}
At the PriceTableItem I have a situation where it can contais a product id or a "grade" id ("grade" is when the product have color and size) but never have both values.
so I implemented like this:
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeParcelable(priceTable, flags);
dest.writeValue(produto);
dest.writeValue(grade);
dest.writeDouble(unitPrice);
dest.writeByte((byte) (isActive ? 1 : 0));
}
protected PriceTableItem(Parcel in) {
id = in.readInt();
priceTable = in.readParcelable(PriceTable.class.getClassLoader());
product = (Product) in.readValue(Product.class.getClassLoader());
grade = (Grade) in.readValue(Grade.class.getClassLoader());
unitPrice = in.readDouble();
isactive = in.readByte() != 0;
}
The problem occur when I pass the order object from my OrderListActivity to OrderDetailActivity. It read all attributes before my list of item. When it try to read the PriceTable on OrderItem I get:
java.lang.RuntimeException: Unable to start activity ComponentInfo{br.com.intelecto.intesigmobile/br.com.intelecto.intesigmobile.activity.PedidoDetailActivity}: android.os.BadParcelableException: ClassNotFoundException when unmarshalling:
The problem line is:
priceTableItem = in.readParcelable(PriceTableItem.class.getClassLoader());
Any ideas on how to solve this?
Still don't know what caused the error, but I solved the problem like bellow
When I was passing the order value I was using only the Intent to do it, like this:
Intent i = new Intent(this, OrderDetailActivity.class);
i.putExtras("order", order);
startActivity(i);
And, to read it I was doing like this:
Order order = getIntent().getParcelableExtra("order");
So, I used a Bundle for pass the value.
Intent i = new Intent(this, OrderDetailActivity.class);
Bundle b = new Bundle();
b.putParcelable("order", order);
i.putExtras(b);
startActivity(i);
And
Bundle b = getIntent().getExtras();
Order order = b.getParcelable("order");
I had same error and the lines causing the error were these:
ingredients = in.readParcelable(Ingredient.class.getClassLoader());
and
public static final Parcelable.Creator<RecipeContent> CREATOR = new Parcelable.Creator<RecipeContent>() {
#Override
public RecipeContent createFromParcel(Parcel in) {
return new RecipeContent(in);
}
So, what I did and it helped was to arrange the in.read items in the same way as the values in the Parcel are arranged.
Here are the details:
public RecipeContent(int id, String recipeName, List<Ingredient> ingredients,
List<BakingStep> bakingSteps, String recipeImage) {
this.id = id;
this.recipeName = recipeName;
this.ingredients = ingredients;
this.bakingSteps = bakingSteps;
this.recipeImage = recipeImage;
}
And then using the same order for the in.read:
public RecipeContent(Parcel in) {
id = in.readInt();
recipeName = in.readString();
ingredients = in.readParcelable(Ingredient.class.getClassLoader());
bakingSteps = in.readParcelable(BakingStep.class.getClassLoader());
recipeImage = in.readString();
}
Hope this helps for you too.

sending a customized object to intent android

I want to pass an object of type Annonce to an Intent. As you can see, it's simple if the class attributes are primitives, However; in my case I have an image (Bitmap) and an attribute of type Client ( I have created a Client class).
My solution is to access the Client attributes (using getter and setter) and parsing it in the writeToParcel method one by one (it takes too much time), and for the image, I am sending it in the mainActivity using ByteArrayOutputStream. Can anyone help me do it all in Annonce class.
public class Annonce implements Parcelable {
String article, desc, temps, ville, categorie;
int prix;
Bitmap img;
Client c;
public Annonce(String article, String desc, String temps, String ville,
String categorie, int prix, Bitmap img, Client c) {
this.article = article;
this.desc = desc;
this.c = c;
this.prix = prix;
this.img = img;
this.temps = temps;
this.categorie = categorie;
this.ville = ville;
}
public static final Parcelable.Creator<Annonce> CREATOR = new Parcelable.Creator<Annonce>() {
public Annonce createFromParcel(Parcel source) {
return new Annonce(source);
}
public Annonce[] newArray(int size) {
return new Annonce[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(article);
parcel.writeString(desc);
parcel.writeString(temps);
parcel.writeString(ville);
parcel.writeString(categorie);
parcel.writeInt(prix);
}
public Annonce(Parcel source) {
article = source.readString();
desc = source.readString();
temps = source.readString();
ville = source.readString();
categorie = source.readString();
prix = source.readInt();
}
}
Having an attribut of type "bitmap" is not a good solution . Instead of that , we can use the path of the image to refer to the bitmap image .
Also, we can convert the Client into object in parcelable in order to send it through intent.

Android Parcelable Issue

I had to pass a dataobject from one activity to another. The best way to do this is uing Parcelable.
The dataobject had some fields with setter and getter methods. After setting some fields and passing the object to another activity, wha tI observed is that the field values got interchanged to other field values.
The order of fields for writing to parcel and reading from parcel is the same.
public void writeToParcel(Parcel out, int flags) {
out.writeInt(id);
out.writeString(appNo);
out.writeString(this.policyNo);
out.writeInt((int)this.AppRcptDt.getTime());
out.writeString(this.currentStatus);
out.writeString(this.productCd);
out.writeDouble(this.sumAssured);
out.writeDouble(this.modalPremium);
out.writeDouble(this.annualPremium);
out.writeString(this.paymentMode);
out.writeString(this.branchCd);
out.writeString(this.branchName);
out.writeString(this.insuredName);
out.writeString(this.auraStatus);
out.writeString(this.ownerName);
out.writeString(this.agentCd);
out.writeString(this.billingMode);
}
private ApplicationTrackerDO(Parcel in) {
id=in.readInt();
this.appNo = in.readString();
this.policyNo = in.readString();
this.AppRcptDt = new Date(in.readLong());
this.currentStatus = in.readString();
this.productCd = in.readString();
this.sumAssured = in.readDouble();
this.modalPremium = in.readDouble();
this.annualPremium = in.readDouble();
this.paymentMode = in.readString();
this.branchCd = in.readString();
this.branchName = in.readString();
this.insuredName = in.readString();
this.auraStatus = in.readString();
this.ownerName = in.readString();
this.agentCd = in.readString();
this.billingMode = in.readString();
}
It is not the order but the data type that is not the same, from the first 4 lines you write int, string, string, int then you read int, string, string, long. I didn't check any further, you must match both order and datatype of read and write operations.
You are writing int
out.writeInt((int)this.AppRcptDt.getTime());
But reading long
this.AppRcptDt = new Date(in.readLong());
my solution to provide an date object with null
#Override
public void writeToParcel ( Parcel dest, int flags ) {
dest.writeInt((birthday != null) ? 1 : 0); // is birthday set?
dest.writeLong((birthday != null) ? birthday.getTime() : 0);
}
public void readFromParcel ( Parcel in ) {
if(in.readInt() == 1) {
birthday = new Date(in.readLong());
} else {
in.readLong(); // ignore stored value
birthday = null;
}
}
java.util.Date implements Serializable:
public class Date implements Serializable, Cloneable, Comparable<Date> {
private static final long serialVersionUID = 7523967970034938905L;
// Used by parse()
private static final int CREATION_YEAR = new Date().getYear();
private transient long milliseconds;
Use:
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeSerializable(myDateField);
}
private MyClass(Parcel in) {
myDateField = (Date)in.readSerializable();
}

Categories

Resources