android 获取通讯录全选反选_Xamarin.Forms读取并展示Android和iOS通讯录 TerminalMACS客户端...

本文同步更新地址:

  • https://dotnet9.com/11520.html

  • https://terminalmacs.com/861.html

阅读导航:

  • 一、功能说明

  • 二、代码实现

  • 三、源码获取

  • 四、参考资料

  • 五、后面计划

一、功能说明

2ce954fb98bfd5f573e1208a495ad1f6.png

完整思维导图:https://github.com/dotnet9/TerminalMACS/blob/master/docs/TerminalMACS.xmind 

本文介绍图中右侧画红圈处的功能,即使用Xamarin.Forms获取和展示Android和iOS的通讯录信息,下面是最终效果,由于使用的是真实手机,所以联系人姓名及电话号码打码显示。

0f88aac2c85f74762b456a97e23d0a86.png

并简单的进行了搜索功能处理,之所以说简单,是因为通讯录列表是全部读取出来了,搜索是直接从此列表进行过滤的。

下图来自:https://www.xamboy.com/2019/10/10/getting-phone-contacts-in-xamarin-forms/,本功能是参考此文所写,所以直接引用文中的图片。

6994835df56bd6a3fa7cba8bf4f5f98d.gif

a90d940ee71f73e7c463e50d13893730.png

二、代码实现

1、共享库工程创建联系人实体类:Contacts.cs

  1. namespace TerminalMACS.Clients.App.Models

  2. {

  3.   ///

  4.   /// 通讯录

  5.   ///

  6.    public class Contact

  7.    {

  8.       ///

  9.       /// 获取或者设置名称

  10.       ///

  11.        public string Name { get; set; }

  12.       ///

  13.       /// 获取或者设置 头像

  14.       ///

  15.        public string Image { get; set; }

  16.       ///

  17.       /// 获取或者设置 邮箱地址

  18.       ///

  19.        public string[] Emails { get; set; }

  20.       ///

  21.       /// 获取或者设置 手机号码

  22.       ///

  23.        public string[] PhoneNumbers { get; set; }

  24.    }

  25. }

2、共享库创建通讯录服务接口:IContactsService.cs

包括:

  • 一个通讯录获取请求接口:RetrieveContactsAsync

  • 一个读取一条通讯结果通知事件:OnContactLoaded

  1. using System;

  2. using System.Collections.Generic;

  3. using System.Threading;

  4. using System.Threading.Tasks;

  5. using TerminalMACS.Clients.App.Models;

  6. namespace TerminalMACS.Clients.App.Services

  7. {

  8.   ///

  9.   /// 通讯录事件参数

  10.   ///

  11.    public class ContactEventArgs:EventArgs

  12.    {

  13.        public Contact Contact { get; }

  14.        public ContactEventArgs(Contact contact)

  15.        {

  16.            Contact = contact;

  17.        }

  18.    }

  19.   ///

  20.   /// 通讯录服务接口,android和iOS终端具体的通讯录获取服务需要继承此接口

  21.   ///

  22.    public interface IContactsService

  23.    {

  24.       ///

  25.       /// 读取一条数据通知

  26.       ///

  27.        event EventHandler OnContactLoaded;

  28.       ///

  29.       /// 是否正在加载

  30.       ///

  31.        bool IsLoading { get; }

  32.       ///

  33.       /// 尝试获取所有通讯录

  34.       ///

  35.       ///

  36.       ///

  37.        Task> RetrieveContactsAsync(CancellationToken? token = null);

  38.    }

  39. }

