Help with passing ArrayList and parcelable Activity - android

So I've been googling most of yesterday and last nite and just can't seem to wrap my head around how to pass an arraylist to a subactivity. There are tons of examples and snippets passing primitive data types, but what I have is an arraylist of type address (address.java below).
I've found a lot of stuff on stackoverflow and around the web on this, but nothing that got a lot of attention except for one with a GeoPoint example. Again, it looked to me like they just flattened the GeoPoint object into two integers and passed it in. I can't do that because my address class may expand to include integers, floats, whatever. Right now, the test app below is only two strings for simplicity. I thought if I could get the parcelalbe stuff working with that, the rest could follow.
Can someone post a working example for an ArrayList of a non-primitive object, or perhaps add code below to make this work?
UPDATE: code below is now working after replies/editing. Thanks!
/* helloParcel.java */
public class helloParcel extends Activity
{
// holds objects of type 'address' == name and state
private ArrayList <address> myList;
#Override
public void onCreate (Bundle savedInstanceState)
{
super.onCreate (savedInstanceState);
setContentView (R.layout.main);
Button b1 = (Button) findViewById(R.id.button1);
b1.setOnClickListener(ocl);
myList = new ArrayList();
address frank = new address ("frank", "florida");
address mary = new address ("mary", "maryland");
address monty = new address ("monty", "montana");
myList.add (frank);
myList.add (mary);
myList.add (monty);
// add the myList ArrayList() the the extras for the intent
}
OnClickListener ocl = new OnClickListener()
{
#Override
public void onClick(View v)
{
// fill parceable and launch activity
Intent intent = new Intent().setClass(getBaseContext (), subActivity.class);
// for some reason, I remember a posting saying it's best to create a new
// object to pass. I have no idea why..
ArrayList <address> addyExtras = new ArrayList <address>();
for (int i = 0; i < myList.size(); i++)
addyExtras.add (myList.get(i));
intent.putParcelableArrayListExtra ("mylist", addyExtras);
startActivity(intent);
}
};
}
/* address.java */
public class address implements Parcelable
{
private String name;
private String state;
private static String TAG = "** address **";
public address (String n, String s)
{
name = n;
state = s;
Log.d (TAG, "new address");
}
public address (Parcel in)
{
Log.d (TAG, "parcel in");
name = in.readString ();
state = in.readString ();
}
public String getState ()
{
Log.d (TAG, "getState()");
return (state);
}
public String getName ()
{
Log.d (TAG, "getName()");
return (name);
}
public static final Parcelable.Creator<address> CREATOR
= new Parcelable.Creator<address>()
{
public address createFromParcel(Parcel in)
{
Log.d (TAG, "createFromParcel()");
return new address(in);
}
public address[] newArray (int size)
{
Log.d (TAG, "createFromParcel() newArray ");
return new address[size];
}
};
#Override
public int describeContents ()
{
Log.d (TAG, "describe()");
return 0;
}
#Override
public void writeToParcel (Parcel dest, int flags)
{
Log.d (TAG, "writeToParcel");
dest.writeString (name);
dest.writeString (state);
}
}
/* subActivity.java */
public class subActivity extends Activity
{
private final String TAG = "** subActivity **";
private ArrayList <address> myList;
#Override
protected void onCreate (Bundle savedInstanceState)
{
super.onCreate (savedInstanceState);
Log.d (TAG, "onCreate() in subActivity");
setContentView(R.layout.subactivity);
TextView tv1 = (TextView) findViewById(R.id.tv_sub);
myList = getIntent().getParcelableArrayListExtra ("mylist");
Log.d (TAG, "got myList");
for (int i = 0; i < myList.size (); i++)
{
address a = myList.get (i);
Log.d (TAG, "state:" + a.getState ());
tv1.setText (a.getName () + " is from " + a.getState ());
}
}
}

I can see a number of problems here:
Why use addressParcelable? Why not make address implement Parcelable, and then use:
intent.putParcelableArrayListExtra( "addresses", addyExtras );
Your parcelable object must include a static CREATOR. See the documentation for details.
You are not actually adding any extras to the intent before you call startActivity(). See point 1 for a suggestion here.
I think that you will need to address all of these issues in order to get it working.

