updating listview after inserting row via content provider - android

I am using custom ContentProvider to insert and query the sqlite database in android. when i query the database i am populating data over listview. but the issue is when i insert the row i want to update the listview also.
i have created custom adapter by extending CursorAdapter and listening to LoaderManager.LoaderCallbacks<Cursor> , now when when i am inserting row by context.getContentResolver().insert() function , i want to update the listview.
i have tried adapter.notifyDataSetChanged(); after inserting the row but listview is not getting updated.
the only option i saw is restartLoader() is there anyother wat to update the listview ?
this is my insert code
context.getContentResolver().insert(
ContentUris.withAppendedId(
Uri.withAppendedPath(Columns.CONTENT_URI,
Columns.INSERT_LOGS ),
Columns.LOGS),
values);
i am inserting the logs of application and showing into listview
here is the code inside content provider
if(rowId > 0 ){
Uri insertUri = ContentUris.withAppendedId(Columns.CONTENT_URI, rowId);
Log.e("inserted " , rowId+"");
getContext().getContentResolver().notifyChange(insertUri,null);
return insertUri;
}

I think, you can see how works this example, because it's also based on Loaders.
Please take a look at it too.
I had troubles like you and the problem was how I did.
It was right like this.
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
...
adapter.swap(cursor)
dataAdapter.notifyDataSetChanged();
listView.SetAdapter(dataAdapter);
...
}

Related

Unable to delete pages when using FragmentStatePagerAdapter - What is the usual behavior of row ID's when integer primary key is used?

