Tôi cố gắng để đồng bộ truy vấn một nhà cung cấp bằng cách sử dụng một CursorLoader
với một SimpleCursorTreeAdapter
SimpleCursorTreeAdapter và CursorLoader cho ExpandableListView
Đây là lớp học của tôi Fragment
mà thực hiện các 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);
}
}
Và đây là bộ chuyển đổi của tôi mà lớp con 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);
}
}
}
vấn đề là khi tôi nhấp vào một trong các nhóm phụ huynh, một trong ba điều xảy ra trong những gì dường như là một thời trang không phù hợp.
1) Hoặc là nhóm mở ra và những đứa trẻ xuất hiện bên dưới nó
2) Nhóm này không mở và cuộc gọi setChildrenCursor()
ném một lỗi NullPointerException
mà bị kẹt trong khối try catch
3) nhóm này không mở và không có lỗi được ném
Dưới đây là một số kết quả gỡ lỗi trong một tình huống trong đó một nhóm được mở rộng và hiển thị các trẻ em:
Khi tất cả các nhóm disp xếp chồng nó ouputs:
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 là loader_id của con trỏ nhóm
Sau đó, nếu tôi chọn một nhóm đặc biệt (chúng ta hãy chỉ gọi nó là nhóm A) nó ra:
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
Nhóm không mở rộng và bị bắt giữ NullPointerException
. Sau đó, nếu tôi chọn một nhóm khác (chúng ta hãy chỉ gọi nó là nhóm B) nó ra:
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
Thời gian này, NullPointerException
không ném. Và thay vì nhóm B mở rộng, nhóm A được mở rộng.
Mọi người có thể giải thích hành vi mà cuộc gọi setChildrenCursor()
đang diễn ra không?
Tôi nghĩ có vấn đề với cách các trình tải con trỏ nhóm/con được khởi tạo trong onCreateLoader()
. Đối với nhóm CursorLoader
tôi chỉ muốn tất cả các nhóm trong điện thoại của tôi. Đứa trẻ CursorLoader
phải chứa tất cả các liên hệ trong một nhóm. Có ai có bất kỳ ý tưởng gì có thể là vấn đề?
CẬP NHẬT
Nhờ @ lời khuyên Yam của tôi giờ đây đã sửa đổi phương pháp getChildrenCursor()
. Bây giờ tôi đang chọn vị trí groupCursor không phải là giá trị của ContactsContract.Groups._ID để chuyển vào lời gọi initLoader(). Tôi cũng đã thay đổi logic để gọi restartLoader() chỉ khi bộ nạp không phải là null và bộ nạp làReset là sai.
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;
}
Điều này chắc chắn có ý nghĩa hơn và không thể hiện một số hành vi thất thường của nhóm mở rộng đôi khi và không phải lúc khác.
Tuy nhiên, có các liên hệ đang được hiển thị trong nhóm mà chúng không thuộc về. Và cũng có một số nhóm có liên hệ với họ nhưng sẽ không hiển thị bất kỳ liên hệ nào. Vì vậy, có vẻ như vấn đề getChildrenCursor()
hiện có thể được giải quyết.
Nhưng bây giờ có vẻ như đây là vấn đề về cách Trình tạo con trỏ được khởi tạo theo phương thức onCreateLoader()
. Có phải CursorLoader
được trả về theo phương thức onCreateLoader()
cho con trỏ được khởi tạo không đúng cách không?
CẬP NHẬT
Vì vậy, tôi đã xác định được một trong những vấn đề của tôi. Trong phương thức getChildrenCursor()
nếu tôi chuyển nhómId vào phương thức initLoader()
, thì trong phương thức onCreateLoader()
, khi CursorLoader
được tạo, nó sẽ nhận được thông số groupid chính xác cho truy vấn. Tuy nhiên, trong số onLoadFinished()
, cuộc gọi tới setChildrenCursor()
đang được chuyển qua id trình tải cho thông số đầu tiên không phải là nhóm đặt giá. Tôi đoán tôi phải ánh xạ id của trình tải lên các vị trí nhóm trong một số cấu trúc dữ liệu. Nhưng tôi không chắc đây có phải là cách tiếp cận tốt nhất hay không. Có ai có bất cứ đề nghị?
Tôi vừa thực hiện việc này, nhưng chưa sử dụng Trình tải trang CursorLoader, do đó, việc đó đang ném tôi ...Trong quá trình thực hiện của tôi, getChildrenCursor trả về một con trỏ. Với loadermanager, con trỏ/dữ liệu đó thực sự đi đâu? Nếu bạn không cho con trỏ vào hàm tạo, cái gì đang được đưa vào 'getChildrenCursor' như groupCursor? – Barak
GroupCursor đã được thiết lập trong phương thức onLoadFinished() của LoaderManager. Tôi đã bước qua mã với một trình gỡ lỗi và trong phương thức getChildenCursor(), groupCursor luôn được định nghĩa. – toobsco42
Tôi không biết, nó thực sự có vẻ như bạn không phải lúc nào cũng nhận được một con người con ... – Barak