I'm trying to implement a fast scroller with AlphabetIndexer, but when the cursor changes, its not refreshing the index chache. In my CursorAdapter constructor I call setCursor(cursor) but nothing changes, and acording to the documentation:
Your adapter is responsible for updating the cursor by calling
setCursor(Cursor) if the cursor changes. getPositionForSection(int)
method does the binary search for the starting index of a given
section (alphabet).
But nothing is happening. I'm using this for a search filter, so when I search contacts, it updates the list with the contacts, so AlphabetIndexer should work for update the index of the new items in the list.
Example: My whole list starts with contacts that starts with 'A' and ends with contacts that starts with 'E'. So the AlphabetIndexer will have this indexes in its cache.
But, lets try to search contacts with 'C', and lets say I have 250 contacts that starts with 'C'. So, I have to fast scroll through this contacts, and the 'C' index must show, but instead of only 'C' it shows all indexes, that were shown when I had the entire list.
Here is my CursorAdapter constructor where I call setCursor(cursor) for every letter I type:
public MyCursorAdapter(Context context, Cursor cursor, ArrayList<Integer> ids)
{
super(context, cursor);
try
{
mAlphaIndexer = new AlphabetIndexer(cursor, cursor.getColumnIndexOrThrow("Name")," ABCDEFGHIJKLMNOPQRSTUVWXYZ");
notifyDataSetChanged();
mAlphaIndexer.setCursor(cursor);
this.mSelectedContacts = ids;
Log.e("MyCursorAdapter", "construtor: ");
}
catch(Exception ex)
{
Log.e("MyCursorAdapter", "Error: " + ex);
}
finally
{
mAlphaIndexer.setCursor(cursor);
}
}
I've solved this by calling this in sequence after I set the adapter:
listView.setFastScrollEnabled(false);
listView.setFastScrollEnabled(true);
That worked for me.
call setContentView(R.layout.whatever)and then re-populate the ListView with your new adapter / new data items. This will redraw the ListView with your new items and the FastScroll Overlay will appear in the correct place.
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 have Fragment and it uses RecyclerView Adapter with LoaderManager to list items. RecyclerView i use is from this address. I've implemented LoaderManager in Fragment and gave context of its Activity to RecyclerView.
When i add new item to list, it does not refresh and show new item in list. I use same Adapter and structure on another activity and it works good. But here i use RecyclerView with Loader inside Fragment and it does not refresh list when i add item, instead i have to go back(finish fragment) and enter again.
I have 2 tables like this. First I get cursor from student_course table and from that cursor i get student ids of specific class. Then using data of first cursor, i get second cursor from students table which has list of students from same class. Then i send second cursor to adapter. I do below operation on both onCreateView() and onCreateLoader:
String[] projection0 = {
DatabaseOpenHelper.COL_STUDENT_ID,
DatabaseOpenHelper.COL_COURSE_ID};
String selection0=DatabaseOpenHelper.COL_COURSE_ID + "=?";
String selectionArgs0[]={String.valueOf(courseId)};
mDataset0=getActivity().getContentResolver().query(StudentContentProvider.CONTENT_URI2, projection0,selection0,selectionArgs0,null);
if(mDataset0.getCount()!=0) {
//BATCH
List<String> selectionList = new ArrayList<String>();
mDataset0.moveToFirst();
...
String[] projection1 = {
DatabaseOpenHelper.COLUMN_ID,
DatabaseOpenHelper.COL_STUDENT_NUMBER,
DatabaseOpenHelper.COL_STUDENT_NAME,
DatabaseOpenHelper.COL_STUDENT_CARD_ID,
DatabaseOpenHelper.COL_STUDENT_COLOR};
selection1 = selection1.substring(0, selection1.length() - 2) + ")";
mDataset1 = getActivity().getContentResolver().query(StudentContentProvider.CONTENT_URI1, projection1, selection1, selectionArgs1, null);
}else{
mDataset1=null;
}
...
mAdapter = new studentListAdapter(getActivity(),mDataset1,this);
mDataset1 is students of same class.
1) What might be the problem ? 2) Is there any way to implement LoaderManager class inside
Update 1: When i change selection1 and selectionArgs1 to null, it updates list immediately after i add item. But it shows all students from every class because i didn't specify selection.
Update 2: When i check onLoadFinished() i see that new cursor, after loading new content, is still same as old.
1) I realized that cursor is not changing when new data is added, so when i add new data i call getLoaderManager().restartLoader(LOADER_ID,null,this) (by localBrodCastManager in my case) which reloads cursor (calls onCreateLoader()) and new data is shown on list. (as Suggested here). This does trick.
2) I found that the reason cursor does not change when item is added, because on onCreateLoader() i returned cursor with specific id selection of students, which means cursor will not change until there is change on that items with specific ids i have given. So, if there is new item added, my cursoris not changed, but if one of the items(which is listed) is removed or changed, then cursor is changed. Solution is here.
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.
i'm making select statement and it works fine
the problem is selecting more than thousands of records ate one and this cause the program too slow.
is there a possibility to select fifty by fifty and when select the first fifty record show them then add the next fifty record to them.
how can i do that .
thanks in advance ...
Use LIMIT/OFFSET Clauses is selection statement
I haven't worked on that but can give some idea reagarding that. You can use AsynTask here. In the doingInbackground() you can get the records and then you can call publishProgress() when 50 records are fetched and update the UI.
UPDATE:
You can use the LIMIT/OFFSET clause that Kiran said to get the limit of the record fetched and can update the UI using AsyncTask.
Here is the code you need to back your AutoCompleteTextView with a cursor adapter.
#Override
protected void onResume() {
super.onResume();
text = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView1);
final AdapterHelper h = new AdapterHelper(this);
Cursor c = h.getAllResults();
startManagingCursor(c);
String[] from = new String[] { "val" };
int[] to = new int[] { android.R.id.text1 };
CursorAdapter adapter = new MyCursorAdapter(this,
android.R.layout.simple_dropdown_item_1line, c,
from, to);
adapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
if (constraint == null) {
return h.getAllResults();
}
String s = '%' + constraint.toString() + '%';
return h.getAllResults(s);
}
});
text.setAdapter(adapter);
}
class MyCursorAdapter extends SimpleCursorAdapter {
public MyCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
}
public CharSequence convertToString(Cursor cursor) {
return cursor.getString(cursor.getColumnIndex("val"));
}
}
The database that I am using has 3k rows of data in it and the Autocomplete works fine.
The things to note are that you need make sure that the your adapter puts the correct value in the text box once a user selects it. Do this with the convertToString method (at the end of the snippet above). You get to this method by extending SimpleCursorAdapter and overriding the method as shown.
Then you need to provide a FilterQueryProvider to your adapter. This allows your query to be run with the where clause of your typed text. If you have a huge dataset, then setting the threshold large enough (either programatically, or in xml) will prevent the filter query running until it will return a suitably sized resultset.
Hope this is useful.
Anthony Nolan
I have an application with a Spinner whos data is populated by a cursor from the database. Works great if there is something in the database table on startup. However, if I add something to the database after the app is running my spinner always shows that it has zero choices, even if one was there to begin with.
My code is as follows. The adapter.getCursor().requery(); did no good. I would like the Spinner to update its choices when the user clicks on it and I found a couple posts on StackOverflow that say you have to use the TextView behind the Spinner for the OnClickListener. However, for me that did nothing. Mostly likely because I'm missing one minor
c1 = myDbHelper.readCars();
startManagingCursor(c1);
// create an array to specify which fields we want to display
String[] from = new String[]{"nickname"};
// create an array of the display item we want to bind our data to
int[] to = new int[]{android.R.id.text1};
adapter = new SimpleCursorAdapter(this, android.R.layout.simple_spinner_item, c1, from, to);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinVehicle.setAdapter(adapter);
txtVehiclePrompt.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
adapter.getCursor().requery();
}
});
Just change the underlying data and call notifyDataSetChanged()
list.clear();
//list modify
list.add("A");
list.add("B");
dataAdapter.notifyDataSetChanged();
Ok,
I got it figured out. I needed to close the adapter, stop managing the cursor, close the cursor & close the db helper in the onSuspend() method then set everything back up in the onResume method since I was using a seperate activity to add records to the table.