我正在尝试在此 Android 教程中构建示例联系人应用程序代码:http: //developer.android.com/training/contacts-provider/retrieve-details.html
那里有示例代码,它只显示给定联系人的地址信息。我能够扩展它以提取所有其他字段,但我无法让其他字段以正确的顺序显示在布局中。
我认为这是因为片段为每个部分使用多个加载器,并在 onLoaderFinished() 中对每个加载器进行布局——但它们完成的顺序是不可预测的。
就像联系人的应用程序一样,只有当该部分有数据时,我才需要能够动态显示部分。例如,电话应该放在第一位,但如果联系人没有电话号码,那么电子邮件应该放在第一位,等等。
处理这个问题的最佳方法是什么?我想到了一些可能性(但尝试失败):
- 无需在 onLoadFinished 中为每个光标构建布局,而是使用联系人详细信息填充新的 Contact 类对象。我遇到的问题是我无法确定在哪里构建布局并确保 Contact 对象已完全填充。我得到空指针异常。
- 创建一个单独的 ContactLoader 类,该片段调用该类来处理所有 Loader 并返回一个指向 Contact 对象的指针。问题:在开始布局之前,如何确定 Contact 对象已完成填充?
- 使用一次调用所有数据列的单个查询加载所有联系人数据。我在为查询设置正确的选择和解析光标时遇到了很多麻烦。
有什么想法吗?
以下是一些代码片段---
这是加载器启动的地方,由 onActivityCreated 调用:
public void setContact(Uri contactLookupUri) { mContactUri = contactLookupUri; // If the Uri contains data, load the contact's image and load contact details. if (contactLookupUri != null) { // Asynchronously loads the contact image mImageLoader.loadImage(mContactUri, mImageView); // Shows the contact photo ImageView and hides the empty view mImageView.setVisibility(View.VISIBLE); mEmptyView.setVisibility(View.GONE); // Shows the edit contact action/menu item if (mEditContactMenuItem != null) { mEditContactMenuItem.setVisible(true); } // Starts queries to retrieve contact information from the Contacts Provider. // restartLoader() is used instead of initLoader() as this method may be called // multiple times. getLoaderManager().restartLoader(ContactDetailQuery.QUERY_ID, null, this); getLoaderManager().restartLoader(ContactOrgQuery.QUERY_ID, null, this); getLoaderManager().restartLoader(ContactPhoneQuery.QUERY_ID, null, this); getLoaderManager().restartLoader(ContactEmailQuery.QUERY_ID, null, this); getLoaderManager().restartLoader(ContactAddressQuery.QUERY_ID, null, this); getLoaderManager().restartLoader(ContactGroupsQuery.QUERY_ID, null, this); getLoaderManager().restartLoader(ContactEventsQuery.QUERY_ID, null, this); } }
这里是 onCreateLoader() 加载器开始查询内容提供者的地方:
public Loader<Cursor> onCreateLoader(int id, Bundle args) { final Uri uri = Uri.withAppendedPath(mContactUri, Contacts.Data.CONTENT_DIRECTORY); switch (id) { case ContactDetailQuery.QUERY_ID: // This query loads main contact's name return new CursorLoader(getActivity(), mContactUri, ContactDetailQuery.PROJECTION, null, null, null); case ContactOrgQuery.QUERY_ID: // This query loads contact's company and title details. return new CursorLoader(getActivity(), uri, ContactOrgQuery.PROJECTION, ContactOrgQuery.SELECTION, null, null); case ContactPhoneQuery.QUERY_ID: // This query loads contact address details. return new CursorLoader(getActivity(), uri, ContactPhoneQuery.PROJECTION, ContactPhoneQuery.SELECTION, null, null); case ContactEmailQuery.QUERY_ID: // This query loads contact email details. return new CursorLoader(getActivity(), uri, ContactEmailQuery.PROJECTION, ContactEmailQuery.SELECTION, null, null); case ContactAddressQuery.QUERY_ID: // This query loads contact address details. return new CursorLoader(getActivity(), uri, ContactAddressQuery.PROJECTION, ContactAddressQuery.SELECTION, null, null); case ContactGroupsQuery.QUERY_ID: // This query loads contact groups the contact belongs to. return new CursorLoader(getActivity(), uri, ContactGroupsQuery.PROJECTION, ContactGroupsQuery.SELECTION, null, null); case ContactEventsQuery.QUERY_ID: // This query loads contact events (birthday, etc) the contact belongs to. return new CursorLoader(getActivity(), uri, ContactEventsQuery.PROJECTION, ContactEventsQuery.SELECTION, null, null); } return null; }
这是 onLoadFinished(),它使用一个开关为每个加载器加载一个 buildLayout 函数:
public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // If this fragment was cleared while the query was running // eg. from a call like setContact(uri) then don't do // anything. if (mContactUri == null) { Log.i(TAG, "mContactUri is null"); return; } final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); switch (loader.getId()) { case ContactDetailQuery.QUERY_ID: // Moves to the first row in the Cursor if (data.moveToFirst()) { // For the contact details query, fetches the contact display name. // ContactDetailQuery.DISPLAY_NAME maps to the appropriate display // name field based on OS version. final String contactName = data.getString(ContactDetailQuery.DISPLAY_NAME); if (mIsTwoPaneLayout && mContactName != null) { // In the two pane layout, there is a dedicated TextView // that holds the contact name. mContactName.setText(contactName); } else { // In the single pane layout, sets the activity title // to the contact name. On HC+ this will be set as // the ActionBar title text. getActivity().setTitle(contactName); } } break; case ContactOrgQuery.QUERY_ID: if (data.moveToFirst()) { final String company = data.getString(ContactOrgQuery.COMPANY); final String title = data.getString(ContactOrgQuery.TITLE); //TODO Add Company|Title subtitle here to the ActionBar. } } break; case ContactPhoneQuery.QUERY_ID: // This query loads the contact phone details. Same as addresses above. // Loops through all the rows in the Cursor if (data.moveToFirst()) { // Displays the header for this category final FrameLayout headerLayout = addHeaderLayout(ContactPhoneQuery.header); mDetailsLayout.addView(headerLayout, layoutParams); do { // Builds the phone layout final LinearLayout layout = buildLayout( data.getInt(ContactPhoneQuery.TYPE), data.getString(ContactPhoneQuery.LABEL), data.getString(ContactPhoneQuery.NUMBER), ContactPhoneQuery.QUERY_ID); // Adds the new address layout to the details layout mDetailsLayout.addView(layout, layoutParams); } while (data.moveToNext()); } else { // If nothing found, adds an empty layout mDetailsLayout.addView(buildEmptyLayout(), layoutParams); } break; // Etc for all the Loaders that were called in setContact()
}