In my OnCreate method I am making a network call and populating a listview with the results. I am saving the result in an ArrayList and using the ArrayList to populate the listview. If the user presses the back button and reenters the activity I would like to store the ArrayLists in a SavedInstanceState Bundle and populate the ListView with the stored ArrayLists instead of making the network call a second time. The following is my OnCreate code:
if (savedInstanceState != null) {
largeImage = savedInstanceState.getParcelableArrayList("Image");
flowercode = savedInstanceState.getStringArrayList("Code");
flowerprice = savedInstanceState.getStringArrayList("Price");
flowerdimensions = savedInstanceState.getStringArrayList("Dimension");
largeImageText = savedInstanceState.getStringArrayList("Imagetext");
ListView listView = (ListView) findViewById(R.id.LowRomance);
FlowerShopAdapter listAdapter = new FlowerShopAdapter(this, flowercode, flowerName, flowerprice, largeImage, flowerdimensions);
listView.setAdapter(listAdapter);
} else if(savedInstanceState == null) {
makesoapcall();
}
}
This is my OnSaveInstanceState code
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putParcelableArrayList("Image", largeImage);
savedInstanceState.putStringArrayList("Code", flowercode);
savedInstanceState.putStringArrayList("Price", flowerprice);
savedInstanceState.putStringArrayList("Dimension", flowerdimensions);
super.onSaveInstanceState(savedInstanceState);
}
Currently my App is making the network call every single time. Is their something else I need to implement in order to get this to work or is their an error in the current way I am trying to implement this? The ArrayList's are definitely not null when I put them into savedInstanceState.
onSaveInstanceState() is not supposed to be called when the back button is pressed on an Activity. It's called when Android decides to destroy the Activity.
e.g. When orientation changes.
What you need here is SharedPreferences. Store the ArrayLists using SharedPreferences in onStop(). And get it in onStart(). Here is how to store ArrayList in SharedPreferences.
Related
I have two activities that each contain fragments. Activity A has a fragment with a ListView. Rows of the ListView are obtained from a SQLiteDatabase.
Activity B contains a fragment that enables entries of the database to be edited. When a user edits a database entry, saves the data, then returns to Activity A via the backbutton or:
actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
How do I notify the ListView in Activity A that there has been a change to the database? I understand how to communicate between an Activity and it's fragments, but how do I implement a notifydatasetchanged when the fragments are in two different activities?
I suspect that going back to Activity A via the back button is simply recalling the view from the stack, but that view has changed when there is a database change.
You could simply call the notifyDataSetChanged in onResume in your Activity A. So when you close or go back from Activity B to A, it will update the data. Don't forget to check if the adapter is different than null, otherwise it could crash the first time you open the App.
You could also make a public function in your Activity A which updates the list and call it from your Activity B.
class ActivityA
public static void updateList() {
if(adapter != null){
adapter.notifyDataSetChanged;
}
}
class ActivityB
ActivityA.updateList();
Hope it helps :)
you could start activity with startActivityForResult(start activityB) and override method onActivityResult in activityA and notifyDataSetChanged when needed.
You will need to read the updated dataset from your SQLite database and pass it adapter again. After you need to notify adapter. For example, like in the code below:
In your Activity A
#Override
protected void onResume() {
super.onResume();
// method to read new dataset
Database ourDB = new Database(this);
ourDB.openDB();
data = ourDB.getData();
ourDB.closeDB();
if(adapter == null) {
// initializes adapter when first launched
adapter = new RecyclerViewAdapter(data, this);
yourListView.setAdapter(adapter);
}
else {
// in subsequent returns to activity we are replacing
// old data with new and reusing existing adapter
adapter.updateData(data);
}
}
In your adapter class include similar method:
public void updateData(ArrayList<String> newData) {
data = newData;
this.notifyDataSetChanged();
}
The main activity has a "To ListView" button that launches a new activity with a list view. Each time this new activity loads, it calls an AsyncTask() method that retrieves some JSON remotely, parses it, and binds the data to the list view. Then, setContentView() is called (in onPostExecute()) to show the UI. How do I preserve the list view data, or at least the data array (for rebinding) on subsequent launches of that activity so that the ASyncTask() doesn't have to be called every time?
The ASyncTask() should get called only at the beginning (and not until when the application is forcefully terminated), subsequent calls should be done manually by the user perhaps with an onClick() event. I have tried setting a boolean for that purpose, but if so how to display the previous state of the list view when the boolean is false? I have also looked into onResume() and onBackPressed() but they don't seem to be much relevant.
Main.java:
toListView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent myIntent = new Intent(getApplicationContext(), ListView.class);
startActivity(myIntent);
}
});
ListView.java:
public class ListView extends ActionBarActivity {
private static boolean isFirstLaunch = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isFirstLaunch) {
// execute the ASyncTask();
isFirstLaunch = false;
}
// else display previous listview data or last activity state
}// onCreate()
For persisting data in your Android application, you have two obvious choices.
Using an SQLite database. This alternative is good if you have quite a lot of data to manage, sort and maintain. E.g if your JSON response contains a lot of data, persisting it to a database would be a good solution. Then you would simply query the database instead of executing the AsyncTask repeatedly. Vogella provides an excellent tutorial on this matter.
Using SharedPerefences. This alternative is better if you only have a couple of variables to relate to. Storing the data in SharedPreferences relieves you of the work needed to design and implement database support for you application. See the official documentation for a reference.
new to droid programming. im having a small problem that im sure is simply fixed but ive done some searching and a bunch of tutorials but cant seem to find just what i need so i figured id ask. My app has 2 activites, the first activity is just a simple form where a user enters course information(class title, professor..etc.)
the first activity passes the data which is supposed to be stored in a list in the second activity. problem is that only the first course gets stored in the list, after the first time nothing new gets added to the second activity. Can someone point me in the right direction please? thanks in advance
First Activity
public class CourseDetail extends Activity {
//Course c = new Course();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button save=(Button)findViewById(R.id.save);
save.setOnClickListener(onSave);
}
private View.OnClickListener onSave=new View.OnClickListener() {
public void onClick(View v) {
EditText course=(EditText)findViewById(R.id.course);
EditText professor=(EditText)findViewById(R.id.professor);
EditText location=(EditText)findViewById(R.id.location);
EditText officehrs=(EditText)findViewById(R.id.officehrs);
Intent i=new Intent(CourseDetail.this, CourseList.class);
i.putExtra("myCourse", course.getText().toString());
i.putExtra("myProfessor", professor.getText().toString());
i.putExtra("myLocation", location.getText().toString());
i.putExtra("myOfficehrs", officehrs.getText().toString());
startActivity(i);
}
};
}
Second Activity
public class CourseList extends Activity {
Button btnCourse;
List<Course> model = new ArrayList<Course>();
CourseAdapter adapter=null;
private String dCourse="";
private String dProfessor="";
private String dLocation="";
private String dOfficehrs="";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.clist);
ListView list =(ListView)findViewById(R.id.courses);
adapter=new CourseAdapter();
list.setAdapter(adapter);
Course c = new Course();
Bundle extras = getIntent().getExtras();
dCourse = extras !=null ? extras.getString("myCourse") :"no value entered";
dProfessor = extras !=null ? extras.getString("myProfessor") :"no value entered";
dLocation = extras !=null ? extras.getString("myLocation") :"no value entered";
dOfficehrs = extras !=null ? extras.getString("myOfficehrs") :"no value entered";
c.setCourse(dCourse);
c.setProfessor(dProfessor);
c.setLocation(dLocation);
c.setOfficeHrs(dOfficehrs);
btnCourse =(Button)findViewById(R.id.btnCourse);
btnCourse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
finish();
}
});
}
You are just getting the user entered value in CourseDetail activity and displaying the received value inside the CourseList activity, that means you are not storing these values permanently.
Go through this Android - Data Storage document.
When you move to 2nd activity i.e. CourseList activity, at that time fetch the data from the SQLite table and display the same. whenever you get new values from previous activity, at that time just update the list by adding the new data in ArrayList and make a call on adapter.notifyDataSetChanged()
Some suggestions:
Have your CourseList extend ListActivity instead of just Activity - check out some tutorials on that which should help you set things up correctly.
There seems to be a bit of confusion with how you're handling your lists - you have your model variable but don't seem to be doing anything with it. Again, have a look at a ListView tutorial (just google "android listview tutorial").
You seem to have figured out that you can use "intents" to pass information from one activity to another, but since you're only doing this in the onCreate() method, it's only happening once. Try doing this in your ListActivity's adapter once for each item.
Don't give up on Android, keep trying :-)
Some suggestion:
You have to add your object to the adapter: adapter.add(c); after you get the data.
Call adapter.notifyDataSetChanged() to notify the system that your data for the listView has been changed. Call list.invalidate() to refresh it.
I noticed that you set the button with the finish() method. Hmm, if you do so, the next time you get to CourseList Activity from CourseDetail, the adapter will be null again. No previously received data will be available. Is this what you really want?
The problem is you are not adding the newly added items to the List.So before setting adapter you have to add all your objects like
list.add(c);
There's stuff I'd like to do in my Activities' onCreate() method only if they're constructed the first time, not when the device was rotated (on configuration changes). Currently I'm checking the savedInstanceState parameter passed into onCreate() for this. If it's null, then it's the first time the Activity starts, else there was only a rotation.
Is this a good and reliable way to tell this? Are there alternatives to this?
I don't know of a better solution. Romain Guy describes the same approach (checking savedInstance state or other objects you pass for null).
In the new activity, in onCreate(), all you have to do to get your
object back is to call getLastNonConfigurationInstance(). In
Photostream, this method is invoked and if the returned value is not
null, the grid is loaded with the list of photos from the previous
activity:
private void loadPhotos() {
final Object data = getLastNonConfigurationInstance();
// The activity is starting for the first time, load the photos from Flickr
if (data == null) {
mTask = new GetPhotoListTask().execute(mCurrentPage);
} else {
// The activity was destroyed/created automatically, populate the grid
// of photos with the images loaded by the previous activity
final LoadedPhoto[] photos = (LoadedPhoto[]) data;
for (LoadedPhoto photo : photos) {
addPhoto(photo);
}
}
}
When I am lazy to do this, I just disable recreating the Activity on orientation change. As described at How do I disable orientation change on Android?
I'm confused when it comes down to saving a state. So I know that onSaveInstanceState(Bundle) is called when the activity is about to be destroyed. But how do you store your information in it and bring it back to its original state in onCreate(Bundle savedInstanceState)? I don't understand how this bundle will restore information. It would be helpful if someone can provide an example.
The Dev guide doesn't do a good job of explaining this.
public class Conversation extends Activity {
private ProgressDialog progDialog;
int typeBar;
TextView text1;
EditText edit;
Button respond;
private String name;
private String textAtView;
private String savedName;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.dorothydialog);
text1 = (TextView)findViewById(R.id.dialog);
edit = (EditText)findViewById(R.id.repsond);
respond = (Button)findViewById(R.id.button01);
if(savedInstanceState != null){
savedInstanceState.get(savedName);
text1.setText(savedName);
}
else{
text1.setText("Hello! What is your name?");
respond.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
name = edit.getText().toString();
text1.setText("Nice to meet you "+ name);
}
});
}
}
#Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putString(savedName, name);
}
}
The Bundle is a container for all the information you want to save. You use the put* functions to insert data into it. Here's a short list (there are more) of put functions you can use to store data in the Bundle.
putString
putBoolean
putByte
putChar
putFloat
putLong
putShort
putParcelable (used for objects but they must implement Parcelable)
In your onCreate function, this Bundle is handed back to the program. The best way to check if the application is being reloaded, or started for the first time is:
if (savedInstanceState != null) {
// Then the application is being reloaded
}
To get the data back out, use the get* functions just like the put* functions. The data is stored as a name-value pair. This is like a hashmap. You provide a key and the value, then when you want the value back, you give the key and the function gets the value. Here's a short example.
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("message", "This is my message to be reloaded");
super.onSaveInstanceState(outState);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String message = savedInstanceState.getString("message");
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
Your saved message will be toasted to the screen.
One major note that all new Android developers should know is that any information in Widgets (TextView, Buttons, etc.) will be persisted automatically by Android as long as you assign an ID to them. So that means most of the UI state is taken care of without issue. Only when you need to store other data does this become an issue.
From Android Docs:
The only work required by you is to
provide a unique ID (with the
android:id attribute) for each widget
you want to save its state. If a
widget does not have an ID, then it
cannot save its state
A good information: you don't need to check whether the Bundle object is null into the onCreate() method. Use the onRestoreInstanceState() method, which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null
Store information:
static final String PLAYER_SCORE = "playerScore";
static final String PLAYER_LEVEL = "playerLevel";
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(PLAYER_SCORE, mCurrentScore);
savedInstanceState.putInt(PLAYER_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
If you don't want to restore information in your onCreate-Method:
Here are the examples: Recreating an Activity
Instead of restoring the state during onCreate() you may choose to implement onRestoreInstanceState(), which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
mCurrentScore = savedInstanceState.getInt(PLAYER_SCORE);
mCurrentLevel = savedInstanceState.getInt(PLAYER_LEVEL);
}
Basically onSaveInstanceState(Bundle outBundle) will give you a bundle.
When you look at the Bundle class, you will see that you can put lots of different stuff inside it. At the next call of onCreate(), you just get that Bundle back as an argument.
Then you can read your values again and restore your activity.
Lets say you have an activity with an EditText. The user wrote some text inside it.
After that the system calls your onSaveInstanceState().
You read the text from the EditText and write it into the Bundle via Bundle.putString("edit_text_value", theValue).
Now onCreate is called. You check if the supplied bundle is not null. If thats the case,
you can restore your value via Bundle.getString("edit_text_value") and put it back into your EditText.
This is for extra information.
Imagine this scenario
ActivityA launch ActivityB.
ActivityB launch a new ActivityAPrime by
Intent intent = new Intent(getApplicationContext(), ActivityA.class);
startActivity(intent);
ActivityAPrime has no relationship with ActivityA.
In this case the Bundle in ActivityAPrime.onCreate() will be null.
If ActivityA and ActivityAPrime should be the same activity instead of different activities,
ActivityB should call finish() than using startActivity().
If Data Is not Loaded From savedInstanceState use following code.
The problem is url call is not to complete fully so, check if data is loaded then to show the instanceState value.
//suppose data is not Loaded to savedInstanceState at 1st swipe
if (savedInstanceState == null && !mAlreadyLoaded){
mAlreadyLoaded = true;
GetStoryData();//Url Call
} else {
if (listArray != null) { //Data Array From JsonArray(ListArray)
System.out.println("LocalData " + listArray);
view.findViewById(R.id.progressBar).setVisibility(View.GONE);
}else{
GetStoryData();//Url Call
}
}