3、iOS工程中添加通讯录服务,实现IContactsService接口:

  1. using Contacts;

  2. using Foundation;

  3. using System;

  4. using System.Collections.Generic;

  5. using System.IO;

  6. using System.Linq;

  7. using System.Threading;

  8. using System.Threading.Tasks;

  9. using TerminalMACS.Clients.App.Models;

  10. using TerminalMACS.Clients.App.Services;

  11. namespace TerminalMACS.Clients.App.iOS.Services

  12. {

  13.   ///

  14.   /// 通讯录获取服务

  15.   ///

  16.    public class ContactsService : NSObject, IContactsService

  17.    {

  18.        const string ThumbnailPrefix = "thumb";

  19.        bool requestStop = false;

  20.        public event EventHandler OnContactLoaded;

  21.        bool _isLoading = false;

  22.        public bool IsLoading => _isLoading;

  23.       ///

  24.       /// 异步请求权限

  25.       ///

  26.       ///

  27.        public async Task<bool> RequestPermissionAsync()

  28.        {

  29.            var status = CNContactStore.GetAuthorizationStatus(CNEntityType.Contacts);

  30.            Tuple<bool, NSError> authotization = new Tuple<bool, NSError>(status == CNAuthorizationStatus.Authorized, null);

  31.            if (status == CNAuthorizationStatus.NotDetermined)

  32.            {

  33.                using (var store = new CNContactStore())

  34.                {

  35.                    authotization = await store.RequestAccessAsync(CNEntityType.Contacts);

  36.                }

  37.            }

  38.            return authotization.Item1;

  39.        }

  40.       ///

  41.       /// 异步请求通讯录,此方法由界面真正调用

  42.       ///

  43.       ///

  44.       ///

  45.        public async Task> RetrieveContactsAsync(CancellationToken? cancelToken = null)

  46.        {

  47.            requestStop = false;

  48.            if (!cancelToken.HasValue)

  49.                cancelToken = CancellationToken.None;

  50.           // 我们创建了一个十进制的TaskCompletionSource

  51.            var taskCompletionSource = new TaskCompletionSource>();

  52.           // 在cancellationToken中注册lambda

  53.            cancelToken.Value.Register(() =>

  54.            {

  55.               // 我们收到一条取消消息,取消TaskCompletionSource.Task

  56.                requestStop = true;

  57.                taskCompletionSource.TrySetCanceled();

  58.            });

  59.            _isLoading = true;

  60.            var task = LoadContactsAsync();

  61.           // 等待两个任务中的第一个任务完成

  62.            var completedTask = await Task.WhenAny(task, taskCompletionSource.Task);

  63.            _isLoading = false;

  64.            return await completedTask;

  65.        }

  66.       ///

  67.       /// 异步加载通讯录,具体的通讯录读取方法

  68.       ///

  69.       ///

  70.        async Task> LoadContactsAsync()

  71.        {

  72.            IList contacts = new List();

  73.            var hasPermission = await RequestPermissionAsync();

  74.            if (hasPermission)

  75.            {

  76.                NSError error = null;

  77.                var keysToFetch = new[] { CNContactKey.PhoneNumbers, CNContactKey.GivenName, CNContactKey.FamilyName, CNContactKey.EmailAddresses, CNContactKey.ImageDataAvailable, CNContactKey.ThumbnailImageData };

  78.                var request = new CNContactFetchRequest(keysToFetch: keysToFetch);

  79.                request.SortOrder = CNContactSortOrder.GivenName;

  80.                using (var store = new CNContactStore())

  81.                {

  82.                    var result = store.EnumerateContacts(request, out error, new CNContactStoreListContactsHandler((CNContact c, ref bool stop) =>

  83.                    {

  84.                        string path = null;

  85.                        if (c.ImageDataAvailable)

  86.                        {

  87.                            path = path = Path.Combine(Path.GetTempPath(), $"{ThumbnailPrefix}-{Guid.NewGuid()}");

  88.                            if (!File.Exists(path))

  89.                            {

  90.                                var imageData = c.ThumbnailImageData;

  91.                                imageData?.Save(path, true);

  92.                            }

  93.                        }

  94.                        var contact = new Contact()

  95.                        {

  96.                            Name = string.IsNullOrEmpty(c.FamilyName) ? c.GivenName : $"{c.GivenName} {c.FamilyName}",

  97.                            Image = path,

  98.                            PhoneNumbers = c.PhoneNumbers?.Select(p => p?.Value?.StringValue).ToArray(),

  99.                            Emails = c.EmailAddresses?.Select(p => p?.Value?.ToString()).ToArray(),

  100.                        };

  101.                        if (!string.IsNullOrWhiteSpace(contact.Name))

  102.                        {

  103.                            OnContactLoaded?.Invoke(this, new ContactEventArgs(contact));

  104.                            contacts.Add(contact);

  105.                        }

  106.                        stop = requestStop;

  107.                    }));

  108.                }

  109.            }

  110.            return contacts;

  111.        }

  112.    }

  113. }

4、在iOS工程中的Info.plist文件添加通讯录权限使用说明

274eb55e2c199539207e411fe2538c54.png

5、在Android工程中添加读取通讯录权限配置:AndroidManifest.xml

  1. android:name="android.permission.READ_CONTACTS"/>

完整权限配置如下

  1. xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.terminalmacs.clients.app">

  2.   android:minSdkVersion="21" android:targetSdkVersion="28" />

  3.   android:label="TerminalMACS.Clients.App.Android">

  4.   android:name="android.permission.ACCESS_NETWORK_STATE" />

  5.   android:name="android.permission.READ_CONTACTS"/>

  6.   android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