Please feel free to skip to the question as this background understanding may not be necessary for you.
I am new to android and sqlite and I am designing an app which has a content provider to access an sqlite database with multiple tables. There are several activities which use different adapters to display info from the database to the UI (i.e. cursor adapters, fragment state page adapter and array adapters). I have been having issues with the delete function in all of my activities which don't use the cursor adapter. When I try to update or delete a row from a table it deletes the wrong row or it doesn't delete anything at all. I believe it is a problem with the adapter where I am trying to figure out which row it is to send the correct info to the content provider.
The identical java code works perfectly with the cursor adapter and the rows delete normally and the other CRUD operations work. The insert and query functions work normally for all tables.The provider code uses a switch statement for each table but it is basically identical for each Uri case. All of the tables have _id as the integer primary key which is NOT set to auto increment. Since I don't fully understand how the row id works my java code does not reflect it and I keep having these issues. Although I have read many documents about content providers, adapters, sqlite databases, etc. certain key details are not clear to me.
My question is how does the row id get assigned numbers in the database when it is set to _id column as a primary key and what happens to those numbers when the database is changed?
For example, say I have an empty database. Initially after inserting the first row, the Uri will return a path segment for the 0 row and the adapter position would be 0... what would the row id for the database be (0 or 1) ?
Then for each row I insert, I know that row number would increase by one integer. Say I insert 4 rows - 0,1,2,3. Now when I am ready to delete - should the last path segment on the Uri be one integer less than the row number (i.e do I send a Uri with a last path segment of 2 to delete row 3)? Finally, after deleting, will the row ids then automatically get re-assigned so that row 4 now becomes row 3 ? Is there some code that I need to write to make that happen in the database? The primary keys are not set to auto increment.
I have different adapters and activities to where I can not access the actual database row ID once the data is displayed in the UI, so I am using the adapter position as a surrogate. This is why I am having trouble with update and delete.
Thank you very much if you read this entire question and take the time to answer it, it would help me tremendously.
I have an activity that is tabbed and uses FragmentStatePagerAdapter that is populated by a database. Here is the Adapter that I adjusted to keep track of the rows:
**EDITED:**
public class TankSectionsPagerAdapter extends FragmentStatePagerAdapter {
private ArrayList<Fragment> tankFragments = new ArrayList<>();
private ArrayList<String> tankTitles = new ArrayList<>();
//I added this ArrayList below to store the tankIDs to match the Fragments//
**public ArrayList<Integer> tankIDs = new ArrayList<>();**
public TankSectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return tankFragments.get(position);
}
#Override
public int getCount() {
return tankFragments.size();
}
#Override
public String getPageTitle(int position) {
return tankTitles.get(position);
}
public void addPage(Fragment fragment, String tankName) {
tankFragments.add(fragment);
tankTitles.add(tankName);
// I added this below so the ID position would match each fragment position //
**tankIDs.add(tankId);**
notifyDataSetChanged();
}
// Finally I added this method below to the adapter//
** public ArrayList<Integer> getPageId(){
return tankIDs;
}**
Here is the activity where the method is called and where it pulls the data from the cursor to pass to the Adapter. There is a loop where each row creates a page(tab) in the ViewPager:
public class MyClass extends Tank implements TabLayout.OnTabSelectedListener, LoaderManager.LoaderCallbacks<Cursor> {
public TankSectionsPagerAdapter tankSectionsPagerAdapter;
TabLayout tabLayout;
private ViewPager mViewPager;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_class);
mViewPager = (ViewPager) findViewById(R.id.container);
addPages(mViewPager);
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
tabLayout.setupWithViewPager(mViewPager);
tabLayout.addOnTabSelectedListener(this);
}
public void addPages(ViewPager mViewPager) {
tankSectionsPagerAdapter = new TankSectionsPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(tankSectionsPagerAdapter)
try {
...
Cursor cursor = getContentResolver().query(MyProvider.CONTENT_URI_TABLE_TANK_SETUP, MyDatabaseHelper.ALL_TABLE_TANK_SETUP_COLUMNS, tankDataFilter, null, null);
if (cursor.moveToFirst()) {
do {
tName = cursor.getString(cursor.getColumnIndex(MyDatabaseHelper.TANK_NAME)); ...
// all the variables are stored in the bundle passed to the fragment/
...
**tankSectionsPagerAdapter.addPage(MainTankFragment.newInstance(tankBundle),tName, int tankID);**
tankDataFilter = tankDataFilter + (-1);
}
while (cursor.moveToNext());
cursor.close();
} else {
Toast...
}
} catch (Exception e) {
Toast..
}
}
...
// Get Row ID from cursor(tankID), parameter in addPage() above//
//Get ID's from Adapter //
** ArrayList <Integer> pageID= tankSectionsPagerAdapter.getPageId();**
This is the Activity with Spinner to choose the rows/fragments to edit or delete.
public class EditTank extends Tank implements LoaderManager.LoaderCallbacks<Cursor>
...
// Get the ArrayList//
ArrayList<Integer> IDtags =getIDIntent.getIntegerArrayListExtra("tank_edit_key");
loadEditTankSpinnerData();
////***Here is the Spinner. Use row ID from the ArrayList******
Note: Don't use the id of the spinner
editTankSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view int position, long id) {
*** tankID = IDtags.get(position); ***
}
private void loadEditTankSpinnerData() {
List<String> tankNames = new ArrayList<String>();
Cursor cursor = getContentResolver().query(MyProvider.CONTENT_URI_TABLE_TANK_SETUP, MyDatabaseHelper.TANK_NAMES, null, null,null);
try{
if (cursor.moveToFirst()) {
do {
tankNames.add(cursor.getString(1));
} while (cursor.moveToNext());
cursor.close();
} else {
deleteTankBtn.setEnabled(false);
editTankBtn.setEnabled(false);
Toast...
}
} catch (Exception e) {
Toast...
}
...
}
The above code worked well with CursorAdapter but not with the fragmentStatePagerAdapter (***Prior to the edits it did not work, now it works well and deletes correctly).
I spent days(weeks) on this because I didn't understand why the rows weren't deleting. I hope this helps someone.
Word of advise - Try to write your question as simple as possible also the code. You shouldn't share too much code in here. People will just ignore.
You're using a CursorLoader but not using it properly. Use the LoadFinished method's cursor data.
Then you can directly pass the cursor to your FragmentPageAdapter and use it directly there.
Hope this helps.
Thanks to #pskink, #CL, and #albeee - this is what I learned from you guys and my own research.
In order to delete rows from database which is populating FragmentStatePagerAdapter or ArrayAdapter you have to be able to link the correct row with what is being displayed in the adapter. Otherwise the rows won't delete or will be inconsistent or incorrect. The CursorAdapter will automatically handle the watching for changes and selecting the ID for you. If you can use CursorAdapter or a direct onItemClickListener and get the id directly from the AdapterView with getSelectedId() or just long ID, then that is a good way to get the row ID. However, if you are getting the id indirectly by other means then you have to handle the associations...
1.You should not use the adapter position, spinner position or even spinner id to select the row. These are useful only to determine which item/fragment you want. Only the ID of the OnClickListener on the item itself will consistently give the correct row.
2.The database rows behave as such - the integer primary key will auto increment even if AUTOINCREMENT is not chosen, but the row ID's are stable once assigned. This means that each new row will have a higher ID than the last but if you delete rows in between, it will not change the IDs of the remaining rows. So the rows will skip and not be consecutive when there are edits and deletions. For this reason you must have a method to link the item to the ID permanently. There may be a better way to do this, however, I will edit the code above to show one way that I was able to do it for the FragmentStatePagerAdapter so the user can add and delete fragments dynamically.

