Overriding View.onSaveInstanceState() and View.onRestoreInstanceState() using View.BaseSavedState? - android

Assume you want to derive your own View class from an existing View implementation, adding a bit of value, hence maintaining a few variables which represent your View's state in a meaningful way.
It would be nice if your View would save its state automatically just like others do (if an ID is assigned) so you would want to override onRestoreInstanceState() and onSaveInstanceState().
Of course, you need to call the respective methods of your base class, and you need to combine your state information with that of your base class.
Obviously, the only safe way to do so is to wrap your super class' Parcelable in an own Parcelable such that the keys won't get mixed up.
Now there's View.BaseSavedState and its interesting getSuperState() method but I somehow fail to understand how this really adds value to just storing the base class' Parcelable in a Bundle along with the derived View's state values and return that. On the other hand, maybe some other system component will expect all InstanceState information to be of type View.AbsSavedState (e.g. such that getSuperState() can be called)?
Any experiences you're willing to share?

To complement James Chen's answer, here is a full example of how to use this method, based on blog article by Charles Harley.
Code from the link:
public class LockCombinationPicker extends LinearLayout {
private NumberPicker numberPicker1;
private NumberPicker numberPicker2;
private NumberPicker numberPicker3;
public LockCombinationPicker(Context context) {
this(context, null);
}
public LockCombinationPicker(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LockCombinationPicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
loadViews();
}
private void loadViews() {
LayoutInflater.from(getContext()).inflate(R.layout.lock_combination_picker, this, true);
numberPicker1 = (NumberPicker) findViewById(R.id.number1);
numberPicker1.setMinValue(0);
numberPicker1.setMaxValue(10);
numberPicker2 = (NumberPicker) findViewById(R.id.number2);
numberPicker2.setMinValue(0);
numberPicker2.setMaxValue(10);
numberPicker3 = (NumberPicker) findViewById(R.id.number3);
numberPicker3.setMinValue(0);
numberPicker3.setMaxValue(10);
}
#Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
return new SavedState(superState, numberPicker1.getValue(), numberPicker2.getValue(), numberPicker3.getValue());
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
numberPicker1.setValue(savedState.getNumber1());
numberPicker2.setValue(savedState.getNumber2());
numberPicker3.setValue(savedState.getNumber3());
}
#Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
// As we save our own instance state, ensure our children don't save and restore their state as well.
super.dispatchFreezeSelfOnly(container);
}
#Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
/** See comment in {#link #dispatchSaveInstanceState(android.util.SparseArray)} */
super.dispatchThawSelfOnly(container);
}
/**
* Convenience class to save / restore the lock combination picker state. Looks clumsy but once created is easy to maintain and use.
*/
protected static class SavedState extends BaseSavedState {
private final int number1;
private final int number2;
private final int number3;
private SavedState(Parcelable superState, int number1, int number2, int number3) {
super(superState);
this.number1 = number1;
this.number2 = number2;
this.number3 = number3;
}
private SavedState(Parcel in) {
super(in);
number1 = in.readInt();
number2 = in.readInt();
number3 = in.readInt();
}
public int getNumber1() {
return number1;
}
public int getNumber2() {
return number2;
}
public int getNumber3() {
return number3;
}
#Override
public void writeToParcel(Parcel destination, int flags) {
super.writeToParcel(destination, flags);
destination.writeInt(number1);
destination.writeInt(number2);
destination.writeInt(number3);
}
public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}

I think the design needs us, and as the name implies, to implement a subclass of View.BaseSavedState to store values by overriding Parcelable's interface.
TextView.SavedState is a good example
public static class SavedState extends BaseSavedState {
int selStart;
int selEnd;
CharSequence text;
boolean frozenWithFocus;
CharSequence error;
SavedState(Parcelable superState) {
super(superState);
}
#Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(selStart);
out.writeInt(selEnd);
out.writeInt(frozenWithFocus ? 1 : 0);
TextUtils.writeToParcel(text, out, flags);
if (error == null) {
out.writeInt(0);
} else {
out.writeInt(1);
TextUtils.writeToParcel(error, out, flags);
}
}
#Override
public String toString() {
String str = "TextView.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " start=" + selStart + " end=" + selEnd;
if (text != null) {
str += " text=" + text;
}
return str + "}";
}
#SuppressWarnings("hiding")
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
private SavedState(Parcel in) {
super(in);
selStart = in.readInt();
selEnd = in.readInt();
frozenWithFocus = (in.readInt() != 0);
text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
if (in.readInt() != 0) {
error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
}
}
}

Related

Extending MaterialButton: Should it be done? SavedState has package visibility

