You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Unity中如何将AR相机拍摄的照片保存至安卓图片图库?

如何将Unity截图保存到安卓图库

嘿,这个问题我刚好踩过坑,直接替换persistentDataPath成固定路径的话大概率会出问题——毕竟安卓不同版本的存储机制差异很大,尤其是安卓10之后的分区存储,而且就算你硬写路径成功了,系统图库也可能识别不到这张图。下面给你两种靠谱的解决方案:

方案1:调用安卓原生API插入媒体库(推荐,适配全版本)

这种方法会通过安卓的MediaStore将图片正式插入系统媒体库,自动处理权限和图库识别问题,还能自定义保存的子目录。

修改后的完整代码示例:

using UnityEngine;
using System.IO;

public class ARGallerySaver : MonoBehaviour
{
    public void SaveARScreenshotToGallery(Texture2D screenshot)
    {
        // 生成带时间戳的文件名
        string screenshotName = "Dorot" + System.DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".png";
        
        // 先把图片写到临时缓存目录(避免权限问题)
        string tempFilePath = Path.Combine(Application.temporaryCachePath, screenshotName);
        File.WriteAllBytes(tempFilePath, screenshot.EncodeToPNG());

        // 调用安卓原生代码处理媒体库插入
        using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        {
            AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
            AndroidJavaObject contentResolver = currentActivity.Call<AndroidJavaObject>("getContentResolver");

            using (AndroidJavaClass mediaStore = new AndroidJavaClass("android.provider.MediaStore"))
            {
                AndroidJavaObject contentValues = new AndroidJavaObject("android.content.ContentValues");
                
                // 设置图片基本信息
                contentValues.Call<AndroidJavaObject>("put", mediaStore.GetStatic<string>("Images.Media.DISPLAY_NAME"), screenshotName);
                contentValues.Call<AndroidJavaObject>("put", mediaStore.GetStatic<string>("Images.Media.MIME_TYPE"), "image/png");

                // 根据安卓版本设置存储路径
                int androidSdkVersion = new AndroidJavaClass("android.os.Build$VERSION").GetStatic<int>("SDK_INT");
                if (androidSdkVersion >= 29)
                {
                    // 安卓10+用RELATIVE_PATH指定图库子目录(比如Pictures/你的APP名)
                    contentValues.Call<AndroidJavaObject>("put", mediaStore.GetStatic<string>("Images.Media.RELATIVE_PATH"), "Pictures/AR_Dorot");
                }
                else
                {
                    // 安卓9及以下直接指定文件路径
                    contentValues.Call<AndroidJavaObject>("put", mediaStore.GetStatic<string>("Images.Media.DATA"), tempFilePath);
                }

                // 插入到媒体库获取URI
                AndroidJavaObject imageUri = contentResolver.Call<AndroidJavaObject>("insert", mediaStore.GetStatic<AndroidJavaObject>("Images.Media.EXTERNAL_CONTENT_URI"), contentValues);
                
                if (imageUri != null)
                {
                    // 将临时文件复制到媒体库指定位置
                    using (AndroidJavaObject outputStream = contentResolver.Call<AndroidJavaObject>("openOutputStream", imageUri))
                    using (AndroidJavaObject inputStream = new AndroidJavaObject("java.io.FileInputStream", tempFilePath))
                    {
                        byte[] buffer = new byte[1024];
                        int readLength;
                        while ((readLength = inputStream.Call<int>("read", buffer)) > 0)
                        {
                            outputStream.Call<void>("write", buffer, 0, readLength);
                        }
                        inputStream.Call<void>("close");
                        outputStream.Call<void>("close");
                    }

                    // 删除临时文件
                    File.Delete(tempFilePath);

                    // 通知系统媒体库更新,让图库立刻显示新图片
                    new AndroidJavaClass("android.media.MediaScannerConnection")
                        .CallStatic("scanFile", currentActivity, new string[] { tempFilePath }, new string[] { "image/png" }, null);
                }
            }
        }

        // 恢复主Canvas显示
        GameObject.Find("MainCanvas").GetComponent<Canvas>().enabled = true;
    }
}

方案2:用Unity自带的CaptureScreenshot(简单但灵活性低)

如果你的需求只是快速保存全屏截图到图库,Unity自带的Application.CaptureScreenshot可以直接搞定,它会自动处理路径和基础权限:

public void QuickSaveScreenshotToGallery()
{
    string screenshotName = "Dorot" + System.DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".png";
    // 直接保存到系统图库,Unity会处理所有细节
    Application.CaptureScreenshot(screenshotName);
    
    // 恢复Canvas显示
    GameObject.Find("MainCanvas").GetComponent<Canvas>().enabled = true;
}

不过这个方法有局限性:只能捕获整个屏幕,无法自定义存储子目录,也不能用你代码里已经截取好的Texture2D对象。

关键注意事项

  • 权限配置
    • 安卓9及以下:在Unity Player Settings里勾选WRITE_EXTERNAL_STORAGE权限
    • 安卓10+:如果需要兼容旧存储逻辑,可以在AndroidManifest.xml中添加android:requestLegacyExternalStorage="true"
  • 测试环境:一定要用真实安卓设备测试,模拟器的图库经常会有延迟或者不识别的问题
  • 媒体扫描:必须通知系统扫描新文件,否则图库可能要等很久才会显示新保存的图片

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

火山引擎 最新活动