xamarin android bundle put parcelable array - android

I need to save array of object in bundle, here is what I did :
create class what is implementing IParcable interface:
public class SimpleSelectorItem : Java.Lang.Object, Android.OS.IParcelable
{
public int Id { get; set; }
public string Name { get; set; }
public bool Selected { get; set; }
IntPtr IJavaObject.Handle { get { return IntPtr.Zero; } }
public int DescribeContents()
{
return 0;
}
public SimpleSelectorItem()
{
}
public SimpleSelectorItem(int id, string name)
{
Id = id;
Name = name;
}
public SimpleSelectorItem(Parcel parcel)
{
Id = parcel.ReadInt();
Name = parcel.ReadString();
}
public void WriteToParcel(Parcel dest, [GeneratedEnum] ParcelableWriteFlags flags)
{
dest.WriteInt(Id);
dest.WriteString(Name);
}
static readonly GenericParcelableCreator<SimpleSelectorItem> _creator
= new GenericParcelableCreator<SimpleSelectorItem>((parcel) => new SimpleSelectorItem(parcel));
[ExportField("CREATOR")]
public static GenericParcelableCreator<SimpleSelectorItem> GetCreator()
{
return _creator;
}
}
when I'm creating instance of my fragment , trying to set array of SimpleSelectorItem in bundle :
var items = _viewModel.GetEmployees().Select(l => new SimpleSelectorItem(l.Id, l.LegalName)).ToArray();
....
args.PutParcelableArray(ITEMS, items);
But when I'm getting back this array, it is array of nulls..
var items = Arguments.GetParcelableArray(ITEMS);
Here is demo application , it throw exception while i'm trying to cast items:
https://github.com/Nininea/XamarinAndroidPutParcelableArray

But when I'm getting back this array, it is array of nulls..
I've tested the project.The data are passed correctly, but the cast failed.
To resolve the problem, you need to cast the array differently:
In SetData Method:
private void SetData(View rootView)
{
...
//use the following line to retrieve and cast the items to SimpleSelectorItem[]
var items = Arguments.GetParcelableArray(ITEMS).Cast<SimpleSelectorItem>().ToList();
_lvSelector = (ListView)rootView.FindViewById(Resource.Id.lvSelector);
//pass the items to your adapter.
SimpleSelectorListAdapter adapter = new SimpleSelectorListAdapter(Activity, items);
_lvSelector.Adapter = adapter;
}

Related

How to change the data of an item at once using DiffUtil?

When I press the toggle button, I want to change the units of the list of the currently displayed recycler views at once.
I used ListAdapter + DiffUtil to display the recycler view.
The way I tried to implement this feature is to load the current list when the toggle button is pressed.
Then, after resetting the new toggle unit values ​​for the current lists, I used submitList() to update the list.
But this was the wrong way.
My guess is because the variable created for the value of the list loaded to be updated has the same reference value, so the value changed at the same time.
In other words, there is no change because the values ​​of the update list and the existing list are the same.
What can I do to solve this problem?
RoutineDetailModel.java
public class RoutineDetailModel {
public int id;
private int set = 1;
public static String unit = "kg";
public RoutineDetailModel() {
Random random = new Random();
this.id = random.nextInt();
}
public RoutineDetailModel(int set) {
Random random = new Random();
this.id = random.nextInt();
this.set = set+1;
}
public int getSet() {
return set;
}
public int getId() {
return id;
}
public String getWeight() {
return weight;
}
public String getUnit() {
return unit;
}
#Override
public int hashCode() {
return Objects.hash(set, weight); // getWeight를 호출하면 더 다양하게 되나?
}
#Override
public boolean equals(#Nullable Object obj) {
if(obj != null && obj instanceof RoutineDetailModel) {
RoutineDetailModel model = (RoutineDetailModel) obj;
if(this.id == model.getId()) {
return true;
}
}
return false;
}
}
MainActivity.java
public class WriteRoutineActivity extends AppCompatActivity implements WritingCommentDialogFragment.OnDialogClosedListener {
List<RoutineModel> items;
RoutineListAdapter listAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write_routine);
listAdapter.setOnRoutineClickListener(new RoutineListAdapter.OnRoutineItemClickListener() {
#Override
public void onUnitBtnClicked(int curRoutinePos, String unit) {
Object obj = listAdapter.getRoutineItem(curRoutinePos);
RoutineModel item = (RoutineModel) obj;
if(obj instanceof RoutineModel) {
for(RoutineDetailModel detailItem : item.getDetailItemList()) {
detailItem.setUnit(unit);
}
listAdapter.submitList(getUpdatedList());
}
}
});
}
}
Please tell me if you need more information
To change all the items you need to use notifyDataSetChanged() otherwise you cannot update ALL the items.
In order to do so, you must create a method inside your adapter which does the following ->
public void updateItems(String unit) {
for({YOUR ITEM TYPE} item: {YOUR LIST}) {
item.unit = unit;
}
notifyDataSetChanged();
}
And call this method when you want to change all units.
yourAdapter.updateItems("Kg");