It can be done MUCH simpler, without all the pain-in-the-ass of implementing Parcelable...
ArrayList (but NOT any List) is Serializable. So, you can put the entire list using putExtra() and retrieve it using getSerializableExtra(), as Sam said.
BUT, I want to add one more important thing: the object your array list stores has to also implement Serializable... and all other complex objects that the object may contain (in your case none) must also implement that (so it's recursive - in order to serialize an object, you must be able to serialize all of its fields).
Now, you might be asking yourself why implementing Serializable instead of Parcelable when there are already methods for reading and writing array lists of parcelables?
Well... the difference is simplicity - just add implements Serializable and optionally private static final long serialVersionUID = SOME_CONSTANT and you're DONE! That is the reason why I never use Parcelable - you can do all those things using Serializable with literally 2 lines of code - instead of many method inheritances and all that stuff...

You can pass Serializable objects via putExtra. ArrayList implements Serializable.

Mike dg is correct!
putExtra() and getSerializable() will store and retrieve an ArrayList<> of your custom objects, with no interface implementing required. Worked for me!

Related

send data from one activity to another from SQLite database

I want to retrieve all the records from table and show it in another activity,but when I do I am getting one record at a time and in reverse order.
I want all the records in single activity
#Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, ViewAllActivity.class);//
while (c.moveToNext()) {
i.putExtra("rollno", c.getString(0));
i.putExtra("name", c.getString(1));
i.putExtra("marks", c.getString(2));
startActivity(i);
}
}
});
ViewAllActivity
String rollno=getIntent().getStringExtra("rollno");
String name=getIntent().getStringExtra("name");
String marks=getIntent().getStringExtra("marks");
TextView text;
text=(TextView) findViewById(R.id.text);
String str="Roll no:"+rollno+"\nName:"+name+"\nmarks:"+marks;
text.setText(str);
You have to create a model for doing the same..
example :
ArrayList<myModel> modelList = new ArrayList<>();
while(c.moveToNext()){
MyModel myModel = new MyModel();
myModel.setRollNumber(c.getString(0));
myModel.setName(c.getString(1));
myModel.setMark(c.getString(2));
modelList.add(myModel);
}
}
});
intent.putParcelableArrayListExtra(TAG, modelList);
startActivity(intent);
MyModel
public class MyModel implements Parcelable{
private String rollNumber;
private String name;
private String mark;
public void setRollNumner(String rollNumber){
this.rollNumber = rillNumber;
}
.....
}
the model should implement Parcelable or Serializable
I'l prefer just start your activity in the button click, and retrieve the data in the second activity.
#Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, ViewAllActivity.class);
startActivity(i);
}
});
ViewAllActivity
//retrieve data to your cursor
String str = "";
while(c.moveToNext()){
str = str + "Roll no:" + rollno + "\nName:" + name + "\nmarks:" + marks;
text.setText(str);
}
If i were you i just start new intent activity and in some method in the second activity, oncreate or onstart retrieve your data from db.
But if you want to send your data in a bundle to the another one, you have to consider something
1.- you have to create a lis of your objets to send.
2.- to send arraylist or objects with a bundle you have to implement and extend Parcelable interface, otherwise will fail.
3.- if you use to send data vía bundle, is not good idea, could weak your performance, just think if your list are many objects, and you want to send vis bundle, app will bd slow. So think about.
Regards.

Global variables not passing properly

