In my code, right now, in order to properly refresh a listview I have to re-fetch my database information and recreate the SimpleCursorAdapter.
For example, I have a button inside a listview. When this button is clicked, it removes the entry from the database for the listview item. So all I want to happen is have the item removed from the listview, without having to recreate the adapter.
I've tried changing my global from SimpleCursorAdapter to BaseAdapater (because it extends SimpleCursorAdapater and allows for the notifyDataSetChanged() function to be used), but it still doesn't work.
Here is the code I'm using now (which does work):
Code for global declarations and onCreate():
private RoutinesDataSource datasource;
private SimpleCursorAdapter dataAdapter;
private boolean isEditing = false;
private Toast toast_deleted;
private String[] columns = new String[] { MySQLiteHelper.COLUMN_NAME };
private int[] to;
#SuppressLint("ShowToast")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_routines);
toast_deleted = Toast.makeText(this, "", Toast.LENGTH_SHORT);
datasource = new RoutinesDataSource(this);
datasource.open();
Cursor cursor = datasource.fetchAllRoutines();
to = new int[] { R.id.listitem_routine_name };
dataAdapter = new SimpleCursorAdapter(this, R.layout.listitem_routine, cursor, columns, to, 0);
setListAdapter(dataAdapter);
}
Code for the delete button inside the listview item:
public void onClick(View view) {
ListView l = getListView();
int position = l.getPositionForView(view);
Cursor cursor = ((SimpleCursorAdapter)l.getAdapter()).getCursor();
cursor.moveToPosition(position);
long id = cursor.getLong(cursor.getColumnIndex(MySQLiteHelper.COLUMN_ID));
String name = cursor.getString(cursor.getColumnIndex(MySQLiteHelper.COLUMN_NAME));
switch (view.getId()) {
case R.id.button_routine_delete:
toast_deleted.setText(getString(R.string.toast_routine_deleted));
toast_deleted.show();
datasource.deleteRoutine(id);
onResume();
break;
}
}
Take note of me using onResume().
I know that datasource.deleteRoutine(id) works because when I close the activity and reopen it the list item is gone.
Code for onResume() which shows the list properly with the listview item removed:
#Override
protected void onResume() {
datasource.open();
Cursor cursor = datasource.fetchAllRoutines();
if (isEditing) {
to = new int[] { R.id.listitem_routine_edit_name };
dataAdapter = new SimpleCursorAdapter(this, R.layout.listitem_routine_edit, cursor, columns, to, 0);
setListAdapter(dataAdapter);
}
else {
to = new int[] { R.id.listitem_routine_name };
dataAdapter = new SimpleCursorAdapter(this, R.layout.listitem_routine, cursor, columns, to, 0);
setListAdapter(dataAdapter);
}
super.onResume();
}
I just think its bad practice to recreate the adapter every time I simply want a list item removed that has been removed from the database. Like I said I've tried notifyDataSetChanged with a BaseAdapater and it simply does not work.
Also take note of the isEditing boolean. That is set to true if the edit button is clicked in the action bar, which shows the delete button. This is useful because I also have an edit button which starts an activity when clicked, so when they come back after they are done editing it still shows the buttons for the user.
So anyways, can someone point me out how to refresh the list without having to recreate the adapter - or is what I've done the best method?
The URL in mango's comment to his resolution worked perfectly.
I just changed the code inside onResume() to this:
datasource.open();
Cursor cursor = datasource.fetchAllRoutines();
dataAdapter.changeCursor(cursor);
super.onResume();
Since onResume() is already called after someone adds or edits an item, I figured it wouldn't hurt to call it when the delete button is pressed considering it no longer recreates the adapter, rather simply changes the cursor.
Related
I am having an issue with a listview refresh, I have a refresh function that works on load and when I click a button tied to it. What it will not do is refresh after I add info to the SQLite database and call the refresh function.
//Add item to order Table.
public void addItemToOrder()
{
itemNumberValue = qoItemNumber.getText().toString();
orderQtyValue = qoOrderQty.getText().toString();
itemDescValue = qoItemDesc.getText().toString();
Intent searchItem = new Intent(getApplicationContext(),inputOrder.class);
searchItem.putExtra("itemnumb", itemNumberValue);
searchItem.putExtra("orderqty", orderQtyValue);
searchItem.putExtra("qoitemdesc", itemDescValue);
startActivity(searchItem);
fillData();
}
//Load item table to listview
public void fillData() {
// Get all of the rows from the database and create the item list
mNotesCursor = mDbHelper.fetchAllOrders();
startManagingCursor(mNotesCursor);
// Create an array to specify the fields we want to display in the list (only TITLE)
String[] from = new String[]{HDWDBHelper.KEY_ITEMNUM, HDWDBHelper.KEY_DESC, HDWDBHelper.KEY_QTY};
// and an array of the fields we want to bind those fields to (in this case just text1)
//int[] to = new int[]{R.id.text1};
int[] to = new int[]{R.id.tvItemNum, R.id.tvItemDesc, R.id.tvQty};
// Now create a simple cursor adapter and set it to display
SimpleCursorAdapter notes =
new SimpleCursorAdapter(this, R.layout.itemlist, mNotesCursor, from, to);
notes.notifyDataSetChanged();
setListAdapter(notes);
notes.notifyDataSetChanged();
Log.v("fillData", "In fillData");
}
When I call fillData() at onCreate() or on button click, it works but when I call it in the addItemToOrder() function, nothing happens. The listview still shows previous items listed until I click the refresh button then it shows the new item added.
What better way to show a progress indicator while my listview is not filled with data of database?
I found some examples of how to do this, using assynctask, but in my case, I am using Loader /CursorLoader.
public class TestActivity extends SherlockFragmentActivity implements
LoaderCallbacks<Cursor> {
SimpleCursorAdapter mAdapter;
ListView mListView;
private static final String[] UI_BINDING_FROM = new String[] {
TestDBAdapter.KEY_NAME, TestDBAdapter.KEY_ICON };
private static final int[] UI_BINDING_TO = new int[] { R.id.text, R.id.icon };
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_Test);
mListView = (ListView) findViewById(R.id.listview);
mAdapter = new TestAdapter(getApplicationContext(),
R.layout.list_item_Test, null, UI_BINDING_FROM,
UI_BINDING_TO, 0);
mListView.setAdapter(mAdapter);
getSupportLoaderManager().initLoader(0, null, this);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri uri = ContentProviderTest.CONTENT_URI;
CursorLoader cursorLoader = new CursorLoader(
this,
uri,
null,
null,
null,
null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
A possible solution, would be to create a new class extending ListFragment and use the setListShown() method , but I do not know if it is the best way to solve this issue.
Thanks
Add a small ProgressBar to your layout file, with its visibility set to "gone". Just before you create your CursorLoader, set its visibility to "visible". It will appear. In onLoadFinished, set the ProgressBar visibility to "gone". It will disappear, and your ListView will load.
I'd use the small style progressbar. To learn more about this, see the reference docs for android.widget.ProgressBar.
BTW, visibility is controlled with View.setVisibility.
In addition to the right answer: it is a good idea in onLoadFinished() to have this check:
if (cursor.getCount() > 0) {
getView().findViewById(R.id.loadingPanel).setVisibility(View.GONE);
getView().findViewById(android.R.id.list).setVisibility(View.VISIBLE);
}
Otherwise, in case of relying on your curstomService to fill the db with data, the cursor may be empty and result would be no progress bar neither list with items showing up.
I have a costumCursorAdapter in a listView.
I also have a cursorLoader to swap the costumCursorAdapter when data is changed in the data base. DB data is entered by a user initiated search.
None of these advices worked, Google was no help - nothing stoped the progress bar once set to visible-
i.e. progress_bar.setVisibility(View.Gone);
Eventually, as a simple and 'only for now' sulotion-
I created a global variable to act as a counter int count = 0.
It is reset to 0 with every search (in the onClick method of a button).
In the bindView of the customAdapter, in the bottom of this code block- I initiated the count with count ++.
At the top of the code block I check if the count reached 4. If it is, I hide the progress bar:
if (count==4){
pb.setVisibility(View.GONE);
}
This way, when the customAdapter binds the 4th view,I know it's resonable to stop the progress bar.
I never have less than 4 items on the list.
Hope this helps someone.
I am using a SimpleCrusorAdaptor to display a list of items in a list view. When an item in listview is selected it starts an other activity, this activity changes the puzzle status which is shown in the list view. When this activity terminates and activity containing the list view again becomes active, list view shows the old status,
How can I ensure if the activity containing list view resumes, the cursor adapter updated the values ? Some thing to be done in onResume() of the cativity ?
private static String[] FROM = { PuzzleDatabase.KEY_PUZZLE_TITLE,
PuzzleDatabase.KEY_PUZZLE_STATUS };
private static int[] TO = { R.id.puzzle_title, R.id.puzzle_status };
ListView listView = (ListView) findViewById(R.id.puzzle_list);
this.cursor = ps.puzzleDatabase.getPuzzleTitles();
// Set up data binding
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.puzzle_list_row, cursor, FROM, TO);
// Assign adapter to ListView
listView.setAdapter(adapter);
for the updating values try doing this
class YourClass extends Activity{
//other members
private Bundle savedInstanceState; //add this to your code
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.savedInstanceState = savedInstanceState; //add this to your code
//your other code here
}
#Override
protected void onResume() { //add this function to your code
datasource.open(); //change datasource to your own database class's object
super.onResume();
onCreate(savedInstanceState);
}
}
The cursor doesn't change its contents, just because the underlying dataset changes its contents! The cursor contains the data that resulted from the query, at the time the query was made.
You need a Loader. You can see find example code here:
https://github.com/marakana/yamba/blob/yambaII/Yamba/src/com/marakana/android/yamba/TimelineActivity.java
You must, first, initialize the loaderManager. Next hand it a loader when it calls you back. Finally, you must swap the loader into the adapter when you it calls you back after the loader has run.
Of course, you have to notify the cursor that it is out of date, too. There is example code for that, here:
https://github.com/marakana/yamba/blob/yambaII/YambaService/src/com/marakana/android/yamba/svc/data/YambaProvider.java
See, e.g., line 182
ListView is populating with the items from the Cursor wich I pass in the SimpleCursorAdapter, but each time I open the application these items are re-added to the listview increasing it continously. When I use SimpleAdapter, i do something like this:
static final ArrayList<HashMap<String, String>> foobar = new
ArrayList<HashMap<String,String>>();
SimpleAdapter adapter = new SimpleAdapter(this, foobar, R.layout.list_item, String[] from, int[] to);
setListAdapter(adapter);
Doing next, solve my problem:
#Override
public void onDestroy(){
super.onDestroy();
foobar.removeAll(foobar);
}
But now, I don't want to delete the database content,so how to solve it if I have a SimpleCursorAdapter? like this one:
> SimpleCursorAdapter myadapter = new SimpleCursorAdapter(this, R.layout.list_item, cursor, String[] from, int[] to);
I have tried setListAdapter(null) or cursor.close(),and many others, but no efect...
Now, these hapen when I exit the application using "back" button of the device. If I press "home" button, when I came back I have the same number of items.So the list is duplicating every time I exit with "back" button.
Solved thanks to Kaediil's answer.The commented lines is what I have improved. The hole class:
public class DataBaseActivity extends ListActivity {
DataBaseMethods dbmet; //is the class that handle database and auxiliar methods working on it
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list);
dbmet = new DataBaseMethods(this);
Cursor mycursor= dbmet.getItems(); // 1. add new cursor
try{
if(!mycursor.moveToFirst()){ //2.check if there are items in the database
dbmet.addItems("Daniel","Son","Karate-kid");
dbmet.addItems("Silv", "Stalone", "Terminator");
dbmet.addItems("foo", "bar", "buz");
} //
showDatabaseContent();
}
finally{
dbmet.close();
}
}
public void showDatabaseContent(){
DataBaseMethods dbmet = new DataBaseMethods(this);
try{
Cursor cursor = dbmet.getItems();
SimpleCursorAdapter myadapter = new SimpleCursorAdapter(this, R.layout.item_list, cursor, dbmet.FROM, TO);
setListAdapter(myadapter);
}
finally{
dbmet.close();
}
}
Umm, this line is suspect:
If does it matter, I'm populating the database in this activity's onCreate(), but I have tried to populate it also from other activity and I get the same behaviour for the listview.
In the populating of the database call do you check to see if the database is already populated?
It sounds like you just keep adding more copies to the DB that the cursor is pointing to.
public void showDatabaseContent(){
DataBaseMethods dbmet = new DataBaseMethods(this);
try{
Cursor cursor = dbmet.getItems();
SimpleCursorAdapter myadapter = new SimpleCursorAdapter(this, R.layout.item_list, cursor, dbmet.FROM, TO);
setListAdapter(myadapter);
-->> myadapter.notifyDataSetChanged(); <<----
}
finally{
dbmet.close();
}
}
You need to call notifyDataSetChanged() to signal the list to invalidate itself ONLY with the new data.
NOTE:
If the issue is from the Database class. Make sure you are not reinserting items in the database every time you start the application. I might mixed your problem, wither the items are duplicate in the listview or in the database. I'm deeply sure that you are reinserting the data every time you run the app.
I'm having a problem populating my spinner with data from my SQLite database. Here's the code from my Activity. The Activity crashes with an Unable to start Activity ComponentInfo error where indicated with an arrow.
public class ProjectsActivity extends Activity {
private ReelDbAdapter dbHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.projects_select);
fillProjectSpinner();
}
private void fillProjectSpinner(){
// initialize cursor to manage data binding to spinner
Cursor projectCursor = null;
Spinner spnExistingProjects = (Spinner)findViewById(R.id.spnExistingProject);
---> projectCursor = dbHelper.getExistingProjects();
//startManagingCursor(projectCursor);
/*
//get the list of project names from the database
String[] from = new String[] {dbHelper.clmProjectName};
//add a new item to the spinner for each of the rows in the database
int [] to = new int[]{R.id.txtViewProjectRow};
//initialize a cursor adapter (similar to ArrayAdapter when populating a spinner from a pre-defined array)
SimpleCursorAdapter projectAdapter = new SimpleCursorAdapter(this, R.layout.view_project_row, projectCursor, from, to);
//add all the rows to the spinner
spnExistingProjects.setAdapter(projectAdapter);
*/
}
Here's the code from the getExistingProjects method from my dbAdapter
public Cursor getExistingProjects() {
if(mDb == null)
{
this.open();
}
return mDb.query(dbTableProject, new String[] {clmProjectName, clmProjectShootingTitle, clmProjectJobNumber},
null, null, null, null, null);
}
Any clues on what I might be doing wrong?
TIA for any help.
Norm
Why don't you try making sure the query is returning something before returning the cursor in your method? Put a log line in that spits out the count of the cursor. Also, you should be able to see this easily while stepping through with the debugger.
Also, why assign null to the cursor's deceleration when you're just going to initialize it a few lines down. Do it all in one line.
Lastly, what db are you trying to one with that this.open() line? I obviously can't tell with just the code you've posted, but put a try catch around that whole thing and spit out the strackTrace. You should see your issue.