使用 CursorLoader 显示具有多个电话号码的联系人
Posted
技术标签:
【中文标题】使用 CursorLoader 显示具有多个电话号码的联系人【英文标题】:Show contacts with multiple phone numbers using CursorLoader 【发布时间】:2017-11-26 06:13:14 【问题描述】:我想检索 android 手机中的所有联系人以及他们的所有电话号码。假设以下是我手机上的联系人列表以及他们的电话号码:
A - 1111(移动)、2222(家庭)、3333(工作)
B - 4444(移动)、5555(家庭)
C - 6666(移动)、7777(家庭)、8888(工作)
我想以以下方式显示联系人,但使用 CursorLoader。
我尝试过的事情:
一次获取所有联系人及其电话号码,并将它们保存在自定义联系人对象的数组列表中,并使用它们在 RecyclerView 中显示列表。 问题在于,如果有很多电话号码,则重新访问此屏幕会导致空白屏幕,这可能是由于资源不可用或内存泄漏问题。堆栈溢出不会正确缩进代码,所以这里是:https://pastebin.com/y9i5R5iN
final Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactListCursor.getLong(ContactsQuery.ID)));
Uri thisContactUri = ContactsContract.Contacts.lookupContact(context.getContentResolver(), uri);
final UserContact contact = new UserContact();
contact.setName(contactListCursor.getString(contactListCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)));
final ArrayList<PhoneType> multipleContacts = new ArrayList<>();
final Uri phoneNumUri = Uri.withAppendedPath(thisContactUri, ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
final Cursor contactPhoneNumCursor = contentResolver.query(phoneNumUri, ContactPhoneQuery.PROJECTION, ContactPhoneQuery.SELECTION, null, null);
通过查询 ContactsContract.Contacts.CONTENT_URI 并获取 ID 和 DISPLAY_NAME_PRIMARY,使用 CursorLoader 和 CursorAdapter 获取所有联系人。在 getView() 方法中,我想我可以初始化第二个 CursorLoader 来调用第一个光标给我的联系人的详细信息。像这样的东西:
如果用户有多个联系人,则显示单选按钮列表,否则显示一个文本视图,其中包含他们唯一的号码。
这似乎会提前加载电话号码,而不是稍后更新视图。要么是因为有延迟,要么是我不确定。
【问题讨论】:
【参考方案1】:找到了一种方法。 使用 CursorLoader 传递给 CustomAdapter 以对联系人进行初始查询,然后使用 _ID 字段查找联系人并为联系人创建 URI,并在 CustomAdapter 的 getView 方法本身中查找电话特定的详细信息。
我试图在两者中都使用 CursorLoaders 或在两者中使用批量加载。混合方法效果最好。
希望这对某人有所帮助。 :)
【讨论】:
你真的应该避免在 getView 上进行光标读取,这会导致某些设备的性能很差,滚动性能也很差。如果我有时间,我明天会发布一个更简单的解决方案 是的,我同意,并且在某些旧手机中滚动时确实看到了性能问题,但是当您说我应该避免在 getView 中读取光标时,您的意思是我不应该在第二部分进行查找_ID 字段并获取联系人的所有电话号码?或者你的意思是我不应该在列表视图上使用光标加载器和 CustomAdapter?因为 Android 开发者页面上的示例使用光标加载联系人姓名,使用第二个光标获取 onClick 中的详细信息。不过,如果您能提出更好的方法,我会很高兴,因为这样可以使旧设备上的滚动更加流畅。【参考方案2】:您可以使用单个查询来获取数据库中的所有电话号码及其联系人 ID 和联系人姓名。
我会运行这个单一查询一次,将所有数据存储在地图中(联系人 ID => 姓名、电话、...) 然后使用自定义适配器将地图显示为联系人列表。
这是查询所有电话以及联系人 ID 和姓名的代码(确保从 ContactsContract
导入所有内容):
Map<Long, List<String>> contacts = new HashMap<Long, List<String>>();
String[] projection = Data.CONTACT_ID, Data.DISPLAY_NAME, Data.MIMETYPE, Data.DATA1, Data.DATA2, Data.DATA3 ;
String selection = Data.MIMETYPE + " IN ('" + Phone.CONTENT_ITEM_TYPE + "')";
Cursor cur = cr.query(Data.CONTENT_URI, projection, selection, null, null);
while (cur != null && cur.moveToNext())
long id = cur.getLong(0);
String name = cur.getString(1);
String mime = cur.getString(2); // type of data (e.g. "phone")
String data = cur.getString(3); // the actual info, e.g. +1-212-555-1234
int type = cur.getInt(4); // a numeric value representing type: e.g. home / office / personal
String label = cur.getString(5); // a custom label in case type is "TYPE_CUSTOM"
String labelStr = Phone.getTypeLabel(getResources(), type, label);
Log.d(TAG, "got " + id + ", " + name + ", " + kind + " - " + data + " (" + labelStr + ")");
// add info to existing list if this contact-id was already found, or create a new list in case it's new
List<String> infos;
if (contacts.containsKey(id))
infos = contacts.get(id);
else
infos = new ArrayList<String>();
infos.add("name = " + name);
contacts.put(id, infos);
infos.add(kind + " = " + data + " (" + labelStr + ")");
注意:地图的值是一个字符串列表,只是为了代码清晰。您应该将其更改为自定义 Contact
对象,该对象在适当的 Java 对象中保存电话的名称和列表
【讨论】:
我之前尝试过这样做,但是当手机有很多 600-1000 范围内的联系人时,1) 它会冻结联系人资源。因此,切换到和退出联系人列表屏幕会导致空白屏幕。 2)它也使屏幕反应迟钝。 3)它可能会占用堆空间,如果它被GC-ed然后我将不得不在屏幕加载时调用重新加载导致问题#1。如果我的理解有误,请纠正我 听起来你正在尝试在主线程上运行查询,你应该在屏幕上显示加载动画,并在AsyncTask
中加载联系人地图,你可以预取联系人在用户进入屏幕之前列出以减少甚至消除加载时间。以上是关于使用 CursorLoader 显示具有多个电话号码的联系人的主要内容,如果未能解决你的问题,请参考以下文章
当 CursorLoader 的构造函数不支持时,如何在 CursorLoader 中使用连接查询
安卓:CursorLoader、LoaderManager、SQLite