I have created a class that holds common variables and functions and is inherited by the activity classes that interface with the different UI pages in my app. I have been passing information between classes and activities using getVariable() and setVariable(input) functions. Suddenly, I can no longer pass information this way (it had been working well until recent edits, and now I can't figure out which change screwed this up). I have used Log outputs to determine that the data is storing properly - with the setVariable(input) functions - but when called later with the getVariable() functions it returns null. Any thoughts?
*Note, I recently started incorporating fragments into my project, extending FragmentActivity instead of Activity on my main class. I don't think this is causing the problem, but could it? If it does, whats the best practice to pass global variable info, and use fragments?
Code samples:
Main Inherited class:
public class MenuBarActivity extends FragmentActivity {
private String keyA;
private String keyB;
private int token;
private String Salt;
private long expires;
public String getKeyB() {
return keyB;
}
public String getKeyA() {
return keyA;
}
public int getTokenID() {
return token;
}
public void setToken(int tkn) {
token = tkn;
}
public void setKeyB(String kyB) {
keyB = kyB;
}
public void setKeyA(String kyA) {
keyA = kyA;
}
//Other common functions
}
LogIn Activity Class (gets log in info from web, stores into global variables):
public class WebContentGet extends MenuBarActivity{
public int tryLogOn(String uEmail, String pw) {
//call to get new keys on start up
JSONObject jObSend = new JSONObject();
try {
jObSend.put("email", uEmail);
jObSend.put("password", pw);
t.start();
t.join();
if(getStatus() == USER_STATUS_SUCCESSFULLOGIN){
String data = getData();
JSONObject jObReturn = new JSONObject(data);
String kyA = jObReturn.getString("keyA");
String kyB = jObReturn.getString("keyB");
int tkn = Integer.parseInt(jObReturn.getString("tokenID"));
String salt = jObReturn.getString("salt");
long exp = Long.parseLong(jObReturn.getString("expiration"));
int uID = Integer.parseInt(jObReturn.getString("userID"));
// Log outputs confirm data being read properly, and reported to setX() functions
setToken(tkn);
setKeyA(kyA);
setKeyB(kyB);
setSalt(salt);
setExpires(exp);
Log.d("WebContentGet tryLogIn","login values stored");
}
return getStatus();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return getStatus();
}
}
Activity Class, checks if keyA/B/etc stored:
public class UserLogIn2 extends MenuBarActivity implements EmailListener {
String emailIn;
String pwIn;
Context context = this;
#Override
public void onEmailLogInClick(String email, String pw) {
Log.d("UserLogin2", "onEmailLogInClick");
emailIn = email;
pwIn = pw;
emailIn = emailIn.trim();
emailIn = emailIn.toUpperCase();
Log.d("prepped email", emailIn);
pwIn = pwIn.trim();
WebContentGet webOb = new WebContentGet();
int webLog = webOb.tryLogOn(emailIn, pwIn);
if (webLog == USER_STATUS_SUCCESSFULLOGIN) {
int tkn = getTokenID();
long exp = getExpires();
String kya = getKeyA();
String kyb = getKeyB();
String slt = getSalt();
Log.d("UserLogIn2 - token", String.valueOf(tkn));
//Log statements confirm that getX() functions returning null
session.storeLoginSession(emailIn, pwIn, thisUser, tkn, exp, kya, kyb, slt);
Intent intent1 = new Intent(context, MainActivitiy.class);
startActivity(intent1);
} else {
showDialog(this, "Log in failure", "Incorrect Password");
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.userlogin2);
}
}
This cannot work, because you have two differend instances of your MenuBarActivity. Also that is not the way to pass data from one activity to another in android.
If you want to use data from one activity in another activity, you have to add them to an intent in the activity which provides the data, and extract them in the other. For more information see here: How do I pass data between Activities in Android application?
If you don't want to start the activity and send the data with the intent, you have to store the data somewhere e.g. SharedPreferences and fetch them again: How to use SharedPreferences in Android to store, fetch and edit values

Why is my #Override of toString() method never called on Android?

It seems that my implementation of toString() method in an Activity is never called no matter what I do. I always get result from java.lang.Object (like Ljava.lang.Object;#4059d6a8) instead of "I was called!". I've read a bunch of articles and scanned through a lot of code and I can't figure out what I'm doing wrong. Could someone please explain to me how do I override the method? This is my code:
public class OverrideTestActivity extends Activity {
private int number = 27;
private String[] items = { "MyFirstItem", "MySecondItem" };
public Object[] stuff = { number, items };
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.test);
OverrideTest();
}
public void OverrideTest() {
TextView result = (TextView) findViewById(R.id.textView1);
result.setText(stuff.toString());
}
#Override
public String toString() {
return "I was called!";
}
}
Thanks a lot in advance!
You are overriding the toString() method for the class OverrideTestActivity and calling toString() for the array of Objects: that's why you are getting the default implementation of the Object class.
In this case, the toString() is called only on objects of type OverrideTestActivity.
Instead you are calling toString() on an array, which doesn't have such an implementation.
private int number = 27;
private String[] items = { "MyFirstItem", "MySecondItem" };
public Object[] stuff = { number, items };
If you did something like this
List<Object> stuff = Arrays.asList(27, "MyFirstItem", "MySecondItem");
stuff.toString();
You would find that it outputs a format that represents the contents of the List. The format will have been determined by the toString() method on the List implementation.

Problem Passing ArrayList from one Activity to Another

i'm continuously running into problems trying to pass an ArrayList from one Activity to another. My code is failing with a Null Pointer Exception when i try to iterate through the ArrayList in my XMLParser Class. I've put print statements into the Activity that generates the ArrayList and it looks fine. Can anyone see what i'm doing wrong or why i get a Null pointer Exception when retrieving the ArrayList?
public void onClick(View v) {
if (selItemArray[0] == null) {
Toast.makeText(getApplicationContext()," Please make a Selection ", Toast.LENGTH_SHORT).show();
} else {
Intent intent = new Intent(Recipes2.this, XMLParser.class);
Log.v("Recipes2", "selItemArray[0] before call to XML Parser : " + selItemArray[0]);
//Log.v("Recipes2", "selItemArray[1] before call to XML Parser : " + selItemArray[1]);
intent.putExtra("items_to_parse", selItemArray);
startActivityForResult(intent, 0);
}
}
public class XMLParser extends ListActivity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Bundle b = getIntent().getExtras();
//itemsToParse = b.getStringArrayList("items_to_parse");
ArrayList<String> itemsToParse = new ArrayList<String>();
itemsToParse = getIntent().getExtras().getStringArrayList("items_to_parse"); Iterator<String> iterator = itemsToParse.iterator(); while(iterator.hasNext())
Log.v("XMLParser", iterator.next().toString());
It looks like you're putting a String array, not a ArrayList<String>.
You used a string array on the "sender" side and are trying to get it back as an ArrayList on the receiver side. That won't work. Use a String array on both sides and -- if necessary -- pull it into an array list.
The procedure for passing the data is here:
Passing String array between two class in android application
To convert it to a List - simply do:
List<String> = Arrays.asList(stringArray);

Passing JSONObject into another activity

I'm hitting an external API that's returning JSON data (new dvd titles). I'm able to parse out the JSON and list each dvd title and other dvd information into a ListView just fine. I was also able to setup an onListItemClick method just fine for primitive data (title string). I ended up writing something like so for the onListItemClick method:
Just to note, the productArray is a class var that's being set by another method that holds an array of JSONObjects.
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Intent i = new Intent(DvdListingActivity.this, MovieProductActivity.class);
try {
JSONObject jsonObj = productArray.getJSONObject(position);
i.putExtra("mTitle", jsonObj.getJSONObject("Title").opt("val").toString());
i.putExtra("mRelDate", jsonObj.getJSONObject("RelDate").opt("val").toString());
i.putExtra("mDesc", jsonObj.getJSONObject("Desc").opt("val").toString());
i.putExtra("mRating", jsonObj.getJSONObject("MPAA").getJSONObject("Rating").opt("val").toString());
i.putExtra("mActors", jsonObj.getJSONObject("Actors").opt("val").toString());
i.putExtra("mImage", jsonObj.getJSONObject("Image").opt("val").toString());
startActivity(i);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The above code all works, but I'm thinking there's GOTTA be a better way for me to pass in data to another Activity. I was thinking that I would be able to pass a JSONObject that contains all the data for a dvd movie instead of setting each data point individually.
I tried for a week and a half to figure out how to use Parcelable. I tried instantiating a new JSONObject jsonObj that implements Parcelable with no luck. I kept getting an error in my LogCat that said that the object was un-parcelable.
I've tried reading the Android developer site and other blogs, but I couldn't apply their examples to what I needed to do.
Any help would be much appreciated
You can simply put an entire JSONObject as a string. Something like this:
i.putString("product", jsonObj.toString);
And then in the MovieProductActivity you could
JSONObject jsonObj = new JSONObject(getIntent().getStringExtra("product"));
From Current Activity
Intent mIntent = new Intent(CurrentActivity.this, TargetActivity.class);
mIntent.putExtra("ITEM_EXTRA", json_object.toString());
startActivity(mIntent);
From Target Activity's onCreate
try {
json_object = new JSONObject(getIntent().getStringExtra("ITEM_EXTRA"));
Log.e(TAG, "Example Item: " + json_object.getString("KEY"));
} catch (JSONException e) {
e.printStackTrace();
}
You can just encapsulate all of the information about a movie into a Movie object, which implements Parcelable.
The code will look similar to above, but instead of passing 6 different extras you can just pass one extra that is the movie.
Movie movie = new Movie();
movie.setTitle(jsonObj.getJSONObject("Title").opt("val").toString());
movie.setRelDat(jsonObj.getJSONObject("RelDate").opt("val").toString());
.
.
.
i.putExtra("movie", movie);
For information on implementing a Parcelable object, see Parcelable docs. You basically just write out each string in 'writeToParcel', and read in each string in 'readFromParcel' in the correct order.
The answer posted here by Cheryl Simon is completely correct however this might not explain it too clearly for someone who is new to android or java. Please let me build on her answer.
The best and cleanest way to do this is with the Parcelable interface.
Declare a class as below that implements Parcelable. This one uses an accessor and mutator which isn't actually necessary.
public class JSonArrayParser implements Parcelable {
private JSONArray jsonArray;
public JSonArrayParser() {}
public JSonArrayParser(JSONArray jsonArray) {
this.jsonArray = jsonArray;
}
public void setJsonArray(JSONArray jsonArray) {
this.jsonArray = jsonArray;
}
public JSONArray getJsonArray() {
return jsonArray;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
}
}
The next step would be to create an instance of that class (in this case JSonArrayParser) and pass it through the Bundle as in the example below.
//This could be an Activity, Fragment, DialogFragment or any class that uses the Bundle Type such as savedInstanceState.
jobItemsPopUp = new JobItemsPopUp();//This example uses a DialogFragment.
Bundle bundle = new Bundle();
JSonArrayParser jSonArrayParser = new JSonArrayParser(jsonArray);
bundle.putParcelable("jsonArray", jSonArrayParser);
jobItemsPopUp.setArguments(bundle);
jobItemsPopUp.show(getActivity().getSupportFragmentManager(), "JobItemsPopUp");
The the way your would retrieve the value would be as follows (I've used the onCreate but you can retrieve the value anywhere):
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
this.jsonArray = ((JSonArrayParser) getArguments().getParcelable("jsonArray")).getJsonArray();
}
}
Just as an additional note: you can do this with custom constructors, but this method has been deprecated by Google.
The old deprecated method (associated with app compat v4 NOT RECOMMENDED) would be:
private Fragment MyFragment;
MyFragment = new MyFragment(jsonArray)
Hope this help and clears a few things up.
The Parcelable interface can be used with any data class with or without mutators and/or accessors.
Are you storing the information in a DB? If you are, you can simply pass the ID of the desired title (via the intent).
Have a look at gson. It allows you to sereialise and deserialise JSON blobs to entire class instances.
Just create a Parcel like this:
public class Parcel implements Serializable {
private JSONObject obj;
public Parcel(JSONObject obj) {
this.obj = obj;
}
public JSONObject getObj() {
return obj;
}}
And put into the bundle:
Parcel p = new Parcel(data);
Bundle args = new Bundle();
args.putSerializable("events", p);
Finally, you can get the JSONObject back using:
JSONObject obj = ((Parcel) ((Bundle) getArguments()).getSerializable("events")).getObj();

Categories

Resources