When I send object from fragment to another fragment original data updated

I am using Parcelable when I am sending data between fragments. I send my object class from 'TasksFragment' to 'EditTaskFragment'. My object class is below.
public class TaskResult extends BaseObservable implements Parcelable {
private int taskID;
private String taskName;
public TaskResult() {
}
public int getTaskID() {
return taskID;
}
public void setTaskID(int taskID) {
this.taskID = taskID;
}
#Bindable
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
notifyPropertyChanged(BR.taskName);
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.taskID);
dest.writeString(this.taskName);
}
protected TaskResult(Parcel in) {
this.taskID = in.readInt();
this.taskName = in.readString();
}
public static final Creator<TaskResult> CREATOR = new Creator<TaskResult>() {
#Override
public TaskResult createFromParcel(Parcel source) {
return new TaskResult(source);
}
#Override
public TaskResult[] newArray(int size) {
return new TaskResult[size];
}
};
}
I get the data like this in 'EditTaskFragment';
mViewModel.taskResult =
getArguments().getParcelable(Keys.KEY_TASK_RESULT);
For example when I updated an any data in TaskResult class which I get, the data which in TasksFragment updated, too.
This value updated in TasksFragment, too. But I don't it. How do I?
If I clone this class, is it mind?
Why do you send your data by the fragment arguments when you are using the viewmodel?
The viewmodel is the best solution to share any data between the multiple fragments.
By anyway, I assume that you use the viewmodel in both fragments based on the one activity. Reason of your problem is that the instance of the viewmodel is equal in your fragments.

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];
}

How to load Json data into a spinner in Android?

I can get the data from the server loaded into a List<>, but can't get the specific items.
Json data looks like this.
[
{"subscriptionType":"1234,
"typeName":"stuff",
"name":"Alpha"},
{"subscriptionType":"1234,
"typeName":"stuff",
"name":"Beta"},
]
and so on...
I have a class that that I load the data into from a Presenter calling a Fetch event. All that seems to be working because I get a Log for the data loaded into the Array
public class AppEntitySubscriptions implements Parcelable {
public AppEntitySubscriptions(ApiSubscription apiSubscription) {
this.subscriptionType = apiSubscription.getSubscriptionType();
this.typeName = apiSubscription.getTypeName();
this.name = apiSubscription.getName();
}
private int subscriptionType;
private String typeName;
private String name;
public
int getSubscriptionType() {
return subscriptionType;
}
public String getTypeName() {
return typeName;
}
public String getName() {
return name;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.subscriptionType);
dest.writeString(this.typeName);
dest.writeString(this.name);
}
#SuppressWarnings("ResourceType")
protected AppEntitySubscriptions(Parcel in) {
this.subscriptionType = in.readInt();
this.typeName = in.readString();
this.name = in.readString();
}
public static final Creator<AppEntitySubscriptions> CREATOR = new Creator<AppEntitySubscriptions>() {
#Override
public AppEntitySubscriptions createFromParcel(Parcel in) {
return new AppEntitySubscriptions(in);
}
#Override
public AppEntitySubscriptions[] newArray(int size) {
return new AppEntitySubscriptions[size];
}
};
Now here is where I am getting lost. I just want to get the data for "name" elements into a spinner
Spinner spinner = (Spinner) findViewById(R.id.spinner);
List<AppEntitySubscriptions> userSubscriptions;//data is here
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.toolbar_spinner_item, "what goes here"???);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
I don't know how to get the name into a string array of it's own and loaded into the spinner. Any suggestions would be helpful.
Thanks.
You could not use this list in Spinner
private List<AppEntitySubscriptions> subscriptionsList = new ArrayList<>();
Create new list
private List subscriptionsStrings = new ArrayList<>();
and fill it
for (int i=0; i<subscriptionsList.size(); i++) {
subscriptionsStrings.add(subscriptionsList.get(i).getTypeName());
}
and then
ArrayAdapter<ArrayList<String>>(this, R.layout.toolbar_spinner_item, subscriptionsStrings);

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