• 欢迎访问年轻的斯基网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏年轻的斯基吧

5分钟搞明白Xamarin.Forms读取Android和iOS通讯录及源码放送

建站知识 liam 6个月前 (03-30) 96次浏览 0个评论 扫描二维码

>

Xamarin.Forms在国内的开发者相对较少,除了官方的文档之外,网上很少有的,5分钟搞明白Xamarin.Forms读取Android和iOS通讯录及源码放送这篇文章是Xamarin.Forms开发安卓和苹果应用读取通讯录的实战代码。

Xamarin.Forms在国内的开发者相对较少,除了官方的文档之外,网上很少有的,5分钟搞明白Xamarin.Forms读取Android和iOS通讯录及源码放送这篇文章是Xamarin.Forms读取通讯录的实战代码。

一、功能说明

阅读导航 展开
5分钟搞明白Xamarin.Forms读取Android和iOS通讯录及源码放送
Xamarin.Forms读取Android和iOS通讯录

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

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

5分钟搞明白Xamarin.Forms读取Android和iOS通讯录及源码放送
Xamarin.Forms读取Android和iOS通讯录

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

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

5分钟搞明白Xamarin.Forms读取Android和iOS通讯录及源码放送
Xamarin.Forms读取Android和iOS通讯录

二、代码实现

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

namespace TerminalMACS.Clients.App.Models {     /// <summary>     /// 通讯录     /// </summary>     public class Contact     {         /// <summary>         /// 获取或者设置名称         /// </summary>         public string Name { get; set; }         /// <summary>         /// 获取或者设置 头像         /// </summary>         public string Image { get; set; }         /// <summary>         /// 获取或者设置 邮箱地址         /// </summary>         public string[] Emails { get; set; }         /// <summary>         /// 获取或者设置 手机号码         /// </summary>         public string[] PhoneNumbers { get; set; }     } }

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

包括:

  • 一个通讯录获取请求接口:RetrieveContactsAsync
  • 一个读取一条通讯结果通知事件:OnContactLoaded
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using TerminalMACS.Clients.App.Models;  namespace TerminalMACS.Clients.App.Services {     /// <summary>     /// 通讯录事件参数     /// </summary>     public class ContactEventArgs:EventArgs     {         public Contact Contact { get; }         public ContactEventArgs(Contact contact)         {             Contact = contact;         }     }      /// <summary>     /// 通讯录服务接口,android和iOS终端具体的通讯录获取服务需要继承此接口     /// </summary>     public interface IContactsService     {         /// <summary>         /// 读取一条数据通知         /// </summary>         event EventHandler<ContactEventArgs> OnContactLoaded;         /// <summary>         /// 是否正在加载         /// </summary>         bool IsLoading { get; }         /// <summary>         /// 尝试获取所有通讯录         /// </summary>         /// <param name="token"></param>         /// <returns></returns>         Task<IList<Contact>> 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 {     /// <summary>     /// 通讯录获取服务     /// </summary>     public class ContactsService : NSObject, IContactsService     {         const string ThumbnailPrefix = "thumb";          bool requestStop = false;          public event EventHandler<ContactEventArgs> OnContactLoaded;          bool _isLoading = false;         public bool IsLoading => _isLoading;          /// <summary>         /// 异步请求权限         /// </summary>         /// <returns></returns>         public async Task<bool> RequestPermissionAsync()         {             var status = CNContactStore.GetAuthorizationStatus(CNEntityType.Contacts);              Tuple<bool, NSError> authotization = new Tuple<bool, NSError>(status == CNAuthorizationStatus.Authorized, null);              if (status == CNAuthorizationStatus.NotDetermined)             {                 using (var store = new CNContactStore())                 {                     authotization = await store.RequestAccessAsync(CNEntityType.Contacts);                 }             }             return authotization.Item1;          }          /// <summary>         /// 异步请求通讯录,此方法由界面真正调用         /// </summary>         /// <param name="cancelToken"></param>         /// <returns></returns>         public async Task<IList<Contact>> RetrieveContactsAsync(CancellationToken? cancelToken = null)         {             requestStop = false;              if (!cancelToken.HasValue)                 cancelToken = CancellationToken.None;              // 我们创建了一个十进制的TaskCompletionSource             var taskCompletionSource = new TaskCompletionSource<IList<Contact>>();              // 在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;          }          /// <summary>         /// 异步加载通讯录,具体的通讯录读取方法         /// </summary>         /// <returns></returns>         async Task<IList<Contact>> LoadContactsAsync()         {             IList<Contact> contacts = new List<Contact>();             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分钟搞明白Xamarin.Forms读取Android和iOS通讯录及源码放送
Xamarin.Forms读取Android和iOS通讯录

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

<uses-permission android:name="android.permission.READ_CONTACTS"/>

完整权限配置如下

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.terminalmacs.clients.app">  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />  <application android:label="TerminalMACS.Clients.App.Android"></application>  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />   <uses-permission android:name="android.permission.READ_CONTACTS"/>   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> </manifest>

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

using 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 {     /// <summary>     /// 通讯录获取服务     /// </summary>     public class ContactsService : IContactsService     {         const string ThumbnailPrefix = "thumb";         bool stopLoad = false;         static TaskCompletionSource<bool> contactPermissionTcs;         public string TAG         {             get             {                 return "MainActivity";             }         }         bool _isLoading = false;         public bool IsLoading => _isLoading;         //权限请求状态码         public const int RequestContacts = 1239;         /// <summary>         /// 获取通讯录需要的请求权限         /// </summary>         static string[] PermissionsContact = {             Manifest.Permission.ReadContacts         };          public event EventHandler<ContactEventArgs> OnContactLoaded;         /// <summary>         /// 异步请求通讯录权限         /// </summary>         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);             }         }          /// <summary>         /// 收到用户响应请求权限操作后的结果         /// </summary>         /// <param name="requestCode"></param>         /// <param name="permissions"></param>         /// <param name="grantResults"></param>         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);                 }              }         }          /// <summary>         /// 异步请求权限         /// </summary>         /// <returns></returns>         public async Task<bool> RequestPermissionAsync()         {             contactPermissionTcs = new TaskCompletionSource<bool>();              // 验证是否已授予所有必需的通讯录权限。             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;         }          /// <summary>         /// 异步请求通讯录,此方法由界面真正调用         /// </summary>         /// <param name="cancelToken"></param>         /// <returns></returns>         public async Task<IList<Contact>> RetrieveContactsAsync(CancellationToken? cancelToken = null)         {             stopLoad = false;              if (!cancelToken.HasValue)                 cancelToken = CancellationToken.None;              // 我们创建了一个十进制的TaskCompletionSource             var taskCompletionSource = new TaskCompletionSource<IList<Contact>>();              // 在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;         }          /// <summary>         /// 异步加载通讯录,具体的通讯录读取方法         /// </summary>         /// <returns></returns>         async Task<IList<Contact>> LoadContactsAsync()         {             IList<Contact> contacts = new List<Contact>();             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;          }          /// <summary>         /// 读取一条通讯录数据         /// </summary>         /// <param name="cursor"></param>         /// <param name="ctx"></param>         /// <returns></returns>         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;         }          /// <summary>         /// 读取联系人电话号码         /// </summary>         /// <param name="ctx"></param>         /// <param name="contactId"></param>         /// <returns></returns>         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();         }          /// <summary>         /// 读取联系人邮箱地址         /// </summary>         /// <param name="ctx"></param>         /// <param name="contactId"></param>         /// <returns></returns>         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();         }          IEnumerable<string> 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.cs

using 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.cs

using 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 {     /// <summary>     /// 通讯录ViewModel     /// </summary>     public class ContactViewModel : BaseViewModel     {         /// <summary>         /// 通讯录服务接口         /// </summary>         IContactsService _contactService;         /// <summary>         /// 标题         /// </summary>         public new string Title => "通讯录";         private string _SearchText;         /// <summary>         /// 搜索关键字         /// </summary>         public string SearchText         {             get { return _SearchText; }             set             {                 SetProperty(ref _SearchText, value);             }         }         /// <summary>         /// 通讯录搜索命令         /// </summary>         public ICommand RaiseSearchCommand { get; }         /// <summary>         /// 通讯录列表         /// </summary>         public ObservableCollection<Contact> Contacts { get; set; }         private List<Contact> _FilteredContacts;         /// <summary>         /// 通讯录过滤列表         /// </summary>         public List<Contact> FilteredContacts          {             get { return _FilteredContacts; }             set             {                 SetProperty(ref _FilteredContacts, value);             }         }         public ContactViewModel(IContactsService contactService)         {             _contactService = contactService;             Contacts = new ObservableCollection<Contact>();             Xamarin.Forms.BindingBase.EnableCollectionSynchronization(Contacts, null, ObservableCollectionCallback);             _contactService.OnContactLoaded += OnContactLoaded;             LoadContacts();             RaiseSearchCommand = new Command(RaiseSearchHandle);         }          /// <summary>         /// 过滤通讯录         /// </summary>         void RaiseSearchHandle()         {             if (string.IsNullOrEmpty(SearchText))             {                 FilteredContacts = Contacts.ToList();                 return;             }              Func<Contact, bool> 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();         }          /// <summary>         /// BindingBase.EnableCollectionSynchronization 为集合启用跨线程更新         /// </summary>         /// <param name="collection"></param>         /// <param name="context"></param>         /// <param name="accessMethod"></param>         /// <param name="writeAccess"></param>         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();             }         }          /// <summary>         /// 收到事件通知,读取一条通讯录信息         /// </summary>         /// <param name="sender"></param>         /// <param name="e"></param>         private void OnContactLoaded(object sender, ContactEventArgs e)         {             Contacts.Add(e.Contact);             RaiseSearchHandle();         }          /// <summary>         /// 异步读取终端通讯录         /// </summary>         /// <returns></returns>         async Task LoadContacts()         {             try             {                 await _contactService.RetrieveContactsAsync();             }             catch (TaskCanceledException)             {                 Console.WriteLine("任务已经取消");             }         }     } }

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

<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"              xmlns:d="http://xamarin.com/schemas/2014/forms/design"              xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"              mc:Ignorable="d"              Title="{Binding Title}"              x:Class="TerminalMACS.Clients.App.Views.ContactPage"              ios:Page.UseSafeArea="true">     <ContentPage.Content>         <StackLayout>             <SearchBar x:Name="filterText"                         HeightRequest="40"                         Text="{Binding SearchText}"                        SearchCommand="{Binding RaiseSearchCommand}"/>             <ListView   ItemsSource="{Binding FilteredContacts}"                         HasUnevenRows="True">                 <ListView.ItemTemplate>                     <DataTemplate>                         <ViewCell>                             <StackLayout Padding="10"                                          Orientation="Horizontal">                                 <Image  Source="{Binding Image}"                                         VerticalOptions="Center"                                         x:Name="image"                                         Aspect="AspectFit"                                         HeightRequest="60"/>                                 <StackLayout VerticalOptions="Center">                                     <Label Text="{Binding Name}"                                        FontAttributes="Bold"/>                                     <Label Text="{Binding PhoneNumbers[0]}"/>                                     <Label Text="{Binding Emails[0]}"/>                                 </StackLayout>                             </StackLayout>                         </ViewCell>                     </DataTemplate>                 </ListView.ItemTemplate>             </ListView>         </StackLayout>     </ContentPage.Content> </ContentPage>

三、源码获取

已编译的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地址等。

Xamarin.Forms读取Android和iOS通讯录,原文标题:Xamarin.Forms读取并展示Android和iOS通讯录 – TerminalMACS客户端,点击这里阅读原文

本站最新优惠

年轻的斯基 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:5分钟搞明白Xamarin.Forms读取Android和iOS通讯录及源码放送
喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址