6、在Android工程中添加通讯录服务,实现IContactServer接口:ContactsService.cs

  1. using Acr.UserDialogs;

  2. using Android;

  3. using Android.App;

  4. using Android.Content;

  5. using Android.Content.PM;

  6. using Android.Database;

  7. using Android.Provider;

  8. using Android.Runtime;

  9. using Android.Support.V4.App;

  10. using Plugin.CurrentActivity;

  11. using System;

  12. using System.Collections.Generic;

  13. using System.IO;

  14. using System.Linq;

  15. using System.Threading;

  16. using System.Threading.Tasks;

  17. using TerminalMACS.Clients.App.Models;

  18. using TerminalMACS.Clients.App.Services;

  19. namespace TerminalMACS.Clients.App.Droid.Services

  20. {

  21.   ///

  22.   /// 通讯录获取服务

  23.   ///

  24.    public class ContactsService : IContactsService

  25.    {

  26.        const string ThumbnailPrefix = "thumb";

  27.        bool stopLoad = false;

  28.        static TaskCompletionSource<bool> contactPermissionTcs;

  29.        public string TAG

  30.        {

  31.            get

  32.            {

  33.                return "MainActivity";

  34.            }

  35.        }

  36.        bool _isLoading = false;

  37.        public bool IsLoading => _isLoading;

  38.       //权限请求状态码

  39.        public const int RequestContacts = 1239;

  40.       ///

  41.       /// 获取通讯录需要的请求权限

  42.       ///

  43.        static string[] PermissionsContact = {

  44.            Manifest.Permission.ReadContacts

  45.        };

  46.        public event EventHandler OnContactLoaded;

  47.       ///

  48.       /// 异步请求通讯录权限

  49.       ///

  50.        async void RequestContactsPermissions()

  51.        {

  52.           //检查是否可以弹出申请读、写通讯录权限

  53.            if (ActivityCompat.ShouldShowRequestPermissionRationale(CrossCurrentActivity.Current.Activity, Manifest.Permission.ReadContacts)

  54.                || ActivityCompat.ShouldShowRequestPermissionRationale(CrossCurrentActivity.Current.Activity, Manifest.Permission.WriteContacts))

  55.            {

  56.               // 如果未授予许可,请向用户提供其他理由用户将从使用权限的附加上下文中受益。

  57.               // 例如,如果请求先前被拒绝。

  58.                await UserDialogs.Instance.AlertAsync("通讯录权限", "此操作需要“通讯录”权限", "确定");

  59.            }

  60.            else

  61.            {

  62.               // 尚未授予通讯录权限。直接请求这些权限。

  63.                ActivityCompat.RequestPermissions(CrossCurrentActivity.Current.Activity, PermissionsContact, RequestContacts);

  64.            }

  65.        }

  66.       ///

  67.       /// 收到用户响应请求权限操作后的结果

  68.       ///

  69.       ///

  70.       ///

  71.       ///

  72.        public static void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)

  73.        {

  74.            if (requestCode == RequestContacts)

  75.            {

  76.               // 我们请求了多个通讯录权限,因此需要检查相关的所有权限

  77.                if (PermissionUtil.VerifyPermissions(grantResults))

  78.                {

  79.                   // 已授予所有必需的权限,显示联系人片段。

  80.                    contactPermissionTcs.TrySetResult(true);

  81.                }

  82.                else

  83.                {

  84.                    contactPermissionTcs.TrySetResult(false);

  85.                }

  86.            }

  87.        }

  88.       ///

  89.       /// 异步请求权限

  90.       ///

  91.       ///

  92.        public async Task<bool> RequestPermissionAsync()

  93.        {

  94.            contactPermissionTcs = new TaskCompletionSource<bool>();

  95.           // 验证是否已授予所有必需的通讯录权限。

  96.            if (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(CrossCurrentActivity.Current.Activity, Manifest.Permission.ReadContacts) != (int)Permission.Granted

  97.                || Android.Support.V4.Content.ContextCompat.CheckSelfPermission(CrossCurrentActivity.Current.Activity, Manifest.Permission.WriteContacts) != (int)Permission.Granted)

  98.            {

  99.               // 尚未授予通讯录权限。

  100.                RequestContactsPermissions();

  101.            }

  102.            else

  103.            {

  104.               // 已授予通讯录权限。

  105.                contactPermissionTcs.TrySetResult(true);

  106.            }

  107.            return await contactPermissionTcs.Task;

  108.        }

  109.       ///

  110.       /// 异步请求通讯录,此方法由界面真正调用

  111.       ///

  112.       ///

  113.       ///

  114.        public async Task> RetrieveContactsAsync(CancellationToken? cancelToken = null)

  115.        {

  116.            stopLoad = false;

  117.            if (!cancelToken.HasValue)

  118.                cancelToken = CancellationToken.None;

  119.           // 我们创建了一个十进制的TaskCompletionSource

  120.            var taskCompletionSource = new TaskCompletionSource>();

  121.           // 在cancellationToken中注册lambda

  122.            cancelToken.Value.Register(() =>

  123.            {

  124.               // 我们收到一条取消消息,取消TaskCompletionSource.Task

  125.                stopLoad = true;

  126.                taskCompletionSource.TrySetCanceled();

  127.            });

  128.            _isLoading = true;

  129.            var task = LoadContactsAsync();

  130.           // 等待两个任务中的第一个任务完成

  131.            var completedTask = await Task.WhenAny(task, taskCompletionSource.Task);

  132.            _isLoading = false;

  133.            return await completedTask;

  134.        }

  135.       ///

  136.       /// 异步加载通讯录,具体的通讯录读取方法

  137.       ///

  138.       ///

  139.        async Task> LoadContactsAsync()

  140.        {

  141.            IList contacts = new List();

  142.            var hasPermission = await RequestPermissionAsync();

  143.            if (!hasPermission)

  144.            {

  145.                return contacts;

  146.            }

  147.            var uri = ContactsContract.Contacts.ContentUri;

  148.            var ctx = Application.Context;

  149.            await Task.Run(() =>

  150.            {

  151.               // 暂时只请求通讯录Id、DisplayName、PhotoThumbnailUri,可以扩展

  152.                var cursor = ctx.ApplicationContext.ContentResolver.Query(uri, new string[]

  153.                {

  154.                        ContactsContract.Contacts.InterfaceConsts.Id,

  155.                        ContactsContract.Contacts.InterfaceConsts.DisplayName,

  156.                        ContactsContract.Contacts.InterfaceConsts.PhotoThumbnailUri

  157.                }, null, null, $"{ContactsContract.Contacts.InterfaceConsts.DisplayName} ASC");

  158.                if (cursor.Count > 0)

  159.                {

  160.                    while (cursor.MoveToNext())

  161.                    {

  162.                        var contact = CreateContact(cursor, ctx);

  163.                        if (!string.IsNullOrWhiteSpace(contact.Name))

  164.                        {

  165.                           // 读取出一条,即通知界面展示

  166.                            OnContactLoaded?.Invoke(this, new ContactEventArgs(contact));

  167.                            contacts.Add(contact);

  168.                        }

  169.                        if (stopLoad)

  170.                            break;

  171.                    }

  172.                }

  173.            });

  174.            return contacts;

  175.        }

  176.       ///

  177.       /// 读取一条通讯录数据

  178.       ///

  179.       ///

  180.       ///

  181.       ///

  182.        Contact CreateContact(ICursor cursor, Context ctx)

  183.        {

  184.            var contactId = GetString(cursor, ContactsContract.Contacts.InterfaceConsts.Id);

  185.            var numbers = GetNumbers(ctx, contactId);

  186.            var emails = GetEmails(ctx, contactId);

  187.            var uri = GetString(cursor, ContactsContract.Contacts.InterfaceConsts.PhotoThumbnailUri);

  188.            string path = null;

  189.            if (!string.IsNullOrEmpty(uri))

  190.            {

  191.                try

  192.                {

  193.                    using (var stream = Android.App.Application.Context.ContentResolver.OpenInputStream(Android.Net.Uri.Parse(uri)))

  194.                    {

  195.                        path = Path.Combine(Path.GetTempPath(), $"{ThumbnailPrefix}-{Guid.NewGuid()}");

  196.                        using (var fstream = new FileStream(path, FileMode.Create))

  197.                        {

  198.                            stream.CopyTo(fstream);

  199.                            fstream.Close();

  200.                        }

  201.                        stream.Close();

  202.                    }

  203.                }

  204.                catch (Exception ex)

  205.                {

  206.                    System.Diagnostics.Debug.WriteLine(ex);

  207.                }

  208.            }

  209.            var contact = new Contact

  210.            {

  211.                Name = GetString(cursor, ContactsContract.Contacts.InterfaceConsts.DisplayName),

  212.                Emails = emails,

  213.                Image = path,

  214.                PhoneNumbers = numbers,

  215.            };

  216.            return contact;

  217.        }

  218.       ///

  219.       /// 读取联系人电话号码

  220.       ///

  221.       ///

  222.       ///

  223.       ///

  224.        string[] GetNumbers(Context ctx, string contactId)

  225.        {

  226.            var key = ContactsContract.CommonDataKinds.Phone.Number;

  227.            var cursor = ctx.ApplicationContext.ContentResolver.Query(

  228.                ContactsContract.CommonDataKinds.Phone.ContentUri,

  229.                null,

  230.                ContactsContract.CommonDataKinds.Phone.InterfaceConsts.ContactId + " = ?",

  231.                new[] { contactId },

  232.                null

  233.            );

  234.            return ReadCursorItems(cursor, key)?.ToArray();

  235.        }

  236.       ///

  237.       /// 读取联系人邮箱地址

  238.       ///

  239.       ///

  240.       ///

  241.       ///

  242.        string[] GetEmails(Context ctx, string contactId)

  243.        {

  244.            var key = ContactsContract.CommonDataKinds.Email.InterfaceConsts.Data;

  245.            var cursor = ctx.ApplicationContext.ContentResolver.Query(

  246.                ContactsContract.CommonDataKinds.Email.ContentUri,

  247.                null,

  248.                ContactsContract.CommonDataKinds.Email.InterfaceConsts.ContactId + " = ?",

  249.                new[] { contactId },

  250.                null);

  251.            return ReadCursorItems(cursor, key)?.ToArray();

  252.        }

  253.        IEnumerable<string> ReadCursorItems(ICursor cursor, string key)

  254.        {

  255.            while (cursor.MoveToNext())

  256.            {

  257.                var value = GetString(cursor, key);

  258.                yield return value;

  259.            }

  260.            cursor.Close();

  261.        }

  262.        string GetString(ICursor cursor, string key)

  263.        {

  264.            return cursor.GetString(cursor.GetColumnIndex(key));

  265.        }

  266.    }

  267. }

