Ich versuche asynchron einen Provider abzufragen, indem ein CursorLoader
mit einem SimpleCursorTreeAdapter
SimpleCursorTreeAdapter und CursorLoader für ExpandableListView
Hier verwenden ist meine Fragment
Klasse, die die CursorLoader
public class GroupsListFragment extends ExpandableListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
private final String DEBUG_TAG = getClass().getSimpleName().toString();
private static final String[] CONTACTS_PROJECTION = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME };
private static final String[] GROUPS_SUMMARY_PROJECTION = new String[] {
ContactsContract.Groups.TITLE, ContactsContract.Groups._ID,
ContactsContract.Groups.SUMMARY_COUNT,
ContactsContract.Groups.ACCOUNT_NAME,
ContactsContract.Groups.ACCOUNT_TYPE,
ContactsContract.Groups.DATA_SET };
GroupsAdapter mAdapter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
populateContactList();
getLoaderManager().initLoader(-1, null, this);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created.
Log.d(DEBUG_TAG, "onCreateLoader for loader_id " + id);
CursorLoader cl;
if (id != -1) {
// child cursor
Uri contactsUri = ContactsContract.Data.CONTENT_URI;
String selection = "(("
+ ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
+ " NOTNULL) AND ("
+ ContactsContract.CommonDataKinds.GroupMembership.HAS_PHONE_NUMBER
+ "=1) AND ("
+ ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
+ " != '') AND ("
+ ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID
+ " = ?))";
String sortOrder = ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
+ " COLLATE LOCALIZED ASC";
String[] selectionArgs = new String[] { String.valueOf(id) };
cl = new CursorLoader(getActivity(), contactsUri,
CONTACTS_PROJECTION, selection, selectionArgs, sortOrder);
} else {
// group cursor
Uri groupsUri = ContactsContract.Groups.CONTENT_SUMMARY_URI;
String selection = "((" + ContactsContract.Groups.TITLE
+ " NOTNULL) AND (" + ContactsContract.Groups.TITLE
+ " != ''))";
String sortOrder = ContactsContract.Groups.TITLE
+ " COLLATE LOCALIZED ASC";
cl = new CursorLoader(getActivity(), groupsUri,
GROUPS_SUMMARY_PROJECTION, selection, null, sortOrder);
}
return cl;
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in.
int id = loader.getId();
Log.d(DEBUG_TAG, "onLoadFinished() for loader_id " + id);
if (id != -1) {
// child cursor
if (!data.isClosed()) {
Log.d(DEBUG_TAG, "data.getCount() " + data.getCount());
try {
mAdapter.setChildrenCursor(id, data);
} catch (NullPointerException e) {
Log.w("DEBUG","Adapter expired, try again on the next query: "
+ e.getMessage());
}
}
} else {
mAdapter.setGroupCursor(data);
}
}
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// is about to be closed.
int id = loader.getId();
Log.d(DEBUG_TAG, "onLoaderReset() for loader_id " + id);
if (id != -1) {
// child cursor
try {
mAdapter.setChildrenCursor(id, null);
} catch (NullPointerException e) {
Log.w("TAG", "Adapter expired, try again on the next query: "
+ e.getMessage());
}
} else {
mAdapter.setGroupCursor(null);
}
}
/**
* Populate the contact list
*/
private void populateContactList() {
// Set up our adapter
mAdapter = new GroupsAdapter(getActivity(),this,
android.R.layout.simple_expandable_list_item_1,
android.R.layout.simple_expandable_list_item_1,
new String[] { ContactsContract.Groups.TITLE }, // Name for group layouts
new int[] { android.R.id.text1 },
new String[] { ContactsContract.Contacts.DISPLAY_NAME }, // Name for child layouts
new int[] { android.R.id.text1 });
setListAdapter(mAdapter);
}
}
implementiert Und hier ist mein Adapter, die Unterklassen SimpleCursorTreeAdapter
public class GroupsAdapter extends SimpleCursorTreeAdapter {
private final String DEBUG_TAG = getClass().getSimpleName().toString();
private ContactManager mActivity;
private GroupsListFragment mFragment;
// Note that the constructor does not take a Cursor. This is done to avoid
// querying the database on the main thread.
public GroupsAdapter(Context context, GroupsListFragment glf,
int groupLayout, int childLayout, String[] groupFrom,
int[] groupTo, String[] childrenFrom, int[] childrenTo) {
super(context, null, groupLayout, groupFrom, groupTo, childLayout,
childrenFrom, childrenTo);
mActivity = (ContactManager) context;
mFragment = glf;
}
@Override
protected Cursor getChildrenCursor(Cursor groupCursor) {
// Given the group, we return a cursor for all the children within that group
int groupId = groupCursor.getInt(groupCursor
.getColumnIndex(ContactsContract.Groups._ID));
Log.d(DEBUG_TAG, "getChildrenCursor() for groupId " + groupId);
Loader loader = mActivity.getLoaderManager().getLoader(groupId);
if (loader != null && loader.isReset()) {
mActivity.getLoaderManager().restartLoader(groupId, null, mFragment);
} else {
mActivity.getLoaderManager().initLoader(groupId, null, mFragment);
}
}
}
Die Problem ist, dass wenn ich eine der Elterngruppen klicken Sie auf eine der drei Dinge passiert, in eine scheinbar im Widerspruch Mode sein.
1) Entweder öffnet sich die Gruppe und die Kinder erscheinen darunter
2) Die Gruppe wird nicht geöffnet und die setChildrenCursor()
Aufruf wirft einen NullPointerException
Fehler, der in der try catch-Block verfängt
3) die Gruppe wird nicht geöffnet und kein Fehler
geworfenHier wird einige Debugging-Ausgabe in einem Szenario, in dem eine Gruppe erweitert wird und die Kinder zeigt:
Wenn alle Gruppen sind disp es ouputs gelegt:
05-20 10:08:22.765: D/GroupsListFragment(22132): onCreateLoader for loader_id -1
05-20 10:08:23.613: D/GroupsListFragment(22132): onLoadFinished() for loader_id -1
-1 die loader_id der Gruppe Cursor
Dann, wenn ich insbesondere eine Gruppe wählen (nennen wir es nur Gruppe A) gibt sie:
05-20 23:22:31.140: D/GroupsAdapter(13844): getChildrenCursor() for groupId 67
05-20 23:22:31.140: D/GroupsListFragment(13844): onCreateLoader for loader_id 67
05-20 23:22:31.254: D/GroupsListFragment(13844): onLoadFinished() for loader_id 67
05-20 23:22:31.254: D/GroupsListFragment(13844): data.getCount() 4
05-20 23:22:31.254: W/GroupsListFragment(13844): Adapter expired, try again on the next query: null
Die Gruppe wird nicht erweitert und die NullPointerException
wird abgefangen. Dann, wenn ich eine andere Gruppe auswählen (nennen wir es einfach Gruppe B) gibt sie:
05-20 23:25:38.089: D/GroupsAdapter(13844): getChildrenCursor() for groupId 3
05-20 23:25:38.089: D/GroupsListFragment(13844): onCreateLoader for loader_id 3
05-20 23:25:38.207: D/GroupsListFragment(13844): onLoadFinished() for loader_id 3
05-20 23:25:38.207: D/GroupsListFragment(13844): data.getCount() 6
Dieses Mal wird die NullPointerException
nicht geworfen. Und anstatt Gruppe B zu erweitern, wird Gruppe A erweitert.
Kann jemand das Verhalten erklären, dass der setChildrenCursor()
Anruf zeigt, wird?
Ich denke es gibt ein Problem mit, wie die Gruppe/Kind CursorLoaders in onCreateLoader()
instanziiert werden. Für die Gruppe CursorLoader
Ich möchte nur alle Gruppen in meinem Handy. Das Kind CursorLoader
sollte alle Kontakte innerhalb einer Gruppe enthalten. Hat jemand irgendwelche Ideen, was könnte das Problem sein?
UPDATE
Dank @ Yam Rat habe ich jetzt die getChildrenCursor()
Verfahren modifiziert. Ich Auswahl jetzt die groupCursor Position nicht den Wert von ContactsContract.Groups._ID in die initLoader() Aufruf zu übergeben. Ich habe auch die Logik nur restartLoader() aufrufen, wenn Lader nicht null ist und Lader isReset ist falsch.
protected Cursor getChildrenCursor(Cursor groupCursor) {
// Given the group, we return a cursor for all the children within that
// group
int groupPos = groupCursor.getPosition();
Log.d(DEBUG_TAG, "getChildrenCursor() for groupPos " + groupPos);
Loader loader = mActivity.getLoaderManager().getLoader(groupPos);
if (loader != null && !loader.isReset()) {
mActivity.getLoaderManager().restartLoader(groupPos, null, mFragment);
} else {
mActivity.getLoaderManager().initLoader(groupPos, null, mFragment);
}
return null;
}
Dies macht definitiv mehr Sinn und zeigt nicht das erratische Verhalten einer Gruppe, die sich manchmal und nicht in anderen Zeiten ausdehnt.
Es gibt jedoch Kontakte, die unter einer Gruppe angezeigt werden, zu der sie nicht gehören. Und auch einige Gruppen, die Kontakte haben, aber keine Kontakte zeigen. So scheint es, dass die getChildrenCursor()
Probleme jetzt gelöst werden können.
Aber jetzt scheint es ein Problem zu sein, wie die CursorLoaders in der onCreateLoader()
Methode instanziiert werden. Wird CursorLoader
in der onCreateLoader()
-Methode für den Child-Cursor zurückgegeben, der nicht ordnungsgemäß instanziiert wird?
UPDATE
So habe ich eines meiner Probleme identifiziert. In der getChildrenCursor()
Methode, wenn ich die GroupId in die initLoader()
Methode dann in der onCreateLoader()
Methode übergeben, wenn die CursorLoader
erstellt wird, wird es den richtigen GroupID-Parameter für die Abfrage erhalten. In der onLoadFinished()
wird jedoch der Aufruf an setChildrenCursor()
die Loader-ID für den ersten Parameter nicht die GroupPosition übergeben. Ich denke, ich muss Loader IDs Gruppenpositionen in einigen Datenstruktur zuordnen. Aber ich bin mir nicht sicher, ob dies der beste Ansatz ist. Hat jemand irgendwelche Vorschläge?
Ich habe gerade dies getan, aber es war nicht mit einem CursorLoader, so dass mich wirft ...In meiner Implementation gibt der getChildrenCursor einen Cursor zurück. Mit dem Lademanager, wo gehen die Cursor/Daten eigentlich hin? Wenn Sie keinen Cursor in den Konstruktor eingeben, was wird in 'getChildrenCursor' als groupCursor eingegeben? – Barak
Der groupCursor wurde in der onLoadFinished() - Methode des LoaderManagers festgelegt. Ich bin mit einem Debugger durch den Code gegangen und in der Methode getChildenCursor() ist der groupCursor immer definiert. – toobsco42
Ich weiß nicht, es klingt wirklich wie Sie nicht immer ein bevölkertes Kind bekommenCursor ... – Barak