I have extended the MaterialButton to display a consistent loading state on the button when clicked however I ran into a minor issue when implementing SavedState restoration.
The progress button variant of this I have written, disables the view, displays an animated loading spinner, optionally displays loading text and restores then restores the previously displayed text/state when the view is enabled.
MaterialButton.SavedState is package-private so cannot be extended externally. This does not create a problem with my implementation as I don't use the 'checked' field it extends but it does raise these questions:
TLDR
Is it wrong to extend this MaterialButton?
If not would it be appropriate to submit a PR to the public repo to make SavedState public?
material-components-android:1.2.0
Is it wrong to extend this MaterialButton?
The MaterialButton is not a final class then you can extend it, and for example the ExtendedFloatingActionButton extends it.
If not would it be appropriate to submit a PR to the public repo to make SavedState public?
You don't need to make MaterialButton.SavedState public. You can do something like:
public class MyButton extends MaterialButton
{
private String text;
//....
static class SavedState extends AbsSavedState {
#Nullable CharSequence myText;
SavedState(Parcelable superState) {
super(superState);
}
SavedState(#NonNull Parcel source, ClassLoader loader) {
super(source, loader);
myText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
}
#Override
public void writeToParcel(#NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
TextUtils.writeToParcel(myText, dest, flags);
}
#NonNull
#Override
public String toString() {
return "MyButton.SavedState{"
+ " text="
+ myText
+ "}";
}
public static final Creator<SavedState> CREATOR =
new ClassLoaderCreator<SavedState>() {
#NonNull
#Override
public SavedState createFromParcel(#NonNull Parcel in, ClassLoader loader) {
return new SavedState(in, loader);
}
#Nullable
#Override
public SavedState createFromParcel(#NonNull Parcel in) {
return new SavedState(in, null);
}
#NonNull
#Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
#Nullable
#Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.myText = text;
return ss;
}
#Override
public void onRestoreInstanceState(#Nullable Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
text = ss.myText.toString();
}
}
In this way the MyButton.SavedState will contain also the MaterialButton.SaveState without extending it.

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.

Save textview attributes when orientation changed

I need to save the textview attributes such as text,color,background color, padding values in onSaveInstanceState() -
The textview time is in bindView() in Adapter class -
viewHolder.time.setText(strText);
viewHolder.time.setTextColor(0xff000000);
viewHolder.time.setTextSize(17);
viewHolder.time.setVisibility(View.VISIBLE);
viewHolder.time.setBackgroundColor(nColor);
viewHolder.time.setPadding(25,25,25,25);
How do I save them in onSaveInstanceState() and use it in onCreate() when orientation changed to landscape.
I dont want to use android:configChanges since I have different layout for landscape.
EDIT :
In MyAdapter.java,
#Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder viewHolder = (ViewHolder) view.getTag();
Child child = children.createStopFromCursor(cursor);
MyFragment ndf = new MyFragment();
viewHolder.time.setText(strText);
viewHolder.time.setTextColor(0xff000000);
viewHolder.time.setTextSize(17);
viewHolder.time.setVisibility(View.VISIBLE);
viewHolder.time.setBackgroundColor(nColor);
viewHolder.time.setPadding(25,25,25,25);
ndf.setLandScape(strText,0xff000000,17,nColor);
view.invalidate();
}
}
In MyFragment.java,
public void setLandScape(String time,int time_color,int time_size,int time_Bcolor){
this.delay_time = time;
this.delay_time_color = time_color;
this.delay_time_size = time_size;
this.delay_time_BColor = time_Bcolor;
}
#Override
public void onSaveInstanceState(Bundle outState) {
int[] padding = {25,25,25,25};
TextViewLandscape attributes = new TextViewLandscape(delay_time, delay_time_color, delay_time_size, delay_time_BColor, padding);
ArrayList<TextViewLandscape> list = new ArrayList<TextViewLandscape>();
list.add(attributes);
outState.putParcelableArrayList("keyTextViewAttributes", list);
super.onSaveInstanceState(outState);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mFromSavedInstanceState = true;
TextViewLandscape textViewAttributes = savedInstanceState.getParcelable("keyTextViewAttributes");
}}
In TextViewLandscape.java,
public class TextViewLandscape implements Parcelable {
private String text;
private int textColor;
private int textSize;
private int backgroundColor;
private int[] paddingAttrs = {4};
public TextViewLandscape(String text, int textColor, int textSize, int backgroundColor, int[] paddingAttrs) {
this.text = text;
this.textColor = textColor;
this.textSize = textSize;
this.backgroundColor = backgroundColor;
this.paddingAttrs = paddingAttrs;
}
public TextViewLandscape(Parcel in) {
text = in.readString();
textColor = in.readInt();
textSize = in.readInt();
backgroundColor = in.readInt();
paddingAttrs = in.createIntArray();
}
public static final Creator<TextViewLandscape> CREATOR = new Creator<TextViewLandscape>() {
#Override
public TextViewLandscape createFromParcel(Parcel in) {
return new TextViewLandscape(in);
}
#Override
public TextViewLandscape[] newArray(int size) {
return new TextViewLandscape[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(text);
dest.writeInt(textColor);
dest.writeInt(textSize);
dest.writeInt(backgroundColor);
dest.writeIntArray(paddingAttrs);
}
}
Still not working in Landscape mode....
In that case, you will have to define some Parcelable class which keeps all your TextView attributes, for instance:
public class TextViewAttributes implements Parcelable {
private String text;
private int textColor;
private int textSize;
private int visibility;
private int backgroundColor;
private int[] paddingAttrs = {4};
public TextViewAttributes(String text, int textColor, int textSize, int visibility, int backgroundColor, int[] paddingAttrs) {
this.text = text;
this.textColor = textColor;
this.textSize = textSize;
this.visibility = visibility;
this.backgroundColor = backgroundColor;
this.paddingAttrs = paddingAttrs;
}
public TextViewAttributes(Parcel in) {
text = in.readString();
textColor = in.readInt();
textSize = in.readInt();
visibility = in.readInt();
backgroundColor = in.readInt();
paddingAttrs = in.createIntArray();
}
public static final Creator<TextViewAttributes> CREATOR = new Creator<TextViewAttributes>() {
#Override
public TextViewAttributes createFromParcel(Parcel in) {
return new TextViewAttributes(in);
}
#Override
public TextViewAttributes[] newArray(int size) {
return new TextViewAttributes[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(text);
dest.writeInt(textColor);
dest.writeInt(textSize);
dest.writeInt(visibility);
dest.writeInt(backgroundColor);
dest.writeIntArray(paddingAttrs);
}
}
This is how you save the values:
#Override
protected void onSaveInstanceState(Bundle outState) {
int[] padding = {0,5,1,3};
TextViewAttributes attributes = new TextViewAttributes("message", 4, 5, 7, 1, padding);
ArrayList<TextViewAttributes> list = new ArrayList<TextViewAttributes>();
list.add(attributes);
outState.putParcelableArrayList("keyTextViewAttributes", list);
super.onSaveInstanceState(outState);
}
And this is how you retrieve them in onCreate method:
if (savedInstanceState != null) {
ArrayList<TextViewAttributes> textViewAttributes = savedInstanceState.getParcelableArrayListExtra("keyTextViewAttributes");
}
Just add configChanges in activity tag in AndroidManifest.xml as gien below:
<activity
android:name=".TextureViewActivity"
android:configChanges="orientation|screenSize"/>
Everything can be saved without a problem in onSaveInstaceState(). The drawback is you will have to use lots of keys for the values and at some point it can become confusing.
Restore them in onCreateView() by checking if the savedInstanceState is not NULL and by using the same keys I had used previously.
EDIT:
In fact you don't really need to save these values. When the screen orientation is changed, the fragment which contains the adapter will be recreated and the adapter as well. But you have your values hard coded in the adapter. Thus, you don't need to save them.

Saving dynamically added LinearLayouts without using savedInstanceState?

I have a layout in which I have dynamically added custom views at a push of a button. These layouts extend LinearLayout and each carry their own unique Action objects.
The views will disappear, however, if onCreate is called again, when the user navigates away or rotates the screen. I want to keep these custom ActionHolder views there. To add to the problem, the ActionHolder objects contain sensitive information. The Action objects themselves store a live timer(that is supposed to keep on ticking even if the app is off), as well as other information.
According to an answer below, I have done the following, but to no avail. Here is what I have so far:
public class ActionHolder extends LinearLayout implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2271402255369440088L;
private Action action;
private String timer;
public static final int ACTION_TITLE = 0, ACTION_TIMER = 1,
PAUSEANDPLAY_BTN = 2, FINISH_BTN = 3;
public ActionHolder(Context context) {
super(context);
}
public ActionHolder(Context context, AttributeSet attr) {
super(context, attr);
}
public ActionHolder(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
}
public void initiate(Action input) {
// int hashedID = input.getActionName().hashCode();
// if (hashedID < 0)
// hashedID *= -1;
// this.setId(hashedID);
this.setOrientation(LinearLayout.VERTICAL);
this.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
action = input;
LayoutInflater inflater = LayoutInflater.from(getContext());
View view = inflater.inflate(R.layout.action_holder_layout, this, true);
TextView actionTitle = (TextView) view
.findViewById(com.tonimiko.mochi_bean.R.id.action_holder_title);
actionTitle.setText(action.getActionName());
actionTitle.setId(ActionHolder.ACTION_TITLE);
TextView actionTimer = (TextView) view
.findViewById(R.id.action_holder_timer);
actionTimer.setId(ActionHolder.ACTION_TIMER);
Button pauseBtn = (Button) view
.findViewById(com.tonimiko.mochi_bean.R.id.pause_and_play_timer_btn);
pauseBtn.setId(ActionHolder.PAUSEANDPLAY_BTN);
Button finishBtn = (Button) view
.findViewById(com.tonimiko.mochi_bean.R.id.finish_activity_button);
finishBtn.setId(ActionHolder.FINISH_BTN);
action.setActivityStartTime();
}
public Action finishAction() {
action.setActivityStopTime();
return action;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
}
public String toString() {
return "Action stored: " + action.getActionName();
}
#Override
public boolean equals(Object other) {
ActionHolder otherObj = (ActionHolder) other;
if (this.action.getActionName().toUpperCase()
.equals(otherObj.action.getActionName().toUpperCase()))
return true;
return false;
}
#Override
public int hashCode() {
return action.getActionName().hashCode();
}
#Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
Bundle data = new Bundle();
data.putString("Timer", timer);
data.putSerializable("Action", action);
Log.e("debug", "View onSaveInstanceState called!"); // TODO
Parcelable test = new ActionHolderSavedState(superState, data);
if(test==null)
Log.e("debug", "NULL PARCELABLE"); // TODO
return new ActionHolderSavedState(superState, data);
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
Log.e("debug", "View onRestore called!");
if (state instanceof ActionHolderSavedState) {
final ActionHolderSavedState savedState = (ActionHolderSavedState) state;
this.action = savedState.getAction();
this.timer = savedState.getTimer();
// this.initiate(action);
super.onRestoreInstanceState(savedState.getSuperState());
Log.e("debug", "View onRestoreInstanceState finished"); // TODO
}
}
static class ActionHolderSavedState extends BaseSavedState {
private Action storedAction;
private String storedTimer;
public ActionHolderSavedState(Parcelable superState, Bundle data) {
super(superState);
storedTimer = data.getString("Timer");
storedAction = (Action) data.getSerializable("Action");
}
private ActionHolderSavedState(Parcel in) {
super(in);
storedTimer = in.readString();
storedAction = in.readParcelable(ActionHolder.class.getClassLoader());
}
public Action getAction() {
return storedAction;
}
public String getTimer() {
return storedTimer;
}
#Override
public void writeToParcel(final Parcel out, final int flags) {
super.writeToParcel(out, flags);
out.writeString(storedTimer);
out.writeSerializable(storedAction);
}
// required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<ActionHolderSavedState> CREATOR = new Parcelable.Creator<ActionHolderSavedState>() {
public ActionHolderSavedState createFromParcel(final Parcel in) {
return new ActionHolderSavedState(in);
}
public ActionHolderSavedState[] newArray(int size) {
return new ActionHolderSavedState[size];
}
};
}
}
Is there SOMETHING I am doing wrong? I've spend almost 4 days already on this.
I have a situation very similar to yours, with custom views being added dynamically to the screen and that need to save state when the activity is killed by the OS and recreated later, for example.
I'm overriding onSaveInstanceState on the custom view. It needs to return a Parcelable object. The key is to create a custom class that extends BaseSavedState and stores your data into that Parcelable. It would look somewhat like this:
#Override
protected Parcelable onSaveInstanceState() {
final Parcelable state = super.onSaveInstanceState();
return new ContainerLayoutSavedState(state, data);
}
#Override
protected void onRestoreInstanceState(final Parcelable state) {
if (state instanceof ContainerLayoutSavedState) {
final ContainerLayoutSavedState savedState = (ContainerLayoutSavedState)state;
this.data = savedState.getData();
super.onRestoreInstanceState(savedState.getSuperState());
}
}
public static class ContainerLayoutSavedState extends BaseSavedState {
private String data;
ContainerLayoutSavedState(final Parcelable superState, final String data) {
super(superState);
// Here in this constructor you inject whatever you want to get saved into the Parcelable object. In this contrived example, we're just saving a string called data.
this.data = data;
}
private ContainerLayoutSavedState(final Parcel in) {
super(in);
data = in.readString();
}
public String getData()
return data;
}
#Override
public void writeToParcel(final Parcel out, final int flags) {
super.writeToParcel(out, flags);
out.writeString(data);
}
// required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<ContainerLayoutSavedState> CREATOR = new Parcelable.Creator<ContainerLayoutSavedState>() {
#Override
public ContainerLayoutSavedState createFromParcel(final Parcel in) {
return new ContainerLayoutSavedState(in);
}
#Override
public ContainerLayoutSavedState[] newArray(final int size) {
return new ContainerLayoutSavedState[size];
}
};
} }
Also, don't forget to set IDs to your dynamically added views, so they get re-added to the View tree when you come back.