需要添加 Plugin.CurrentActivity 和 Acr.UserDialogs 包。

7、Android工程添加权限处理判断类

Permission.Util.cs

  1. using Android.Content.PM;

  2. namespace TerminalMACS.Clients.App.Droid

  3. {

  4.    public static class PermissionUtil

  5.    {

  6.        /**

  7.    * 通过验证给定数组中的每个条目的值是否为Permission.Granted,检查是否已授予所有给定权限。

  8.    *

  9.    * See Activity#onRequestPermissionsResult (int, String[], int[])

  10.    */

  11.        public static bool VerifyPermissions(Permission[] grantResults)

  12.        {

  13.           // 必须至少检查一个结果.

  14.            if (grantResults.Length < 1)

  15.                return false;

  16.           // 验证是否已授予每个必需的权限,否则返回false.

  17.            foreach (Permission result in grantResults)

  18.            {

  19.                if (result != Permission.Granted)

  20.                {

  21.                    return false;

  22.                }

  23.            }

  24.            return true;

  25.        }

  26.    }

  27. }

MainActivity.OnRequestPermissionResult是权限申请结果处理函数,在此函数中调用ContactsService.OnRequestPermissionsResult通知通讯录服务权限处理结果。

MainActivity.cs

  1. using Acr.UserDialogs;

  2. using Android.App;

  3. using Android.Content.PM;

  4. using Android.OS;

  5. using Android.Runtime;

  6. using TerminalMACS.Clients.App.Droid.Services;

  7. using TerminalMACS.Clients.App.Services;

  8. namespace TerminalMACS.Clients.App.Droid

  9. {

  10.    [Activity(Label = "TerminalMACS.Clients.App", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]

  11.    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity

  12.    {

  13.        IContactsService contactsService = new ContactsService();

  14.        protected override void OnCreate(Bundle savedInstanceState)

  15.        {

  16.            TabLayoutResource = Resource.Layout.Tabbar;

  17.            ToolbarResource = Resource.Layout.Toolbar;

  18.            base.OnCreate(savedInstanceState);

  19.            Xamarin.Essentials.Platform.Init(this, savedInstanceState);

  20.            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

  21.            UserDialogs.Init(() => this);

  22.           // 将通讯录服务实例传递给共享库,由共享库使用读取通讯录接口

  23.            LoadApplication(new App(contactsService));

  24.        }

  25.        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)

  26.        {

  27.            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

  28.           // 通讯录服务处理权限请求结果

  29.            ContactsService.OnRequestPermissionsResult(requestCode, permissions, grantResults);

  30.            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);

  31.        }

  32.    }

  33. }

