INTRODUCTION
I've got an app which consists on an Acitivity where I create some elements and then I save this elements on a ListFragment. This stuff works fine, but the problem comes when I try to delete an element from the list.
Briefly, the listFragment doesn't refresh when I delete an element. If I go back to main Activity and then I enter again to the ListFragment, then the element that I deleted doesn't appear, but the thing would be to refresh this list at the moment I delete an element.
Have to say that I'm a bit confused because at first, it was doing this right, but I don't know what I have touched that now does not do it.
CODE
This are relevant code snippets of ListFragment:
public class MyPlacesListFragment extends ListFragment {
//...
final class PlacesCursorAdapter extends ResourceCursorAdapter {
//...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getAdapter();
}
public void getAdapter() {
Cursor c = getActivity().getContentResolver().query(PlacesProvider.CONTENT_URI, PROJECTION, null, null, null);
mCursorAdapter = new PlacesCursorAdapter(getActivity(), c);
setListAdapter(mCursorAdapter);
}
private void deleteItem(long id){
Uri uri = ContentUris.withAppendedId(PlacesProvider.CONTENT_URI, id);
getActivity().getContentResolver().delete(uri, null, null);
}
I have to say that I work with dataBase and ContentProvider, but these work fine, I've tested them with other apps.
Also, I call notifyChange() on the Insert, Update, and Delete methods of the Provider this way:
getContext().getContentResolver().notifyChange(uri, null);
Just call this:
mCursorAdapter.notifyDataSetChanged();
After you made changes to your underlying data.
Also, I'd suggest renaming your getAdapter() method, as it has a misleading name IMHO. I'd expect it to return always the same adapter, while you seem to use it to initialize a new adapter.
Related
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.
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 have an app which does the following: an Activity with a ListView displays three items: Beginning, Intermediate, Advanced. Depending on the user's choice, the app should go to a Fragment with a ListView which displays all items from the underlying SQLiteDatabase where Level=Beginning or Intermediate or Advanced. This second step is not working for me. I've verified through the debugger that I have data in my database, so that's not the problem. Here's my code in the initial Activity - this is from the onItemClick method:
final String[] values = new String[] {
"Beginning", "Intermediate", "Advanced"
};
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
Intent choice = new Intent(getApplicationContext(), com.MyKnitCards.project.StitchList.class);
Bundle dataBundle = new Bundle();
String chosenValue = values[position];
dataBundle.putString("Level",chosenValue);
choice.putExtras(dataBundle);
try {
startActivity(choice);
}
}
Here's the StitchList class, which is the Fragment class, and which is referenced in the code above:
public class StitchList extends FragmentActivity {
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.stitchfragment);
}
}
Here's the XML file for the Fragment, called stitchfragment.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<fragment android:id="#+id/frag_stitchlist"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_marginTop="?android:attr/actionBarSize"
class="com.MyKnitCards.project.ListFrag" />
<fragment android:id="#+id/frag_stitchdetail"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.MyKnitCards.project.DetailFrag" />
</LinearLayout>
Here's the ListFrag class. I'm using the Support Library and a CursorLoader with a ContentProvider, called StitchProvider; the SQLiteDatabase is called SQLData:
public class ListFrag<Cursor> extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String STITCHTABLE_BASEPATH = "MyStitches_tbl";
private static final String AUTHORITY = "com.MyKnitCards.project.SQLData";
public static final Uri STITCHES_URI = Uri.parse("content://" + AUTHORITY + "/" + STITCHTABLE_BASEPATH);
private static final String[] PROJECTION = new String[] { "_id", "stitchname" };
private SimpleCursorAdapter mAdapter;
private static final int LOADER_ID = 0;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Intent myData = getActivity().getIntent();
Bundle info = myData.getExtras();
String[] dataColumns = { "stitchname", "_id" };
int[] viewIDs = { R.id.frag_stitchlist };
mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_1, null, dataColumns, viewIDs, 0);
setListAdapter(mAdapter);
getLoaderManager().initLoader(0, info, (LoaderCallbacks<Cursor>) this);
}
public void onListItemClick(ListView l, View v, int position, long id) {
String item = (String) getListAdapter().getItem(position);
DetailFrag frag = (DetailFrag) getFragmentManager().findFragmentById(R.id.frag_stitchdetail);
if (frag != null && frag.isInLayout()) {
frag.setText(item);
}
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String selection = "stitchlevel='" + args.getString("Level") + "'";
return (Loader<Cursor>) new CursorLoader(getActivity(), STITCHES_URI,
PROJECTION, selection, null, null);
}
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
switch (loader.getId()) {
case LOADER_ID:
mAdapter.swapCursor((android.database.Cursor) cursor);
break;
}
}
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
The onListItemClick method is for displaying data in the details Fragment, but I'm never getting this far. The list of items retrieved from the database based on the data in the Intent is never displayed.
Some possible issues/questions come to mind:
1) Is the syntax of my query in the onCreateLoader method correct? It should read, "stitchlevel='Beginning'", for example. I know the data in args.getString("Level") is valid because I've followed it through in the debugger, but is the syntax correct here? I'm assuming it should be standard SQL syntax, but...
2) In the assignment of value to viewIDs in the Fragment's onActivityCreated method, am I pointing to the correct place?
3) Similarly, in the next line, the SimpleCursorAdapter, is android.R.layout.simple_list_item_1 correct here? Or should it perhaps be R.layout.stitchfragment?
4) I put a breakpoint on the ContentProvider's query method, I never hit the breakpoint. Shouldn't I when the CursorLoader gets created? Furthermore, if I put a breakpoint on the List Fragment's onLoadFinished method, the cursor is null. What am I missing
5) I was told that dataColumns in the Fragment's onActivityCreated method should contain my table's _id column. Is that true? It seems odd to me, since the list will only display one piece of information: the stitchname. However, whether _id is included or not here, the Fragment list does not display anything
6) Is it proper for me to pass the Bundle, info, to the CursorLoader through getLoaderManager.initLoader, as I do in the Fragment's onActivityCreatedMethod?
7) I've seen examples of setListAdapter being called before getLoaderManager().initLoader() in the Fragment's onActivityCreated method and I've seen examples where setListAdapter is called after. Which is correct? Neither seems to help me....
I have been able to get a Fragment to display if I simply do this:
Intent myData = getActivity().getIntent();
Bundle info = myData.getExtras();
String level = info.getString("Level");
String[] values = new String[] {level, level};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, values);
setListAdapter(adapter);
So I know I can display data in the Fragment; it just doesn't seem to be working with the CursorLoader and ContentProvider. I hope this isn't information overload; my apologies if it is. If anyone has any ideas, I'd be most grateful.
1) Is the syntax of my query in the onCreateLoader method correct? It
should read, "stitchlevel='Beginning'", for example. I know the data
in args.getString("Level") is valid because I've followed it through
in the debugger, but is the syntax correct here? I'm assuming it
should be standard SQL syntax, but
Yes, it's correct. Even better would be:
String selection = "stitchlevel= ?";
String selectionArgs = new String[] {args.getString("Level")};
return new CursorLoader(getActivity(), STITCHES_URI, PROJECTION, selection, selectionArgs, null);
2) In the assignment of value to viewIDs in the Fragment's
onActivityCreated method, am I pointing to the correct place? 3)
Similarly, in the next line, the SimpleCursorAdapter, is
android.R.layout.simple_list_item_1 correct here? Or should it perhaps
be R.layout.stitchfragment?
No. The int array that you supply to the adapter represents the ids from the row layout that is used in the adapter and not the id of the fragment like you wrote it. If you use the android.R.layout.simple_list_item_1(which consists of only a TextView) than you have to provide the id of that TextView, which is android.R.id.text1:
String[] dataColumns = { "stitchname"};
int[] viewIDs = { android.R.id.text1 };
mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_1, null, dataColumns, viewIDs, 0);
4) I put a breakpoint on the ContentProvider's query method, I never
hit the breakpoint. Shouldn't I when the CursorLoader gets created?
Furthermore, if I put a breakpoint on the List Fragment's
onLoadFinished method, the cursor is null. What am I missing
I think you didn't do what I recommended you on your last question. The provider's query method should have been at least called and this not happening just leaves one option. I've noticed that you cast the Loaders and you're missing the Override annotation on the callback methods from LoaderManager.LoaderCalbbacks. Add the Override annotation to those methods:
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String selection = "stitchlevel= ?";
String selectionArgs = new String[] {args.getString("Level")};
return new CursorLoader(getActivity(), STITCHES_URI,
PROJECTION, selection, selectionArgs, null);
}
and see what happens. If you get errors make sure you used the correct imports and either only use the classes from the compatibility package or the normal ones(you could easily delete all imports from the file and then re-import everything and use the classes with android.support.v4). Just a wild guess.
5) I was told that dataColumns in the Fragment's onActivityCreated
method should contain my table's _id column. Is that true? It seems
odd to me, since the list will only display one piece of information:
the stitchname. However, whether _id is included or not here, the
Fragment list does not display anything
Cursor based adapters require that the used Cursor must contain a column named _id(see the documentation of the CursorAdapter class, the super class of SimpleCursorAdapter). It doesn't need to be present in the String array which is set into the adapter.
6) Is it proper for me to pass the Bundle, info, to the CursorLoader
through getLoaderManager.initLoader, as I do in the Fragment's
onActivityCreatedMethod?
Yes. But regarding the id, be consistent, if you're going to use LOADER_ID use it everywhere and not just in some places in your code. Or even better, if you're going to use just one Loader just use 0 everywhere(so there is no need to test for the LOADER_ID in the onLoadFinished method).
7) I've seen examples of setListAdapter being called before
getLoaderManager().initLoader() in the Fragment's onActivityCreated
method and I've seen examples where setListAdapter is called after.
Which is correct?
Both can be used. I would set the adapter and then initialize the Loader.
I have a ListView which adapts data from a cursor. The cursor's position of the record is stored via View.setTag(), so it can be retrieved in response to user events.
public class OrderActivity extends Activity {
private ListView list;
private CursorAdapter adapter;
private SQLiteDatabase database;
public void onResume() {
database = new Database(this).getWriteableDatabase();
// query the db and store the position of the cursor
// as the tag while binding the view in the overridden
// CursorAdapter.bindView()
adapter = new SimpleCursorAdapter( /* code here */ );
list.setAdapter(adapter);
}
public void onPause() {
adapter.changeCursor(null);
database.close();
}
private void onClickRowDumpOrder(View row) {
int newPosition = (Integer) row.getTag();
Cursor cursor = adapter.getCursor();
int originalPosition = cursor.getPosition(); // Throws NPE
cursor.moveToPosition(newPosition);
Log.v("tag", "Description: " + cursor.getString(0));
// restore the cursor
cursor.moveToPosition(originalPosition);
}
}
I store the database and the adapter in instance fields of my Activity to allocate/free resources during the activity lifecycle: I create a new database onResume and close it onPause. Unfortunately, I receive lots of reports from my users that a NPE is thrown in the line outlined in the pseudocode above, but I'm not able to reproduce it.
It seems that cursor is null, but I wonder how this is possible if the method onClickRowDumpOrder can only be called after onResume, since it's the callback to the click event (I set this in the XML layout via android:onClick)
Am I doing something wrong? What API misusage causes the cursor to be null? Is there some documentation describing how cursors and adapters are intended to fit in the activity lifecycle?
UPDATE
I got rid of android:onClick in my XML file and manually set the listener inside SimpleCursorAdapter.bindView. To avoid leaks, I remove the listener either in my custom AbsListView.RecycleListener and in my activity's onPause (I retrieve all views with reclaimViews(List<View>)). This seems to fix the bug, and here is my explanation.
A new View is inflated when my activity first starts
instance#1 of my activity is set as the OnClickListener for that particular View in the View's constructor, when it parses the android:onClick attribute
instance#1 leaves the foreground, thus onPause() sets the cursor to null. Note that this activity is still the listener for the View, because neither the view nor the activity are marked for garbage collection. This means that they are referenced in some cache in the Android classes
instance#2 of my activity is created, and its list view somewhat recycle the already created View. The data is shown correctly, but this view still has the old activity (with a null cursor) as the listener
when user clicks my view, instance#1 handler is called, but it has a null cursor. This causes the NPE
This explanation is realistic, however I didn't find relevant code in Android classes (there is a cache in AbsListView.RecycleBin, but the ListView itself is not reused). Moreover, I've never been able to reproduce this bug, and I solely think my fix works because in the last two days I received no reports (usually I get a few dozens a day)
Are you aware of any code in the Android stack which can validate my assumptions?
you can try this way.
if (cursor.moveToFirst()) {
for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToPosition(i);
}
}
I think you are creating the cursor at the wrong point in the Activity lifecycle. The ListView sample puts in onCreate() what you have in onResume(). I don't know that it's necessarily harmful, but you may be recreating some things that don't need to be.
I'm trying to update a view displayed by a CursorAdapter just after its data modification.
Each row contains an image. When I touch this image, the database is update and I want to modify the image regarding this data update.
Except a requery on the cursor i didn't find a solution...My database is really big and a requery is too heavy. Do you have a better solution ?
Yes, sometimes requery is not the best solutions. What I usually do is creating helping methods in the database adapter to retrieve pieces of code. This is an excerpt of one of my adapters:
#Override
public void bindView(View view, final Context context, Cursor cursor) {
String name = cursor.getString(cursor.getColumnIndex("name"));
long id = cursor.getLong(cursor.getColumnIndex("_id"));
((TextView)view.findViewById(R.id.name)).setText(name);
Button btnFavorite = (Button) view.findViewById(R.id.item_add_favorite);
btnFavorite.setTag(id);
btnFavorite.setOnClickListener(mFavoriteCliked);
}
What I do is basically set a click listener to the image... and make sure it takes care of change its state properly:
private OnClickListener mFavoriteCliked = new OnClickListener() {
#Override
public void onClick(View v) {
// change the image of the v object
}
};