新闻详情

新闻详情

首页 / 资讯中心 / 详情

Xamarin Forms 调用设备相机拍照并检索图片完整示例

发布时间:2026/9/29 14:10:15来源:尧图网络
Xamarin Forms 调用设备相机拍照并检索图片完整示例
简介这份资源是面向 C# 与 Xamarin.Forms 开发者的相机集成示例适合正在构建跨平台移动应用、需要调用设备原生拍照能力的中级开发者参考。示例围绕 DependencyService 接口设计展开在共享项目中定义 ICameraService再分别用 UIImagePickerController 与 MediaStore.ACTION_IMAGE_CAPTURE 完成 iOS、Android 平台实现并覆盖图片流处理、运行时权限申请、XAML 布局与异常反馈等环节帮助读者理解共享代码与平台特定代码的协作方式。压缩包共 83 个文件约 286KB以 31 个 png 截图、23 个 cs 源码、3 个 xaml 界面文件及 csproj、sln、plist 等工程配置为主另含 README 说明结构完整可直接运行。目前已有 307 人学习适合作为跨平台相机功能落地的参考范例。1. 从一次“拍完照找不到图”的翻车说起Xamarin Forms 里调用设备相机到底难在哪很多做 Xamarin Forms 的团队都遇到过这个场景需求文档上写着“用户点击按钮调起设备相机拍照拍完把图片显示在页面上并上传”。听起来三行代码的事真上手才发现——Android 上拍完照返回的Intent里拿不到完整图片iOS 上权限弹窗没配描述直接闪退模拟器上相机根本打不开拍完的图存哪、怎么读、怎么在Image控件里显示每一步都能卡住半天。这个标题讲的正是这件事在 Xamarin Forms 应用程序里用设备相机检索拍摄并获取图片的完整示例。它解决的不是“怎么调一个 API”而是“跨 Android 和 iOS 两个平台怎么把拍照、存文件、读文件、显示图片这条链路跑通”。适合正在用 Xamarin Forms 做业务 App、需要接入拍照上传功能的开发者也适合想搞清楚MediaPlugin这类插件背后到底干了什么的人。下面我按自己踩过的顺序把选型、配置、代码和坑一条条讲清楚。2. 选型先立住为什么我最终用 MediaPlugin 而不是自己写平台代码2.1 三种常见做法的取舍在 Xamarin Forms 里调相机业内常见做法有三条路。第一条是纯依赖注入加平台特定代码Android 写Intent加OnActivityResultiOS 写UIImagePickerController通过DependencyService暴露给共享层。第二条是用Xamarin.Essentials里的MediaPicker它在新版本里已经能拍照和选图。第三条是用社区维护的Xamarin.Plugin.Media常叫 MediaPlugin一行CrossMedia.Current.TakePhotoAsync就能拿到文件。我一般会先看项目的最低支持版本。如果项目已经全面用Xamarin.Essentials优先用它自带的MediaPicker少引一个包。但很多存量项目还停在较早的 Forms 版本Xamarin.Essentials的MediaPicker在部分 Android 机型上对存储路径处理不一致这时候 MediaPlugin 反而更稳。自己写平台代码不是不行但 Android 的FileProvider、AndroidManifest权限、iOS 的Info.plist描述两套都要维护团队里没人愿意长期背这个包袱。方案代码量跨平台一致性维护成本适用场景平台特定代码 DependencyService大需自己保证高有特殊定制需求Xamarin.Essentials MediaPicker小较好低新项目、版本较新Xamarin.Plugin.Media小好低存量项目、需要稳定拍照选型理由说白了就一句拍照这件事的复杂度不在“调起相机”而在“拿到一个两个平台都能读的文件对象”。MediaPlugin 帮你把这层抹平了代价是引入一个第三方依赖需要留意它的版本和 Forms 版本匹配。2.2 权限与清单配置不配这两处代码写得再对也白搭Android 侧AndroidManifest.xml里至少要声明相机和存储相关权限。注意 Android 6.0 以后是运行时权限清单声明只是第一步真正弹窗要靠代码请求。下面是我项目里常用的清单片段。!-- Platforms/Android/AndroidManifest.xml -- manifest xmlns:androidhttp://schemas.android.com/apk/res/android uses-permission android:nameandroid.permission.CAMERA / uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE / uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE / uses-feature android:nameandroid.hardware.camera android:requiredfalse / application android:labelCameraDemo !-- Android 7.0 必须配置 FileProvider否则拍照后写文件会抛异常 -- provider android:nameandroidx.core.content.FileProvider android:authorities${applicationId}.fileprovider android:exportedfalse android:grantUriPermissionstrue meta-data android:nameandroid.support.FILE_PROVIDER_PATHS android:resourcexml/file_paths / /provider /application /manifestuses-feature里requiredfalse很关键写成true会让没有相机的设备比如部分平板、模拟器在应用商店里被过滤掉。FileProvider的authorities用${applicationId}.fileprovider是常见写法保证不同包名不冲突。对应的file_paths.xml放在Resources/xml下内容大致是!-- Platforms/Android/Resources/xml/file_paths.xml -- paths xmlns:androidhttp://schemas.android.com/apk/res/android external-path nameexternal_files path. / cache-path namecache_files path. / /pathsiOS 侧Info.plist必须加相机和相册的使用描述缺一个调用时直接崩溃而且崩溃日志不一定直观。!-- Platforms/iOS/Info.plist -- keyNSCameraUsageDescription/key string需要使用相机拍摄照片用于上传/string keyNSPhotoLibraryUsageDescription/key string需要访问相册以选择照片/string keyNSPhotoLibraryAddUsageDescription/key string需要保存拍摄的照片到相册/string提示NSCameraUsageDescription的文案会直接显示在系统权限弹窗上写清楚用途别写“测试”两个字审核和用户信任都过不去。2.3 共享层调用相机的最小代码配置齐了共享层代码其实很短。下面这段是我常用的拍照方法包含权限检查、拍照、拿文件、显示到Image控件。// 共享层 CameraService.cs using Plugin.Media; using Plugin.Media.Abstractions; using Xamarin.Forms; public async TaskImageSource TakePhotoAsync() { // 先初始化某些平台首次调用必须执行 await CrossMedia.Current.Initialize(); // 检查设备是否支持拍照模拟器上通常为 false if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported) { await Application.Current.MainPage.DisplayAlert(提示, 当前设备不支持拍照, 确定); return null; } // 关键参数CompressionQuality 控制压缩质量PhotoSize 控制分辨率 var file await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions { Directory CameraDemo, Name $photo_{DateTime.Now:yyyyMMddHHmmss}.jpg, PhotoSize PhotoSize.Medium, // Small/Medium/Large按上传带宽选 CompressionQuality 75, // 0-10075 是清晰度和体积的平衡点 SaveToAlbum false // 是否同时存到系统相册 }); if (file null) return null; // 用户取消拍照 // file.Path 是临时文件路径file.GetStream() 可直接读流上传 return ImageSource.FromStream(() file.GetStream()); }逻辑说明Initialize()在部分平台首次调用必须执行否则后续方法可能抛空引用。IsCameraAvailable在模拟器上返回 false这是正常的别以为是代码错了。StoreCameraMediaOptions里PhotoSize和CompressionQuality是最该调的两个参数——PhotoSize.Large在低端 Android 机上可能直接内存溢出CompressionQuality设 100 会让一张图好几 MB上传慢还费流量。file.GetStream()每次调用返回新流ImageSource.FromStream里用 lambda 包一层是标准写法避免流被提前释放。3. 把图片检索链路跑通从拍照到显示到上传的完整步骤3.1 页面层怎么接按钮、预览、状态管理共享层方法有了页面层要处理的是“点了按钮之后界面别卡死、拍完能预览、失败有提示”。我一般把拍照逻辑放在 ViewModel 里页面只负责绑定。下面是一个精简的 ViewModel 片段。// ViewModel/MainViewModel.cs public class MainViewModel : INotifyPropertyChanged { private ImageSource _photo; public ImageSource Photo { get _photo; set { _photo value; OnPropertyChanged(); } } public ICommand TakePhotoCommand { get; } public MainViewModel() { TakePhotoCommand new Command(async () await ExecuteTakePhoto()); } private bool _isBusy; private async Task ExecuteTakePhoto() { if (_isBusy) return; // 防止连点导致多次调起相机 _isBusy true; try { var service new CameraService(); Photo await service.TakePhotoAsync(); } catch (Exception ex) { // 真机上权限被拒、存储满都会走到这里 await Application.Current.MainPage.DisplayAlert(拍照失败, ex.Message, 确定); } finally { _isBusy false; } } // INotifyPropertyChanged 实现略 }_isBusy这个标志位是血泪经验不加它用户快速连点按钮Android 上会连续调起多个相机 Activity返回时OnActivityResult对不上号拿到的文件可能是上一次的。try/catch也不能省权限被用户拒绝时抛的异常信息虽然不友好但至少能让你在日志里定位。3.2 上传环节流怎么读、什么时候释放拍完照最终要上传。MediaFile提供了GetStream()和Path两种方式。用HttpClient上传时常见做法是构造MultipartFormDataContent。// 上传示例 public async Taskbool UploadAsync(MediaFile file, string url) { using (var client new HttpClient()) using (var content new MultipartFormDataContent()) using (var stream file.GetStream()) { var fileContent new StreamContent(stream); fileContent.Headers.ContentType new System.Net.Http.Headers.MediaTypeHeaderValue(image/jpeg); content.Add(fileContent, file, Path.GetFileName(file.Path)); var response await client.PostAsync(url, content); return response.IsSuccessStatusCode; } }参数说明MultipartFormDataContent的字段名file要和后端接口约定一致很多上传失败是字段名对不上。MediaTypeHeaderValue写image/jpeg如果拍照存的是 png 要相应改。using包住 stream 很重要MediaFile底层是临时文件流不释放Android 上临时文件删不掉拍几十张后存储就告急。上传大图前建议先压缩CompressionQuality在拍照阶段就压过一轮上传阶段一般不用再压。3.3 检索已拍图片从相册选图与路径读取标题里的“检索图片”除了拍照往往还包括从相册选已有图片。MediaPlugin 同样支持PickPhotoAsync。var file await CrossMedia.Current.PickPhotoAsync(new PickMediaOptions { PhotoSize PhotoSize.Medium, CompressionQuality 80 }); if (file ! null) { Photo ImageSource.FromStream(() file.GetStream()); }PickMediaOptions和拍照的StoreCameraMediaOptions参数含义类似但注意选图时PhotoSize会影响返回文件是否被重新编码——设成Medium时插件会把原图压缩后给你如果你需要保留原图设成PhotoSize.Full或直接用file.Path读原始文件。这里有个容易忽略的点PickPhotoAsync在 Android 上返回的路径可能是content://开头的 URI 而非文件路径直接拿Path去File.ReadAllBytes会失败必须用GetStream()。这是新手最常翻车的地方之一。4. 避坑与排查相机功能在真机上翻车的 5 个典型场景4.1 拍完照返回图片是黑的或者空的现象调起相机拍完TakePhotoAsync返回的MediaFile不为 null但GetStream()读出来是 0 字节或者显示一片黑。原因Android 上临时文件写入失败最常见是FileProvider的authorities和代码里不一致或者file_paths.xml没包含实际写入目录。另一个原因是存储权限被拒后插件没抛异常而是返回了一个空文件。解决先确认AndroidManifest里authorities的值和file_paths.xml的路径配置再在拍照前显式请求Permissions.Camera和Permissions.StorageWrite用Xamarin.Essentials的Permissions.RequestAsync检查返回值被拒就提示用户去设置里开。4.2 iOS 上一点按钮就闪退现象Android 正常iOS 调TakePhotoAsync直接崩溃日志里能看到NSCameraUsageDescription相关字样。原因Info.plist缺NSCameraUsageDescriptioniOS 在访问相机前会检查这个键没有就直接终止进程这是系统行为不是异常catch 不住。解决补齐NSCameraUsageDescription、NSPhotoLibraryUsageDescription、NSPhotoLibraryAddUsageDescription三个键。改完要清理重新编译增量编译有时不生效。4.3 模拟器上相机打不开现象IsCameraAvailable返回 false或者调起后黑屏。原因Android 模拟器默认没有相机硬件iOS 模拟器也不支持相机。这是预期行为。解决用真机调试。如果必须在模拟器上验证 UI 流程可以在代码里判断DeviceInfo.DeviceType DeviceType.Virtual时走一个假数据分支但别把这个分支带到生产。4.4 连续拍照后应用变卡甚至被杀现象拍了几张后应用响应变慢Android 上被系统回收。原因MediaFile的流没释放临时文件堆积或者PhotoSize.Large在低内存设备上解码大图导致 OOM。解决所有GetStream()用using包住PhotoSize按业务需要选MediumImage控件显示大图前先降采样别直接把原图塞进去。4.5 上传接口报 413 或超时现象小图能传大图报 413 Request Entity Too Large 或直接超时。原因CompressionQuality设太高或者PhotoSize用了Full单张图好几 MB。解决拍照阶段CompressionQuality控制在 70-80PhotoSize用Medium服务端如果有限制上传前再压一轮或者改用分片上传。别指望用户网络都好移动网络下 2MB 以上的图失败率明显上升。5. 进阶技巧把拍照封装成可测试、可替换的服务5.1 用接口隔离插件依赖直接在 ViewModel 里new CameraService()能跑但没法单元测试也没法在模拟器上替换成假实现。我一般抽一个接口。public interface IPhotoService { TaskMediaFile TakePhotoAsync(); TaskMediaFile PickPhotoAsync(); } public class MediaPluginPhotoService : IPhotoService { public async TaskMediaFile TakePhotoAsync() { await CrossMedia.Current.Initialize(); if (!CrossMedia.Current.IsCameraAvailable) return null; return await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions { PhotoSize PhotoSize.Medium, CompressionQuality 75 }); } // PickPhotoAsync 类似 }这样在测试项目里注入一个返回固定MediaFile的假实现就能在不依赖真机的情况下测 ViewModel 逻辑。参数集中在一个类里以后要换Xamarin.Essentials的MediaPicker只改这一个实现类。5.2 验证清单上线前逐条过一遍检查项AndroidiOS说明相机权限声明是是清单/plist 都要运行时权限请求是系统自动Android 6.0 必须代码请求FileProvider 配置是不适用authorities 要对上权限描述文案不适用是三个 key 都要真机测试是是模拟器测不了相机取消拍照处理是是返回 null 要判断流释放是是using 包住大图压缩是是控制质量和尺寸这张表我每次接相机功能都会过一遍漏一条就可能在某个机型上翻车。尤其是取消拍照返回 null 这一条很多代码没判断用户点取消后直接空引用崩溃。5.3 一个我自己的习惯我现在接任何 Xamarin Forms 的硬件相关功能第一件事不是写代码而是先把权限清单和真机调试环境准备好。相机这个功能代码量其实不大翻车基本都翻在配置和平台差异上。把IPhotoService抽出来、把参数集中、把验证清单过一遍后面换插件或者加新功能都省事。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