8、创建通讯录ViewModel,并使用通讯录服务

  1. using System;

  2. using System.Collections;

  3. using System.Collections.Generic;

  4. using System.Collections.ObjectModel;

  5. using System.Linq;

  6. using System.Threading.Tasks;

  7. using System.Windows.Input;

  8. using TerminalMACS.Clients.App.Models;

  9. using TerminalMACS.Clients.App.Services;

  10. using Xamarin.Forms;

  11. namespace TerminalMACS.Clients.App.ViewModels

  12. {

  13.   ///

  14.   /// 通讯录ViewModel

  15.   ///

  16.    public class ContactViewModel : BaseViewModel

  17.    {

  18.       ///

  19.       /// 通讯录服务接口

  20.       ///

  21.        IContactsService _contactService;

  22.       ///

  23.       /// 标题

  24.       ///

  25.        public new string Title => "通讯录";

  26.        private string _SearchText;

  27.       ///

  28.       /// 搜索关键字

  29.       ///

  30.        public string SearchText

  31.        {

  32.            get { return _SearchText; }

  33.            set

  34.            {

  35.                SetProperty(ref _SearchText, value);

  36.            }

  37.        }

  38.       ///

  39.       /// 通讯录搜索命令

  40.       ///

  41.        public ICommand RaiseSearchCommand { get; }

  42.       ///

  43.       /// 通讯录列表

  44.       ///

  45.        public ObservableCollection Contacts { get; set; }

  46.        private List _FilteredContacts;

  47.       ///

  48.       /// 通讯录过滤列表

  49.       ///

  50.        public List FilteredContacts

  51.        {

  52.            get { return _FilteredContacts; }

  53.            set

  54.            {

  55.                SetProperty(ref _FilteredContacts, value);

  56.            }

  57.        }

  58.        public ContactViewModel(IContactsService contactService)

  59.        {

  60.            _contactService = contactService;

  61.            Contacts = new ObservableCollection();

  62.            Xamarin.Forms.BindingBase.EnableCollectionSynchronization(Contacts, null, ObservableCollectionCallback);

  63.            _contactService.OnContactLoaded += OnContactLoaded;

  64.            LoadContacts();

  65.            RaiseSearchCommand = new Command(RaiseSearchHandle);

  66.        }

  67.       ///

  68.       /// 过滤通讯录

  69.       ///

  70.        void RaiseSearchHandle()

  71.        {

  72.            if (string.IsNullOrEmpty(SearchText))

  73.            {

  74.                FilteredContacts = Contacts.ToList();

  75.                return;

  76.            }

  77.            Funcbool> checkContact = (s) =>

  78.            {

  79.                if (!string.IsNullOrWhiteSpace(s.Name) && s.Name.ToLower().Contains(SearchText.ToLower()))

  80.                {

  81.                    return true;

  82.                }

  83.                else if (s.PhoneNumbers.Length > 0 && s.PhoneNumbers.ToList().Exists(cu => cu.ToString().Contains(SearchText)))

  84.                {

  85.                    return true;

  86.                }

  87.                return false;

  88.            };

  89.            FilteredContacts = Contacts.ToList().Where(checkContact).ToList();

  90.        }

  91.       ///

  92.       /// BindingBase.EnableCollectionSynchronization 为集合启用跨线程更新

  93.       ///

  94.       ///

  95.       ///

  96.       ///

  97.       ///

  98.        void ObservableCollectionCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess)

  99.        {

  100.           // `lock` ensures that only one thread access the collection at a time

  101.            lock (collection)

  102.            {

  103.                accessMethod?.Invoke();

  104.            }

  105.        }

  106.       ///

  107.       /// 收到事件通知,读取一条通讯录信息

  108.       ///

  109.       ///

  110.       ///

  111.        private void OnContactLoaded(object sender, ContactEventArgs e)

  112.        {

  113.            Contacts.Add(e.Contact);

  114.            RaiseSearchHandle();

  115.        }

  116.       ///

  117.       /// 异步读取终端通讯录

  118.       ///

  119.       ///

  120.        async Task LoadContacts()

  121.        {

  122.            try

  123.            {

  124.                await _contactService.RetrieveContactsAsync();

  125.            }

  126.            catch (TaskCanceledException)

  127.            {

  128.                Console.WriteLine("任务已经取消");

  129.            }

  130.        }

  131.    }

  132. }

