Trying to use the FilterQueryProvider function to update the contents of a ListView, as characters are entered into the filter, my runQuery function does get called, and a new cursor with filtered content is returned, but the ListView does not update to show the new contents. The docs state that after runQuery returns, changeCursor is called. Isn't this enough to update the ListView display? My code to set up the filter is:
mAdapter = new ItemAdapter(curs);
mAdapter.setFilterQueryProvider(new ItemFilter());
setListAdapter(mAdapter);
And my filter class is simply:
class ItemFilter implements FilterQueryProvider {
public Cursor runQuery(CharSequence filter) {
return getNewCursor(filter);
}
}
The cursor from getNewCursor does correctly fetch a subset of the rows. Have I missed something in getting the ListView to update?
Related
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);
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
1)Create a CursorAdapter or SimpleCursorAdapter to render a list.
2) Now scroll down the list a bit so you are not at position 0.
3) Now execute a CursorAdapter filter that you have created.
Result:
The filter correctly updates the data and the list view is updated with the new data.
The big problem is that the list position/selection stays at your previous position (if there are enough rows in the new result).
I want to perform a setSelection(0) within my filter to always set position to 0 but don't know how to do it. Any suggestions to the obvious?
dataAdapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
listView.setSelection(0);
return newCursor;
});
The above doesn't work since the setSelection() is made before data is updated, cursor swapped.
Have you tried ListView.setItemChecked(int position, boolean checked) or listView.smoothScrollToPosition(position) (if you are API >= 8)?
Found the answer. There is no way to update the setSelection() via the filter but overriding the changeCursor method of CursorAdapter did the trick!
#Override
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
listView.setSelection(0);
}
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()
I am using a AutoCompleteTextView in my code and loading the list from database using SimpleCursorAdapter.
AutoCompleteTextView cocktailIngredientView = (AutoCompleteTextView) findViewById(R.id.item);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
android.R.layout.simple_spinner_item, mCursor,
new String[] { "field" },
new int[] { android.R.id.text1 });
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
cocktailIngredientView.setAdapter(adapter);
cocktailIngredientView.setThreshold(0);
It populates the list correctly but I have two issues:
I want this list to be sorted
Whatever I enter, it displays the complete list. I want it to filter based on matching patterns in the list. e.g. if the list contains values Page, Tools...then if I enter T in the box, the drop-down should show only Tools. The idea is to display options which contain the entered pattern anywhere in the string text.
How can this be done?
You have to tell the adapter what items to display. I tried implementing something similar to this by using a FilterQueryProvider that queries the database for the items that I want to display in the dropdown.
FilterQueryProvider filter = new FilterQueryProvider() {
#Override
public Cursor runQuery(CharSequence constraint) {
// Make a DB query that filters based on the constraint
return //whatever query results;
}
};
myAdapter.setFilterQueryProvider(filter);
As for the situation when you select an item on the list, you have to override the CursorToStringConverter of the SimpleCursorAdapter. Something like:
SimpleCursorAdapter.CursorToStringConverter conv = new SimpleCursorAdapter.CursorToStringConverter() {
#Override
public CharSequence convertToString(Cursor cursor) {
int numCol = cursor.getColumnIndexOrThrow("whateverFieldYouNeed");
String term = cursor.getString(numCol);
return term;
}
};
myAdapter.setCursorToStringConverter(conv);
Instead of the CursorToStringConverter you could also use
mAdapter.setStringConversionColumn(mCursor.getColumnIndexOrThrow("whateverFieldYouNeed"));