从0开始创建项目突然不会了:用 TaoToken 统一 Key 打通 Android Studio 新建工程配置 2026/9/29 15:09:23

从0开始创建项目突然不会了:用 TaoToken 统一 Key 打通 Android Studio 新建工程配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
简易画图工具的实现3 2026/9/29 15:09:23

简易画图工具的实现3

前言 继上篇笔记,当时鼠标监听器绑定在JFrame窗体上,会出现绘图坐标错位,同时任意三角形交互逻辑不完善。本次修复坐标偏移问题,完善自定义三角形绘制逻辑,使用flag状态变量控制多步绘图流程。 一、实现思路 这个画图工…

阅读更多 →
大庆集装箱供应企业价格公道不玩套路:正规厂家选择指南 2026/9/29 15:09:23

大庆集装箱供应企业价格公道不玩套路:正规厂家选择指南

大庆集装箱供应企业价格公道不玩套路:正规厂家选择指南在东北工程临建领域,找一家报价透明、适配寒地工况、供货及时的集装箱供应商,能帮项目省下不少沟通成本和隐性开支,哈尔滨市道外区冰城围挡加工厂是哈尔滨本地集生产、销售、…

阅读更多 →
佳木斯彩钢房专业制造商实力与用户口碑,价格公道不玩套路 2026/9/29 15:09:23