ListView frequently hangs when Sqlite database cursor is updated

I have one EditText which is used for the purpose of taking user input. Once the user enters certain data, the text changing listener associated with the EditText calls for a refreshed cursor and tries to update the result which is being displayed in the ListView, placed just below.
Everything is fine. But whenever any change in the search query occurs, the resulting cursor and ListView update takes some time, say around n seconds. During this span of n second, the UI stops (halts/hangs whatever you may call) and does not respond until a refreshed cursor is available and the entire ListView is populated.
When I tried to put the updating of the cursor in a different thread, it did not allow the same to be reflected in the UI as the UI-thread does not allow being commanded by other threads in action. Any UI activity such as the list update has to be implemented through runOnUiThread in the MainActivity class.
Kindly suggest ways by which I can allow the EditText to be modified by the user as well as the updated cursor refreshing the ListView happen without affecting the former.
Basically, you are trying the wrong approach. When we want the data for the list to be sourced directly from an SQLite database query, we can use a CursorAdapter.
Create an adapter
public class MyCursorAdapter extends CursorAdapter {
// Default constructor
public MyCursorAdapter(Context context, Cursor cursor, int flags) {
...
}
public void bindView(View view, Context context, Cursor cursor) {
...
}
public View newView(Context context, Cursor cursor, ViewGroup parent) {
...
return null;
}
}
Get Values from database
Cursor todoCursor = db.rawQuery("SELECT * FROM todo_items", null);
Now, we can use the CursorAdapter in the Activity to display an array of items into the ListView:
// Find ListView to populate
ListView lvItems = (ListView) findViewById(R.id.lvItems);
// Setup cursor adapter using cursor from last step
TodoCursorAdapter todoAdapter = new TodoCursorAdapter(this, todoCursor);
// Attach cursor adapter to the ListView
lvItems.setAdapter(todoAdapter);
This will then trigger the CursorAdapter iterating through the result set and populating the list. We can change the cursor to update the adapter at any time with:
// Switch to new cursor and update contents of ListView
todoAdapter.changeCursor(newCursor);

delete entry from sqlite data base in android application. where to get the that row id to pass it ot delete function

After going through many posts I am posting my query. Not able to get proper resolution for my problem.
I am using sqlite and inserting some data (name, info etc..)
Now I get the all rows and show in list view.
Now user selects a one list entry for deletion, I have to call db.delete with id which is rowid of that particular record.
How will get the record id?
When user selects from list view I have position number which is index to the list. But not the database row id where that entry resides.
Do i have to save all ids returned when I call db.insert(table, data) when rows are created?
all examples show the implementation like
public void deleteRow(long id) {
db.delete(TABLE_NAME,KEY_ID + " = ?",
new String[] { String.valueOf(id) });
}
where should i get id from?
For setup a list view you have create an Adapter, right?
The adapter hold your entries and create the views.
Ask your adapter which item is on position x.
The BaseAdapter implement a getItemId method.
public long getItemId(int position) {
// Example for List<Object>
return mItems.get(position).getId();
}
You can simple call adapter.getItemId(x); or use OnItemClickListener that give the ID.
For more informations loot at the android developer pages.
SimpleAdapter:
http://developer.android.com/reference/android/widget/SimpleAdapter.html
ArrayAdapter:
http://developer.android.com/reference/android/widget/ArrayAdapter.html
BaseAdapter:
http://developer.android.com/reference/android/widget/BaseAdapter.html
I hope it helps.
Updated:
Use getItemId(int) instead of getItem(int), thanks to #pskink