To pass a parcelable arraylist of objects

I trying to pass array of objects via Parcelable. But after the transfer, the data is converted into something strange
The transmitting part:
for (int i=0; i<res.size(); i++) {
Log.d(LOG_TAG, "id = "+res.get(i).id+" weigth = "+res.get(i).weight);
}
ParcelableProducts Checked = new ParcelableProducts();
Checked.setList(res);
intent.putExtra(ParcelableProducts.class.getCanonicalName(), Checked);
The receiving part:
ParcelableProducts res = (ParcelableProducts) data.getParcelableExtra(ParcelableProducts.class.getCanonicalName());
ArrayList<prProduct> prod = res.prod;
for (int i=0; i<prod.size(); i++) {
Log.d(LOG_TAG, "id = "+prod.get(i).id+" weigth = "+prod.get(i).weight);
}
Classes
Parcelable with ArrayList:
public class ParcelableProducts implements Parcelable {
final private static String LOG_TAG = "ParcelableProducts";
public ArrayList<prProduct> prod;
public ParcelableProducts() {
prod = new ArrayList<prProduct>();
}
public void setList(ArrayList<prProduct> _prod){
prod = _prod;
}
public ArrayList<prProduct> getList() {
return prod;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeList(prod);
}
public static final Parcelable.Creator<ParcelableProducts> CREATOR = new Parcelable.Creator<ParcelableProducts>() {
public ParcelableProducts createFromParcel(Parcel in) {
return new ParcelableProducts(in);
}
public ParcelableProducts[] newArray(int size) {
return new ParcelableProducts[size];
}
};
private ParcelableProducts(Parcel parcel) {
prod = new ArrayList<prProduct>();
parcel.readTypedList(prod, prProduct.CREATOR);
}
}
and prProduct:
public class prProduct implements Parcelable {
final static String LOG_TAG = "prProduct";
public float weight;
public int id;
public prProduct(int _id, Float _weight) {
weight = _weight;
id = _id;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeFloat(weight);
parcel.writeInt(id);
}
public static final Parcelable.Creator<prProduct> CREATOR = new Parcelable.Creator<prProduct>() {
public prProduct createFromParcel(Parcel in) {
return new prProduct(in);
}
public prProduct[] newArray(int size) {
return new prProduct[size];
}
};
private prProduct(Parcel parcel) {
weight = parcel.readFloat();
id = parcel.readInt();
}
}
in log:
Transmit: id = 7 weigth = 0.0
Recive: id = 7602278 weigth = 4.2E-44
Off hand I don't see where the data is getting corrupted in transmission, but it will help to clean up your code. Personally I have fixed many weird bugs in the past when I refactor my code to improve readability. First thing you should do is remove the class "ParcelableProducts" because you don't need it. Just pass the ArrayList in the intent directly using the putParcelableArrayListExtra method. Shown here.
Also this is a bit nit picky but you shouldn't directly access your fields. It is better to set them as private and use getters/setters. Also using a for each loop for your logging statement would be a bit cleaner.
A safer PrProduct class.
//Java classes should start with capital letter
public class PrProduct implements Parcelable {
private final static String LOG_TAG = "PrProduct";
private float weight;
private int id;
public prProduct(int id, float weight) {
this.weight = weight;
this.id = id;
}
//Using getters only makes this immutable which is safer since the
//weight/id aren't likely to change.
public float getWeight(){
return weight;}
public int getId(){
return id;}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeFloat(weight);
parcel.writeInt(id);
}
public static final Parcelable.Creator<prProduct> CREATOR = new
Parcelable.Creator<prProduct>() {
public prProduct createFromParcel(Parcel in) {
return new prProduct(in);
}
public prProduct[] newArray(int size) {
return new prProduct[size];
}
};
private prProduct(Parcel parcel) {
weight = parcel.readFloat();
id = parcel.readInt();
}
}
//A sample for each loop
for(PrProduct product: prod)
Log.d(LOG_TAG, "weight=" + product.getWeight() + " id=" + product.getId());
Why do you need to create the object ParcelableProducts that implements Parcelable? I think you can just pass the arraylist directly using putParcelableArrayListExtra method from the intent?

Categories

Resources