You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin Android如何实现将ImageView图片保存到系统相册

Xamarin.Android 图片保存功能实现方案

1 前置配置

  • 新增字符串资源:在Resources/values/Strings.xml中添加保存按钮的文本定义
<string name="Lbl_Save">保存图片</string>
  • 权限配置:在AndroidManifest.xml中添加对应存储权限,适配不同安卓版本:
    • Android 12及以下:添加android.permission.WRITE_EXTERNAL_STORAGE权限
    • Android 13及以上:添加android.permission.READ_MEDIA_IMAGES权限
  • 注意需要在调用保存功能前动态申请所需权限,权限通过后再执行保存逻辑。

2 核心代码修改

2.1 扩展OnSelection方法分支

在原有分享逻辑的后面新增保存功能的判断分支:

public async void OnSelection(MaterialDialog dialog, View itemView, int position, string itemString)
{
    try
    {
        if (itemString == GetText(Resource.String.Lbl_MessageInfo))
        {
            var intent = new Intent(this, typeof(MessageInfoActivity));
            intent.PutExtra("UserId", Id);
            intent.PutExtra("MainChatColor", !string.IsNullOrEmpty(MesData.ChatColor) ? MesData.ChatColor : AppSettings.MainColor ?? AppSettings.MainColor);
            intent.PutExtra("SelectedItem", JsonConvert.SerializeObject(MesData));
            StartActivity(intent);
        }
        else if (itemString == GetText(Resource.String.Lbl_Forward))
        {
            var intent = new Intent(this, typeof(ForwardMessagesActivity));
            intent.PutExtra("SelectedItem", JsonConvert.SerializeObject(MesData));
            StartActivity(intent);
        }
        else if (itemString == GetText(Resource.String.Lbl_Share))
        {
            string urlImage = MediaFile;
            var fileName = urlImage?.Split('/').Last();
            await ShareFileImplementation.ShareRemoteFile(urlImage, urlImage, fileName, GetText(Resource.String.Lbl_SendTo));
        }
        // 新增保存图片分支
        else if (itemString == GetText(Resource.String.Lbl_Save))
        {
            // 先检查权限,已授权再执行保存
            if (CheckStoragePermission())
            {
                string urlImage = MediaFile;
                var fileName = urlImage?.Split('/').Last();
                await SaveImageToGallery(urlImage, fileName);
                Toast.MakeText(this, "图片已保存到相册", ToastLength.Short)?.Show();
            }
            else
            {
                // 调用动态权限申请逻辑,申请通过后再触发保存即可
                RequestStoragePermission();
            }
        }
    }
    catch (Exception e)
    {
        Methods.DisplayReportResultTrack(e);
        Toast.MakeText(this, "保存失败,请重试", ToastLength.Short)?.Show();
    }
}

2.2 新增图片保存方法

在同一个Activity类中添加保存图片到系统相册的异步方法:

private async Task SaveImageToGallery(string imageUrl, string fileName)
{
    using var httpClient = new HttpClient();
    // 下载远程图片字节流
    var imageBytes = await httpClient.GetByteArrayAsync(imageUrl);
    
    // 适配分区存储,插入到公共相册目录
    var contentValues = new ContentValues();
    contentValues.Put(MediaStore.Images.Media.InterfaceConsts.DisplayName, fileName);
    contentValues.Put(MediaStore.Images.Media.InterfaceConsts.MimeType, "image/jpeg");
    if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
    {
        contentValues.Put(MediaStore.Images.Media.InterfaceConsts.RelativePath, "DCIM/" + GetString(Resource.String.ApplicationName));
        contentValues.Put(MediaStore.Images.Media.InterfaceConsts.IsPending, true);
    }

    var resolver = ContentResolver;
    var uri = resolver.Insert(MediaStore.Images.Media.ExternalContentUri, contentValues);
    if (uri == null) return;

    // 写入图片数据
    await using var stream = resolver.OpenOutputStream(uri);
    await stream.WriteAsync(imageBytes, 0, imageBytes.Length);

    if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
    {
        contentValues.Clear();
        contentValues.Put(MediaStore.Images.Media.InterfaceConsts.IsPending, false);
        resolver.Update(uri, contentValues, null, null);
    }
}

2.3 权限检查/申请辅助方法

如果项目没有现成的权限处理逻辑,可以添加以下简易实现:

private const int StoragePermissionRequestCode = 1001;

private bool CheckStoragePermission()
{
    if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu)
    {
        return CheckSelfPermission(Manifest.Permission.ReadMediaImages) == Permission.Granted;
    }
    return CheckSelfPermission(Manifest.Permission.WriteExternalStorage) == Permission.Granted;
}

private void RequestStoragePermission()
{
    string[] permissions;
    if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu)
    {
        permissions = new[] { Manifest.Permission.ReadMediaImages };
    }
    else
    {
        permissions = new[] { Manifest.Permission.WriteExternalStorage };
    }
    RequestPermissions(permissions, StoragePermissionRequestCode);
}

// 重写权限申请结果回调
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
    base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == StoragePermissionRequestCode && grantResults.All(p => p == Permission.Granted))
    {
        // 权限申请通过,重新触发保存逻辑
        string urlImage = MediaFile;
        var fileName = urlImage?.Split('/').Last();
        _ = SaveImageToGallery(urlImage, fileName);
        Toast.MakeText(this, "图片已保存到相册", ToastLength.Short)?.Show();
    }
    else
    {
        Toast.MakeText(this, "需要存储权限才能保存图片", ToastLength.Short)?.Show();
    }
}

3 最后操作

在你生成圆点菜单的代码中,把"保存图片"的选项加入菜单列表即可完成功能接入。

内容的提问来源于stack exchange,提问作者user3261054

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 01:09:04