Android ListView Live Updating

I have a ListView in a ListActivity that's bound to some data. I have a content provider that's providing the data.
The ListActivity gets the data by querying a content resolver:
Uri uri = Uri.parse("content://my.provider.DocumentProvider/mystuff");
contentCursor = this.getContentResolver().query(uri, null, null, null, null);
So now the activity has the cursor. It creates an adapter and attaches it to the list:
ListAdapter adapter = new DocumentListCursorAdapter(this, R.layout.main_row_layout, contentCursor, new String[] { "titleColumn" }, new int[] { titleColumnIndex });
setListAdapter(adapter);
This works fine; the list shows the data in the cursor.
But now the content provider has new data. I want the list to update to show the new data.
The examples I've seen involve a call to the adapter's notifyDataSetChanged, but it seems to me that this breaks the separation between the content provider and the list, which is consuming the content.
Does the content provider need to know what adapters are attached to the cursor so it can call their notifyDataSetChanged method? Or is there a better way that doesn't see these two things coupled this way.
I found the answer here:
http://mylifewithandroid.blogspot.com/2008/03/observing-content.html
In a nutshell, the provider calls notifyChange to indicate that the content at the URI has changed:
getContext().getContentResolver().notifyChange(uri, null);
And the ListActivity calls setNotificationUri on the cursor to register that it's interested in receiving notification of changes:
contentCursor.setNotificationUri(getContentResolver(), uri);
(Thanks njzk2 for pointing me in the right direction).
asuming that ContentProvider is yours you should add cursor.setNotificationUri(getContext().getContentResolver(), uri); in your query implementation of CP(before you return cursor) and getContext().getContentResolver().notifyChange(uri, null); in update, insert, delete ... SimpleCursorAdapter which is prolly base of your DocumentListCursorAdapter should take care about refreshing listview.
You mean, you want to update list as per data changed.
For that just try this :
when you get new cursor then just put this code instead set new adapter to list..
adapter.notifyDatasetChanged();
and
listview.invalidate()

Android ListView update with SimpleCursorAdapter

Hey i use a listview for demonstrate entries which are stored in a database. I also have a EditText element and a button which adds the content of the EditText into the Database. To bind the view to the database content i use the SimpleCursorAdapter and following populate function:
private void populate() {
cursor = dbAdapter.getAllItems();
startManagingCursor(cursor);
String[] from = new String[] { DBAdapter.KEY_TASK };
int[] to = new int[] { android.R.id.text1 };
// Now create an array adapter and set it to display using our row
cursorAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, cursor, from, to);
list.setAdapter(cursorAdapter);
}
If i added a new entry, by clicking on the button i want to refresh the listview but this only works with the populate function and not with the common adapter function notifyDataSetChanged();. Do i have a bug or is this the right way to refresh a listview?
Have you seen this, tried the swap cursor method, or tried just simply calling setAdapter() again?
I had a similar issue where I could not get my list to update, and what I did was just create a refreshListView() method. Now you can call this initially from your onCreate(), AND anytime a user adds something to the DB. All it does is re-bind the listview to a cursor. With all the deprecating methods (requery()), and issues with notifyDataSetChanged(), I decided this was the easiest way.
Please refer this link...it works like charm
Update SimpleCursorAdapter while maintaining scroll position in ListView
for dynamic listview on scroll i added new item from database ..
I did mistake here ..
i was assigning new adapter for each time for same simplecursoradapter .
Instead of creating new adapter.
just use
adapter.changecursor(newcursorValue);
adapter.notifydatasetChanged();
lsMedicine1.setSelectionFromTop(lsMedicine1.getLastVisiblePosition()-20, 0);
You need to call swapcursor() before notifyDataSetChanged() on the adapter.

Categories

Resources