9、添加通讯录页面展示通讯录数据

  1. <?xml version="1.0" encoding="utf-8" ?>

  2. "http://xamarin.com/schemas/2014/forms"

  3.             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

  4.             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  5.             xmlns:d="http://xamarin.com/schemas/2014/forms/design"

  6.             xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"

  7.             mc:Ignorable="d"

  8.             Title="{Binding Title}"

  9.             x:Class="TerminalMACS.Clients.App.Views.ContactPage"

  10.             ios:Page.UseSafeArea="true">

  11.            "filterText"

  12.                        HeightRequest="40"

  13.                        Text="{Binding SearchText}"

  14.                       SearchCommand="{Binding RaiseSearchCommand}"/>

  15.            "{Binding FilteredContacts}"

  16.                        HasUnevenRows="True">

  17.                            "10"

  18.                                         Orientation="Horizontal">

  19.                                "{Binding Image}"

  20.                                        VerticalOptions="Center"

  21.                                        x:Name="image"

  22.                                        Aspect="AspectFit"

  23.                                        HeightRequest="60"/>

  24.                                "Center">

  25.                                    "{Binding Name}"

  26.                                       FontAttributes="Bold"/>

  27.                                    "{Binding PhoneNumbers[0]}"/>

  28.                                    "{Binding Emails[0]}"/>

三、源码获取

  • 1.完整源码:https://github.com/dotnet9/TerminalMACS

  • 2.Android客户端可成功取得通讯录数据,并可查询;

