Maui Blazor 安卓Android 多选照片以及快速显示照片
1. 本文感谢两位大佬提供相关教程,相关文章和具体实现原理请参考如下链接:
YU-CORE: MAUI Blazor 显示本地图片新思路:https://www.cnblogs.com/Yu-Core/p/17571292.html
2. 在MauiBlazor项目根目录新建Service文件夹,新建一个IPhotoPickerService.cs接口Interface进行调用:
using System; namespace AndroidPhotoPicker.Service { public interface IPhotoPickerService { /// <summary> /// Intent /// </summary> Task<Dictionary<string, string>> GetImageAsyncByIntent(); } }
3. 在Platforms-Android文件夹内新建AndroidPhotoPIckerService.cs:
using System; using Android.Content; using Android.Provider; using AndroidPhotoPicker.Service; using AndroidX.Activity.Result; using AndroidX.Activity.Result.Contract; namespace AndroidPhotoPicker.Platforms.Android { public class AndroidPhotoPickerService : IPhotoPickerService { /// <summary> /// Intent /// </summary> public Task<Dictionary<string, string>> GetImageAsyncByIntent() { Intent intent = new Intent(Intent.ActionPick); intent.SetDataAndType(MediaStore.Images.Media.ExternalContentUri, "image/*"); intent.PutExtra(Intent.ExtraAllowMultiple, true); MainActivity.Instance.StartActivityForResult(Intent.CreateChooser(intent, "Select Picture"), MainActivity.PickImageId); MainActivity.Instance.PickImageTaskCompletionSource = new TaskCompletionSource<Dictionary<string, string>>(); return MainActivity.Instance.PickImageTaskCompletionSource.Task; } } }
4. 在Platforms-Android文件夹内MainActivity.cs里添加如下代码:

using Android.App; using Android.Content.PM; using Android.OS; using AndroidX.Activity.Result; using AndroidX.Activity.Result.Contract; using AndroidX.DocumentFile.Provider; namespace AndroidPhotoPicker; [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] public class MainActivity : MauiAppCompatActivity { internal static MainActivity Instance { get; private set; } public static readonly int PickImageId = 1000; internal static ActivityResultLauncher PickMultipleMedia { get; private set; } public TaskCompletionSource<Dictionary<string, string>> PickImageTaskCompletionSource { set; get; } protected override void OnCreate(Bundle savedInstanceState) { Instance = this; PickMultipleMedia = Instance.RegisterForActivityResult(new ActivityResultContracts.PickMultipleVisualMedia(100), new ActivityResultCallback()); base.OnCreate(savedInstanceState); } protected Dictionary<string, string> GetImageDicFromUris(List<Android.Net.Uri> list) { Dictionary<string, string> fileList = new Dictionary<string, string>(); for (int i = 0; i < list.Count; i++) { var imageUri = list[i]; var documentFile = DocumentFile.FromSingleUri(Instance, imageUri); if (documentFile != null) { using (var stream = Instance.ContentResolver.OpenInputStream(imageUri)) { string tempFolderPath = Path.GetTempPath(); string filePath = Path.Combine(tempFolderPath, $"{Guid.NewGuid()}.{Path.GetExtension(documentFile.Name)}"); using (FileStream fs = new FileStream(filePath, FileMode.Create)) { stream.CopyTo(fs); fileList.Add($"{Guid.NewGuid()}.{Path.GetExtension(documentFile.Name)}", filePath); Console.WriteLine(filePath); } } } } return fileList; } protected override void OnActivityResult(int requestCode, Result resultCode, Android.Content.Intent intent) { base.OnActivityResult(requestCode, resultCode, intent); if (requestCode == PickImageId) { if ((resultCode == Result.Ok) && (intent != null)) { var imageNames = intent.ClipData; if (imageNames != null) { var uris = new List<Android.Net.Uri>(); for (int i = 0; i < imageNames.ItemCount; i++) { var imageUri = imageNames.GetItemAt(i).Uri; uris.Add(imageUri); } var fileList = Instance.GetImageDicFromUris(uris); PickImageTaskCompletionSource.SetResult(fileList); } } else { PickImageTaskCompletionSource.SetResult(new Dictionary<string, string>()); } } } private class ActivityResultCallback : Java.Lang.Object, IActivityResultCallback { public void OnActivityResult(Java.Lang.Object p0) { if (!p0.Equals(new Android.Runtime.JavaList())) { var list = (Android.Runtime.JavaList)p0; if (!list.IsEmpty) { var uris = list.Cast<Android.Net.Uri>().ToList(); var fileList = Instance.GetImageDicFromUris(uris); Instance.PickImageTaskCompletionSource.SetResult(fileList); } else { Instance.PickImageTaskCompletionSource.SetResult(new Dictionary<string, string>()); } } } } }
5. 在MauiProgram.cs里注册安卓端接口:
#if ANDROID builder.Services.AddSingleton<IPhotoPickerService, AndroidPhotoPickerService>(); #endif
6. 在Index.razor里添加测试代码进行多选照片测试:
@page "/" @using AndroidPhotoPicker.Service <h1>Hello, world!</h1> Welcome to your new app. <button @onclick="GetImageAsync3">添加图片INTENT</button> @if (_phoneDictionary.Any()) { @foreach (var phone in _phoneDictionary) { <div style="height: 100%; width: 100%;"> <img src="@phone.Value" style="height:90px;width:90px;object-fit:cover;" /> </div> <div>图片名称: @phone.Key</div> } } @code{ [Inject] private IPhotoPickerService _photoPickerService { get; set; } //注入服务接口 private Dictionary<string, string> _phoneDictionary { get; set; } = new Dictionary<string, string>();//图片路径字典 private async Task GetImageAsync3() { var photoDic = await _photoPickerService.GetImageAsyncByIntent(); foreach (var photo in photoDic) { _phoneDictionary.Add(photo.Key, photo.Value); } await InvokeAsync(StateHasChanged); } }
7. Android文件夹内的AndroidManifest.xml里别忘记添加读取储存的权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
8. 在MainPage.xaml里添加BlazorWebViewInitializing和BlazorWebViewInitialized事件,参考如下:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:AndroidPhotoPicker" x:Class="AndroidPhotoPicker.MainPage" BackgroundColor="{DynamicResource PageBackgroundColor}"> <BlazorWebView x:Name="blazorWebView" HostPage="wwwroot/index.html" BlazorWebViewInitializing="blazorWebView_BlazorWebViewInitializing" BlazorWebViewInitialized="blazorWebView_BlazorWebViewInitialized"> <BlazorWebView.RootComponents> <RootComponent Selector="#app" ComponentType="{x:Type local:Main}" /> </BlazorWebView.RootComponents> </BlazorWebView> </ContentPage>
9. 在MainPage.xaml.cs里BlazorWebViewInitializing(暂时留空,IOS快速显示本地图片后续添加相关代码)和BlazorWebViewInitialized事件里添加如下代码:
void blazorWebView_BlazorWebViewInitialized(System.Object sender, Microsoft.AspNetCore.Components.WebView.BlazorWebViewInitializedEventArgs e) { #if ANDROID e.WebView.SetWebViewClient(new MyWebViewClient(e.WebView.WebViewClient)); #endif } #if ANDROID private class MyWebViewClient : WebViewClient { private WebViewClient WebViewClient { get; } public MyWebViewClient(WebViewClient webViewClient) { WebViewClient = webViewClient; } public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request) { return WebViewClient.ShouldOverrideUrlLoading(view, request); } public override WebResourceResponse ShouldInterceptRequest(Android.Webkit.WebView view, IWebResourceRequest request) { var resourceResponse = WebViewClient.ShouldInterceptRequest(view, request); if (resourceResponse == null) return null; if (resourceResponse.StatusCode == 404) { var path = request.Url.Path; if (File.Exists(path)) { string mime = MimeTypeMap.Singleton.GetMimeTypeFromExtension(Path.GetExtension(path)); string encoding = "UTF-8"; Stream stream = File.OpenRead(path); return new(mime, encoding, stream); } } //Debug.WriteLine("路径:" + request.Url.ToString()); return resourceResponse; } public override void OnPageFinished(Android.Webkit.WebView view, string url) => WebViewClient.OnPageFinished(view, url); protected override void Dispose(bool disposing) { if (!disposing) return; WebViewClient.Dispose(); } } #endif
知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名Tony(包含链接: https://www.cnblogs.com/wecareu/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系 。
转载声明
本文来自博客园,作者:Tony,转载请注明原文链接:https://www.cnblogs.com/wecareu/p/17855489.html
本文来自博客园,作者:MTony,转载请注明原文链接:https://www.cnblogs.com/wecareu/p/17855415.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库