佳木斯彩钢房专业制造商实力与用户口碑,价格公道不玩套路

哈尔滨市道外区冰城围挡加工厂成立于2019年9月9日,是哈尔滨本地集生产、销售、租赁、回收于一体的一站式工程临建物资源头厂家,主营彩钢房、围挡、集装箱打包箱等工程临建物资,服务覆盖哈尔滨全市及黑龙江省内周边市县。从寒地工况出发的本地…

阅读更多 →
工业缺陷检测实战:小样本训练与漏检控制全流程 2026/9/29 15:09:23

工业缺陷检测实战:小样本训练与漏检控制全流程

工业缺陷检测实战:小样本训练与漏检控制全流程工业缺陷检测和"通用目标检测"是两回事:缺陷通常极小、样本极少、漏检代价极高。 用通用检测的套路(堆数据、只看 mAP)去做缺陷检测,最常见的结局是&#xff1a…

阅读更多 →
武侯区豆包搜索结果优化哪家强?正规服务商合作实力参考 2026/9/29 15:09:10

武侯区豆包搜索结果优化哪家强?正规服务商合作实力参考

武侯区做豆包搜索结果优化,找对本土服务商才能适配实际需求,不少本地中小实体企业都在找靠谱的合作方,今天就给大家整理正规服务商的合作实力参考方向。本土深耕更专业,适配本地搜索习惯更顺畅。 做豆包搜索结果优化,不…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