已编译的Android客户端:https://terminalmacs.com/terminalmacs-clients-app-android

  • 3.iOS读取通讯录功能代码也已添加,但由于本人没有iOS测试环境,所以未验证,有条件的朋友可以测试下iOS的通讯录读取功能,如果代码不起作用,可参考本文参考的文章检查iOS代码。

四、参考资料

Getting phone contacts in Xamarin Forms:https://www.xamboy.com/2019/10/10/getting-phone-contacts-in-xamarin-forms/

参考文章末尾有源代码链接。

五、后面计划

Xamarin.Forms客户端基本信息获取,比如IMEI、IMSI、本机号码、Mac地址等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/523116.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA Terminal替换成Git Bash_05

IDEA Terminal替换成Git Bash 在IDEA中&#xff0c;打开settings&#xff0c;设置相应的bash路径 settings–>Tools–>Terminal–>Shell path:C:\Program Files\Git\bin\bash.exe

再提“鸿蒙”,任正非说这是为物联网而生;硅谷公司年薪报告出炉,谷歌年薪居榜首;苹果CEO库克接班人浮出水面,苹果高层或大换血?...

关注并标星星CSDN云计算极客头条&#xff1a;速递、最新、绝对有料。这里有企业新动、这里有业界要闻&#xff0c;打起十二分精神&#xff0c;紧跟fashion你可以的&#xff01;每周三次&#xff0c;打卡即read更快、更全了解泛云圈精彩newsgo go go 麒麟810芯片真容&#xff08…

ECS 按量付费VPC实例停机不收费FAQ

摘要&#xff1a; 停机不收费支持范围 目前只支持VPC类型的按量付费的ECS实例&#xff0c;您需要在控制台签署接受停机不收费协议可以开启。开启之后下次Stop机器自动进入停机不收费模式。不影响您的经典网络和包年包月预付费ECS实例的行为。 停机不收费支持范围 目前只支持VP…

在maven引入一个maven仓库中不存在的jar,安装本地底仓库

问题描述&#xff1a;如果要在一个maven工程中引入一个不存在的jar&#xff0c;即无法通过denpendecy的gav定位到。 处理方式&#xff1a; 方式一&#xff1a;如果maven工程是war包形式&#xff0c;可以直接放在 WEB-INF下的lib目录中&#xff0c;然后add到类路径中。 方式二…

重磅!Python再次第一,Java和C下降,凭什么?

编程语言流行指数(PYPL)排行榜近日公布了2019年7月份榜单。在最新一期榜单上&#xff0c; Python的份额高达28.08&#xff05;&#xff0c;再次蝉联第一&#xff0c;并且增长4.7%&#xff0c;同时成为增长势头较好的语言。而被挤到第二蝉联Java&#xff0c;同比下跌1.8个百分点…

deepin中mysql数据库的连接_教你如何典雅的用Python连接MySQL数据库

简介:作者 | Python语音识别不管是机器学习、web开发或者爬虫&#xff0c;数据库都是绕不过去的。那么今天我们就来介绍Python如何Mysql数据库进行连接以及数据的交换。主要分为以下几个方面&#xff1a;什么是数据库&#xff1f;什么是MySQLdb&#xff1f;Python如 ...书页库概…

AliOS Things KV组件的写平衡特性

摘要&#xff1a; KV组件的写平衡&#xff08;磨损平衡&#xff09;特性就是通过异地更新、垃圾回收等策略来平衡flash介质各个存储区块的磨损程度&#xff0c;以避免某些“特定”存储区块因过度使用而形成坏区&#xff0c;从而延长flash的使用寿命。 前言 KV组件是AliOS Thing…

解决win10系统下,git Bash闪退的问题

问题描述 在git官网下载了软件&#xff0c;安装之后&#xff0c;git Bash出现闪退现象&#xff0c;同时在当前文件夹下面会生成一个mintty.exe.stackdump的文件。 git GUI同样打不开&#xff0c;同时会报出错误&#xff0c;具体情况如下图&#xff1a; 点击百度云链接,下载一个…

AliOS Things网络适配框架 - SAL

摘要&#xff1a; 很多物联网应用场景中&#xff0c;都需要使用主控MCU外接连接芯片&#xff08;如WiFi、NB-IoT&#xff09;的解决方案。为方便这类场景的开发&#xff0c;AliOS Things提供了Socket Adapter Layer&#xff08;SAL&#xff09;框架和组件方案 AliOS Things中提…

文末送书啦!| Device Mapper,那些你不知道的Docker核心技术

戳蓝字“CSDN云计算”关注我们哦&#xff01;接触Docker 比较早的同学应该知道&#xff0c;Docker 在最开始只能在Ubuntu和Debian等少数的Linux 发行版上运行&#xff0c;并且在这些发行版上默认使用的存储驱动为AUFS。由于Linux 并未将AUFS的支持纳入自己的内核主线(据说是因为…

