新网站如何做排名,领创科技网站开发,免费的crm的app,成都有哪些比较做网站比较好的本文同步更新地址#xff1a;https://dotnet9.com/11520.htmlhttps://terminalmacs.com/861.html阅读导航#xff1a;一、功能说明二、代码实现三、源码获取四、参考资料五、后面计划一、功能说明完整思维导图#xff1a;https://github.com/dotnet9/TerminalMACS/blob/mast… 本文同步更新地址https://dotnet9.com/11520.htmlhttps://terminalmacs.com/861.html阅读导航一、功能说明二、代码实现三、源码获取四、参考资料五、后面计划一、功能说明完整思维导图https://github.com/dotnet9/TerminalMACS/blob/master/docs/TerminalMACS.xmind 本文介绍图中右侧画红圈处的功能即使用Xamarin.Forms获取和展示Android和iOS的通讯录信息下面是最终效果由于使用的是真实手机所以联系人姓名及电话号码打码显示。并简单的进行了搜索功能处理之所以说简单是因为通讯录列表是全部读取出来了搜索是直接从此列表进行过滤的。下图来自https://www.xamboy.com/2019/10/10/getting-phone-contacts-in-xamarin-forms/本功能是参考此文所写所以直接引用文中的图片。二、代码实现1、共享库工程创建联系人实体类Contacts.csnamespace TerminalMACS.Clients.App.Models{ /// /// 通讯录 /// public class Contact { /// /// 获取或者设置名称 /// public string Name { get; set; } /// /// 获取或者设置 头像 /// public string Image { get; set; } /// /// 获取或者设置 邮箱地址 /// public string[] Emails { get; set; } /// /// 获取或者设置 手机号码 /// public string[] PhoneNumbers { get; set; } }}2、共享库创建通讯录服务接口IContactsService.cs包括一个通讯录获取请求接口RetrieveContactsAsync一个读取一条通讯结果通知事件OnContactLoadedusing System;using System.Collections.Generic;using System.Threading;using System.Threading.Tasks;using TerminalMACS.Clients.App.Models;namespace TerminalMACS.Clients.App.Services{ /// /// 通讯录事件参数 /// public class ContactEventArgs:EventArgs { public Contact Contact { get; } public ContactEventArgs(Contact contact) { Contact contact; } } /// /// 通讯录服务接口android和iOS终端具体的通讯录获取服务需要继承此接口 /// public interface IContactsService { /// /// 读取一条数据通知 /// event EventHandler OnContactLoaded; /// /// 是否正在加载 /// bool IsLoading { get; } /// /// 尝试获取所有通讯录 /// /// /// Task RetrieveContactsAsync(CancellationToken? token null); }}3、iOS工程中添加通讯录服务实现IContactsService接口using Contacts;using Foundation;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Threading;using System.Threading.Tasks;using TerminalMACS.Clients.App.Models;using TerminalMACS.Clients.App.Services;namespace TerminalMACS.Clients.App.iOS.Services{ /// /// 通讯录获取服务 /// public class ContactsService : NSObject, IContactsService { const string ThumbnailPrefix thumb; bool requestStop false; public event EventHandler OnContactLoaded; bool _isLoading false; public bool IsLoading _isLoading; /// /// 异步请求权限 /// /// public async Taskbool RequestPermissionAsync() { var status CNContactStore.GetAuthorizationStatus(CNEntityType.Contacts); Tuplebool, NSError authotization new Tuplebool, NSError(status CNAuthorizationStatus.Authorized, null); if (status CNAuthorizationStatus.NotDetermined) { using (var store new CNContactStore()) { authotization await store.RequestAccessAsync(CNEntityType.Contacts); } } return authotization.Item1; } /// /// 异步请求通讯录此方法由界面真正调用 /// /// /// public async Task RetrieveContactsAsync(CancellationToken? cancelToken null) { requestStop false; if (!cancelToken.HasValue) cancelToken CancellationToken.None; // 我们创建了一个十进制的TaskCompletionSource var taskCompletionSource new TaskCompletionSource(); // 在cancellationToken中注册lambda cancelToken.Value.Register(() { // 我们收到一条取消消息取消TaskCompletionSource.Task requestStop true; taskCompletionSource.TrySetCanceled(); }); _isLoading true; var task LoadContactsAsync(); // 等待两个任务中的第一个任务完成 var completedTask await Task.WhenAny(task, taskCompletionSource.Task); _isLoading false; return await completedTask; } /// /// 异步加载通讯录具体的通讯录读取方法 /// /// async Task LoadContactsAsync() { IList contacts new List(); var hasPermission await RequestPermissionAsync(); if (hasPermission) { NSError error null; var keysToFetch new[] { CNContactKey.PhoneNumbers, CNContactKey.GivenName, CNContactKey.FamilyName, CNContactKey.EmailAddresses, CNContactKey.ImageDataAvailable, CNContactKey.ThumbnailImageData }; var request new CNContactFetchRequest(keysToFetch: keysToFetch); request.SortOrder CNContactSortOrder.GivenName; using (var store new CNContactStore()) { var result store.EnumerateContacts(request, out error, new CNContactStoreListContactsHandler((CNContact c, ref bool stop) { string path null; if (c.ImageDataAvailable) { path path Path.Combine(Path.GetTempPath(), ${ThumbnailPrefix}-{Guid.NewGuid()}); if (!File.Exists(path)) { var imageData c.ThumbnailImageData; imageData?.Save(path, true); } } var contact new Contact() { Name string.IsNullOrEmpty(c.FamilyName) ? c.GivenName : ${c.GivenName} {c.FamilyName}, Image path, PhoneNumbers c.PhoneNumbers?.Select(p p?.Value?.StringValue).ToArray(), Emails c.EmailAddresses?.Select(p p?.Value?.ToString()).ToArray(), }; if (!string.IsNullOrWhiteSpace(contact.Name)) { OnContactLoaded?.Invoke(this, new ContactEventArgs(contact)); contacts.Add(contact); } stop requestStop; })); } } return contacts; } }}4、在iOS工程中的Info.plist文件添加通讯录权限使用说明5、在Android工程中添加读取通讯录权限配置AndroidManifest.xml android:nameandroid.permission.READ_CONTACTS/完整权限配置如下 xmlns:androidhttp://schemas.android.com/apk/res/android android:versionCode1 android:versionName1.0 packagecom.companyname.terminalmacs.clients.app android:minSdkVersion21 android:targetSdkVersion28 / android:labelTerminalMACS.Clients.App.Android android:nameandroid.permission.ACCESS_NETWORK_STATE / android:nameandroid.permission.READ_CONTACTS/ android:nameandroid.permission.WRITE_EXTERNAL_STORAGE /6、在Android工程中添加通讯录服务实现IContactServer接口ContactsService.csusing Acr.UserDialogs;using Android;using Android.App;using Android.Content;using Android.Content.PM;using Android.Database;using Android.Provider;using Android.Runtime;using Android.Support.V4.App;using Plugin.CurrentActivity;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Threading;using System.Threading.Tasks;using TerminalMACS.Clients.App.Models;using TerminalMACS.Clients.App.Services;namespace TerminalMACS.Clients.App.Droid.Services{ /// /// 通讯录获取服务 /// public class ContactsService : IContactsService { const string ThumbnailPrefix thumb; bool stopLoad false; static TaskCompletionSourcebool contactPermissionTcs; public string TAG { get { return MainActivity; } } bool _isLoading false; public bool IsLoading _isLoading; //权限请求状态码 public const int RequestContacts 1239; /// /// 获取通讯录需要的请求权限 /// static string[] PermissionsContact { Manifest.Permission.ReadContacts }; public event EventHandler OnContactLoaded; /// /// 异步请求通讯录权限 /// async void RequestContactsPermissions() { //检查是否可以弹出申请读、写通讯录权限 if (ActivityCompat.ShouldShowRequestPermissionRationale(CrossCurrentActivity.Current.Activity, Manifest.Permission.ReadContacts) || ActivityCompat.ShouldShowRequestPermissionRationale(CrossCurrentActivity.Current.Activity, Manifest.Permission.WriteContacts)) { // 如果未授予许可请向用户提供其他理由用户将从使用权限的附加上下文中受益。 // 例如如果请求先前被拒绝。 await UserDialogs.Instance.AlertAsync(通讯录权限, 此操作需要“通讯录”权限, 确定); } else { // 尚未授予通讯录权限。直接请求这些权限。 ActivityCompat.RequestPermissions(CrossCurrentActivity.Current.Activity, PermissionsContact, RequestContacts); } } /// /// 收到用户响应请求权限操作后的结果 /// /// /// /// public static void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) { if (requestCode RequestContacts) { // 我们请求了多个通讯录权限因此需要检查相关的所有权限 if (PermissionUtil.VerifyPermissions(grantResults)) { // 已授予所有必需的权限显示联系人片段。 contactPermissionTcs.TrySetResult(true); } else { contactPermissionTcs.TrySetResult(false); } } } /// /// 异步请求权限 /// /// public async Taskbool RequestPermissionAsync() { contactPermissionTcs new TaskCompletionSourcebool(); // 验证是否已授予所有必需的通讯录权限。 if (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(CrossCurrentActivity.Current.Activity, Manifest.Permission.ReadContacts) ! (int)Permission.Granted || Android.Support.V4.Content.ContextCompat.CheckSelfPermission(CrossCurrentActivity.Current.Activity, Manifest.Permission.WriteContacts) ! (int)Permission.Granted) { // 尚未授予通讯录权限。 RequestContactsPermissions(); } else { // 已授予通讯录权限。 contactPermissionTcs.TrySetResult(true); } return await contactPermissionTcs.Task; } /// /// 异步请求通讯录此方法由界面真正调用 /// /// /// public async Task RetrieveContactsAsync(CancellationToken? cancelToken null) { stopLoad false; if (!cancelToken.HasValue) cancelToken CancellationToken.None; // 我们创建了一个十进制的TaskCompletionSource var taskCompletionSource new TaskCompletionSource(); // 在cancellationToken中注册lambda cancelToken.Value.Register(() { // 我们收到一条取消消息取消TaskCompletionSource.Task stopLoad true; taskCompletionSource.TrySetCanceled(); }); _isLoading true; var task LoadContactsAsync(); // 等待两个任务中的第一个任务完成 var completedTask await Task.WhenAny(task, taskCompletionSource.Task); _isLoading false; return await completedTask; } /// /// 异步加载通讯录具体的通讯录读取方法 /// /// async Task LoadContactsAsync() { IList contacts new List(); var hasPermission await RequestPermissionAsync(); if (!hasPermission) { return contacts; } var uri ContactsContract.Contacts.ContentUri; var ctx Application.Context; await Task.Run(() { // 暂时只请求通讯录Id、DisplayName、PhotoThumbnailUri可以扩展 var cursor ctx.ApplicationContext.ContentResolver.Query(uri, new string[] { ContactsContract.Contacts.InterfaceConsts.Id, ContactsContract.Contacts.InterfaceConsts.DisplayName, ContactsContract.Contacts.InterfaceConsts.PhotoThumbnailUri }, null, null, ${ContactsContract.Contacts.InterfaceConsts.DisplayName} ASC); if (cursor.Count 0) { while (cursor.MoveToNext()) { var contact CreateContact(cursor, ctx); if (!string.IsNullOrWhiteSpace(contact.Name)) { // 读取出一条即通知界面展示 OnContactLoaded?.Invoke(this, new ContactEventArgs(contact)); contacts.Add(contact); } if (stopLoad) break; } } }); return contacts; } /// /// 读取一条通讯录数据 /// /// /// /// Contact CreateContact(ICursor cursor, Context ctx) { var contactId GetString(cursor, ContactsContract.Contacts.InterfaceConsts.Id); var numbers GetNumbers(ctx, contactId); var emails GetEmails(ctx, contactId); var uri GetString(cursor, ContactsContract.Contacts.InterfaceConsts.PhotoThumbnailUri); string path null; if (!string.IsNullOrEmpty(uri)) { try { using (var stream Android.App.Application.Context.ContentResolver.OpenInputStream(Android.Net.Uri.Parse(uri))) { path Path.Combine(Path.GetTempPath(), ${ThumbnailPrefix}-{Guid.NewGuid()}); using (var fstream new FileStream(path, FileMode.Create)) { stream.CopyTo(fstream); fstream.Close(); } stream.Close(); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } var contact new Contact { Name GetString(cursor, ContactsContract.Contacts.InterfaceConsts.DisplayName), Emails emails, Image path, PhoneNumbers numbers, }; return contact; } /// /// 读取联系人电话号码 /// /// /// /// string[] GetNumbers(Context ctx, string contactId) { var key ContactsContract.CommonDataKinds.Phone.Number; var cursor ctx.ApplicationContext.ContentResolver.Query( ContactsContract.CommonDataKinds.Phone.ContentUri, null, ContactsContract.CommonDataKinds.Phone.InterfaceConsts.ContactId ?, new[] { contactId }, null ); return ReadCursorItems(cursor, key)?.ToArray(); } /// /// 读取联系人邮箱地址 /// /// /// /// string[] GetEmails(Context ctx, string contactId) { var key ContactsContract.CommonDataKinds.Email.InterfaceConsts.Data; var cursor ctx.ApplicationContext.ContentResolver.Query( ContactsContract.CommonDataKinds.Email.ContentUri, null, ContactsContract.CommonDataKinds.Email.InterfaceConsts.ContactId ?, new[] { contactId }, null); return ReadCursorItems(cursor, key)?.ToArray(); } IEnumerablestring ReadCursorItems(ICursor cursor, string key) { while (cursor.MoveToNext()) { var value GetString(cursor, key); yield return value; } cursor.Close(); } string GetString(ICursor cursor, string key) { return cursor.GetString(cursor.GetColumnIndex(key)); } }}需要添加 Plugin.CurrentActivity 和 Acr.UserDialogs 包。7、Android工程添加权限处理判断类Permission.Util.csusing Android.Content.PM;namespace TerminalMACS.Clients.App.Droid{ public static class PermissionUtil { /** * 通过验证给定数组中的每个条目的值是否为Permission.Granted检查是否已授予所有给定权限。 * * See Activity#onRequestPermissionsResult (int, String[], int[]) */ public static bool VerifyPermissions(Permission[] grantResults) { // 必须至少检查一个结果. if (grantResults.Length 1) return false; // 验证是否已授予每个必需的权限否则返回false. foreach (Permission result in grantResults) { if (result ! Permission.Granted) { return false; } } return true; } }}MainActivity.OnRequestPermissionResult是权限申请结果处理函数在此函数中调用ContactsService.OnRequestPermissionsResult通知通讯录服务权限处理结果。MainActivity.csusing Acr.UserDialogs;using Android.App;using Android.Content.PM;using Android.OS;using Android.Runtime;using TerminalMACS.Clients.App.Droid.Services;using TerminalMACS.Clients.App.Services;namespace TerminalMACS.Clients.App.Droid{ [Activity(Label TerminalMACS.Clients.App, Icon mipmap/icon, Theme style/MainTheme, MainLauncher true, ConfigurationChanges ConfigChanges.ScreenSize | ConfigChanges.Orientation)] public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity { IContactsService contactsService new ContactsService(); protected override void OnCreate(Bundle savedInstanceState) { TabLayoutResource Resource.Layout.Tabbar; ToolbarResource Resource.Layout.Toolbar; base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); global::Xamarin.Forms.Forms.Init(this, savedInstanceState); UserDialogs.Init(() this); // 将通讯录服务实例传递给共享库由共享库使用读取通讯录接口 LoadApplication(new App(contactsService)); } public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) { Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults); // 通讯录服务处理权限请求结果 ContactsService.OnRequestPermissionsResult(requestCode, permissions, grantResults); base.OnRequestPermissionsResult(requestCode, permissions, grantResults); } }}8、创建通讯录ViewModel并使用通讯录服务using System;using System.Collections;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Linq;using System.Threading.Tasks;using System.Windows.Input;using TerminalMACS.Clients.App.Models;using TerminalMACS.Clients.App.Services;using Xamarin.Forms;namespace TerminalMACS.Clients.App.ViewModels{ /// /// 通讯录ViewModel /// public class ContactViewModel : BaseViewModel { /// /// 通讯录服务接口 /// IContactsService _contactService; /// /// 标题 /// public new string Title 通讯录; private string _SearchText; /// /// 搜索关键字 /// public string SearchText { get { return _SearchText; } set { SetProperty(ref _SearchText, value); } } /// /// 通讯录搜索命令 /// public ICommand RaiseSearchCommand { get; } /// /// 通讯录列表 /// public ObservableCollection Contacts { get; set; } private List _FilteredContacts; /// /// 通讯录过滤列表 /// public List FilteredContacts { get { return _FilteredContacts; } set { SetProperty(ref _FilteredContacts, value); } } public ContactViewModel(IContactsService contactService) { _contactService contactService; Contacts new ObservableCollection(); Xamarin.Forms.BindingBase.EnableCollectionSynchronization(Contacts, null, ObservableCollectionCallback); _contactService.OnContactLoaded OnContactLoaded; LoadContacts(); RaiseSearchCommand new Command(RaiseSearchHandle); } /// /// 过滤通讯录 /// void RaiseSearchHandle() { if (string.IsNullOrEmpty(SearchText)) { FilteredContacts Contacts.ToList(); return; } Funcbool checkContact (s) { if (!string.IsNullOrWhiteSpace(s.Name) s.Name.ToLower().Contains(SearchText.ToLower())) { return true; } else if (s.PhoneNumbers.Length 0 s.PhoneNumbers.ToList().Exists(cu cu.ToString().Contains(SearchText))) { return true; } return false; }; FilteredContacts Contacts.ToList().Where(checkContact).ToList(); } /// /// BindingBase.EnableCollectionSynchronization 为集合启用跨线程更新 /// /// /// /// /// void ObservableCollectionCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess) { // lock ensures that only one thread access the collection at a time lock (collection) { accessMethod?.Invoke(); } } /// /// 收到事件通知读取一条通讯录信息 /// /// /// private void OnContactLoaded(object sender, ContactEventArgs e) { Contacts.Add(e.Contact); RaiseSearchHandle(); } /// /// 异步读取终端通讯录 /// /// async Task LoadContacts() { try { await _contactService.RetrieveContactsAsync(); } catch (TaskCanceledException) { Console.WriteLine(任务已经取消); } } }}9、添加通讯录页面展示通讯录数据?xml version1.0 encodingutf-8 ?http://xamarin.com/schemas/2014/forms xmlns:xhttp://schemas.microsoft.com/winfx/2009/xaml xmlns:mchttp://schemas.openxmlformats.org/markup-compatibility/2006 xmlns:dhttp://xamarin.com/schemas/2014/forms/design xmlns:iosclr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assemblyXamarin.Forms.Core mc:Ignorabled Title{Binding Title} x:ClassTerminalMACS.Clients.App.Views.ContactPage ios:Page.UseSafeAreatrue filterText HeightRequest40 Text{Binding SearchText} SearchCommand{Binding RaiseSearchCommand}/ {Binding FilteredContacts} HasUnevenRowsTrue 10 OrientationHorizontal {Binding Image} VerticalOptionsCenter x:Nameimage AspectAspectFit HeightRequest60/ Center {Binding Name} FontAttributesBold/ {Binding PhoneNumbers[0]}/ {Binding Emails[0]}/三、源码获取1.完整源码https://github.com/dotnet9/TerminalMACS2.Android客户端可成功取得通讯录数据并可查询已编译的Android客户端https://terminalmacs.com/terminalmacs-clients-app-android3.iOS读取通讯录功能代码也已添加但由于本人没有iOS测试环境所以未验证有条件的朋友可以测试下iOS的通讯录读取功能如果代码不起作用可参考本文参考的文章检查iOS代码。四、参考资料Getting phone contacts in Xamarin Formshttps://www.xamboy.com/2019/10/10/getting-phone-contacts-in-xamarin-forms/参考文章末尾有源代码链接。五、后面计划Xamarin.Forms客户端基本信息获取比如IMEI、IMSI、本机号码、Mac地址等。