铃木uy125最高时速_五菱宏光mini EV月销三万辆,铃木是否后悔退出中国?

国内新能源汽车市场一直是“兵家必争之地”&#xff0c;大到传统汽车厂商&#xff0c;小到造车新势力&#xff0c;都希望能在新能源汽车市场分一杯羹。但长久以来&#xff0c;大多数汽车厂商都只注意到了一部分中国消费者对于“大”的需求&#xff0c;忽视了微型车市场&#xf…

服务器读取账号密码,WIN服务器得明文密码神器 mimikatz、WCE

一、mimikatz(自己亲试&#xff0c;可以在win7 x64下使用)昨天有朋友发了个法国佬写的神器叫mimikatz 让我们看下还有一篇用这个神器直接从 lsass.exe 里获取windows处于active状态账号明文密码的文章http://pentestmonkey.net/blog/mimikatz-tool-to-recover-cleartext-passwo…

100%移植阿里云移动测试技术,竟仅需1周?!——移动测试专有云(3)——内容详解...

摘要&#xff1a; Android兼容性测试旨在帮助解决Android应用在不同真机机型上的各类兼容性问题&#xff0c;包括 Crash/ANR分析、6项性能分析、UI检测、3个版本的覆盖安装检测等。Android兼容性测试使用非常简单&#xff0c;客户只需要提交被检测的APK文件&#xff08;如需登录…

投篮机投篮有技巧吗_「技巧干货」高手练习投篮的几个技巧,让投篮变得更实用...

会打篮球的都知道&#xff0c;篮球主要的是用更高于对手的得分来取得胜利&#xff1b;然而&#xff0c;让很多的球友烦恼的是&#xff0c;投篮时因为不够稳定&#xff0c;而导致每一场的发挥都不一样&#xff0c;全靠运气投球&#xff1b;这可是球场上的一大禁忌&#xff0c;人…

天龙八部服务器都需要那种系统,天龙八部排行榜系统怎么看 排行榜系统分类介绍...

天龙八部排行榜系统怎么看?有这么一群人&#xff0c;不但战力高&#xff0c;等级高&#xff0c;在其他玩家眼中&#xff0c;他们就是膜拜的大神&#xff0c;他们便是游戏中排行榜上的大神玩家。排行榜分类等级&#xff1a;排行榜打开后的第一个排名就是玩家角色的等级排名&…

SDN精华问答 | 了解SDN架构

SDN火热了好一阵子&#xff0c;无论运营商、政府企业、投资机构&#xff0c;一段时间&#xff0c;不知道SDN、不能甩几个SDN相关的名词术语&#xff0c;似乎都落后于时代了。今天&#xff0c;就来看看关于SDN的精华问答吧。1Q&#xff1a;SDN的本质属性&#xff1f; A&#xff…

SpringBoot2整合Activiti6工作流框架

文章目录下载实战操作&#xff1a;创建用户3 创建app4 选择我们的指定的流程图下载 wget https://github.com/Activiti/Activiti/releases/download/activiti-6.0.0/activiti-6.0.0.zip浏览器 http://localhost:8080/activiti-app/#实战操作&#xff1a; 创建用户 2.创建流程…

如何转obj_Java 开发中如何正确的踩坑,看完这个你可以避免50%的错误

为什么说一个好的员工能顶 100 个普通员工我们的做法是&#xff0c;要用最好的人。我一直都认为研发本身是很有创造性的&#xff0c;如果人不放松&#xff0c;或不够聪明&#xff0c;都很难做得好。你要找到最好的人&#xff0c;一个好的工程师不是顶10个&#xff0c;是顶100个…

ajax 在新选卡打开,开始使用 AJAX 控制工具包 (VB) | Microsoft Docs

AJAX 控件工具包入门 (VB)05/12/2009本文内容了解开始使用 AJAX 控制工具包所需的所有知识。AJAX 控制工具包包含 30 多个免费控件&#xff0c;可用于ASP.NET应用程序中。 在本教程中&#xff0c;您将了解如何下载 AJAX 控件工具包&#xff0c;并将工具包控件添加到可视化工作室…

IBM斥资340亿美元完成收购红帽;亚马逊云计算Q2营收数据新鲜出炉;甲骨文推出Oracle专用自治数据库云……...

关注并标星星CSDN云计算极客头条&#xff1a;速递、最新、绝对有料。这里有企业新动、这里有业界要闻&#xff0c;打起十二分精神&#xff0c;紧跟fashion你可以的&#xff01;每周三次&#xff0c;打卡即read更快、更全了解泛云圈精彩newsgo go go Waymo应用&#xff08;图片来…