unity+android:打开安卓文件浏览器,并获取选择文件真实路径

本文介绍了一种在Unity3D与Android环境下跨平台获取文件真实路径的方法,通过自定义的GetFilePath类,实现了从Android系统中读取文件路径并传递给Unity的功能。文中详细解释了如何处理不同类型的URI,包括外部存储、下载和媒体文档,并提供了完整的代码实现。

java代码

package com.luo.yikun.getfilepath;


import android.app.Activity;
import android.app.Fragment;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;

import android.provider.DocumentsContract;
import android.provider.MediaStore;


import com.unity3d.player.UnityPlayer;


import java.io.File;




public class GetFilePath extends Fragment {
    String m_gameObjectName;
    String m_funcName;

    private Context unityContext;
    private Activity unityActivity;

    private static final String TAG = "GetFilePath";

    public void UnityFunc(String gameObjectName, String funcName)
    {
        m_gameObjectName = gameObjectName;
        m_funcName = funcName;
    }

    private static GetFilePath Instance = null;
    //private String gameObjectName;

    public static GetFilePath GetInstance()
    {
        if(Instance == null)
        {
            Instance = new GetFilePath();
            //Instance.gameObjectName = gameObject;
            Instance.unityActivity = UnityPlayer.currentActivity;
            if (Instance.unityActivity == null)
            {
                Instance.CallUnity("Instance.unityActivity == null");
            }
            Instance.unityContext = Instance.unityActivity.getBaseContext();
            if (Instance.unityContext == null)
            {
                Instance.CallUnity("Instance.unityContext == null");
            }
            UnityPlayer.currentActivity.getFragmentManager().beginTransaction().add(Instance, TAG).commit();

        }
        return Instance;
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);  // 这一句很重要,保存对该Fragment的引用,防止在旋转屏幕等操作时时丢失引用(Fragment隶属于Activity)
    }

    // 调用 Unity
    // gameObjectName 为接受消息的Unity 中 GameObject 的名字
    // functionName   为接受消息的GameObject 挂载 C# 脚本中的函数名
    // _content       为发送给Unity 的内容
    public void CallUnity( String _content)
    {
        UnityPlayer.UnitySendMessage(m_gameObjectName, m_funcName, _content);
    }

    public void fileBrower()
    {
        Intent myintent = new Intent(Intent.ACTION_GET_CONTENT);
        myintent.setType("*/*");
        myintent.addCategory(Intent.CATEGORY_OPENABLE);
        startActivityForResult(Intent.createChooser(myintent, "选择文件"), 1);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        // TODO Auto-generated method stub
        if (resultCode != Activity.RESULT_OK) {
            super.onActivityResult(requestCode, resultCode, data);
            return;
        }
        if (requestCode == 1) {
            Uri uri = data.getData();
            String path = getPath(unityContext, uri);
            if (path != null) {
                File file = new File(path);
                if (file.exists()) {
                    String upLoadFilePath = file.toString();
                    String upLoadFileName = file.getName();
                    CallUnity(upLoadFilePath);
                }
            }

        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    public String getPath(final Context context, final Uri uri) {

        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
//                Log.i(TAG,"isExternalStorageDocument***"+uri.toString());
//                Log.i(TAG,"docId***"+docId);
//                以下是打印示例:
//                isExternalStorageDocument***content://com.android.externalstorage.documents/document/primary%3ATset%2FROC2018421103253.wav
//                docId***primary:Test/ROC2018421103253.wav
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
//                Log.i(TAG,"isDownloadsDocument***"+uri.toString());
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
//                Log.i(TAG,"isMediaDocument***"+uri.toString());
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[]{split[1]};

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
//            Log.i(TAG,"content***"+uri.toString());
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
//            Log.i(TAG,"file***"+uri.toString());
            return uri.getPath();
        }
        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context       The context.
     * @param uri           The Uri to query.
     * @param selection     (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public String getDataColumn(Context context, Uri uri, String selection,
                                String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {column};

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }


    public boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    public boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    public boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }
}

AndroidMainfest文件增加权限

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.luo.yikun.getfilepath">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
</manifest>

demo下载
https://pan.baidu.com/s/16KOk1Nt7Eri4JPsXsHwm7A
demo是早期的,可能跟上面的不同,但是测试过unity打包出apk能浏览到文件的真实路径

<think>首先,用户的问题是关于Unity中实现可视化选择文件夹功能的方法。用户引用了三个引用,但明确指出这些引用仅供参考,我应该更多参考用户上一次的问题和我的回答。然而,在这个对话历史中,没有之前的用户问题记录。系统提示显示这是一个新的查询。 用户引用的内容: - [^1] 提到Unity中的特殊文件夹,如Editor、iOS等。 - [^2] 讨论Library文件夹和persistentDataPath。 - [^3] 关于Gizmos的使用,用于可视化调试。 用户当前的问题是:“我想查找在Unity中实现可视化选择文件夹功能的方法 请问Unity中实现可视化选择文件夹的方法” 所以,焦点是实现一个可视化界面让用户选择文件夹的功能。 在Unity中,实现文件选择功能通常涉及使用Editor Scripting或运行时API。Unity本身没有内置的文件夹选择对话框,但有几个选项: 1. **Editor Scripting**:在编辑器脚本中,可以使用`UnityEditor.EditorUtility.OpenFolderPanel`或`UnityEditor.EditorUtility.SaveFilePanel`来打开文件对话框。这些只在编辑器中可用。 2. **运行时**:在游戏运行时,Unity不提供内置的文件夹选择器。开发者可能需要使用平台特定的API: - 在Windows上,可以使用`System.Windows.Forms.FolderBrowserDialog`(需要添加System.Windows.Forms引用)。 - 在Mac或Linux上,使用其他方法。 - 或者使用Unity的`Application.OpenURL`打开系统文件浏览器,但这不直接提供选择功能。 3. **第三方插件**:如使用Asset Store中的插件来提供跨平台的文件选择器。 用户提到“可视化选择文件夹”,所以很可能是指运行时或编辑时的可视化界面。 基于引用: - [^1] 提到Editor文件夹,暗示Editor Scripting。 - [^3] 提到Gizmos,用于可视化调试,但不直接相关。 - [^2] 讨论persistentDataPath,可能与文件路径相关。 我应该构建一个清晰的回答: - 介绍方法。 - 提供代码示例。 - 遵循系统指令:使用$...$行内数学表达式,$$...$$独立公式(但这里可能不需要数学),中文回答,生成相关问题。 系统指令: - 所有行内数学表达式用$...$。 - 独立公式用$$...$$单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 在这个上下文中,可能不需要数学表达式,但如果有计算或公式,要遵守。 回答结构: 1. 解释问题。 2. 提供解决方案: - Editor Scripting方法。 - 运行时方法。 - 第三方插件建议。 3. 给出代码示例。 4. 添加引用标识(如果适用)。 5. 生成相关问题。 用户引用了[^1], [^2], [^3],但我应该基于真实知识回答。引用是提供的,所以我可以在回答中提到它们,但要确保相关。 步骤: - 确认用户需求:Unity中可视化选择文件夹。 - 主要方法: - 在编辑器中:使用`EditorUtility.OpenFolderPanel`。 - 在运行时:使用System.Windows.Forms for Windows,或其他跨平台方案。 - 代码示例展示。 - 提醒只能在Editor中使用,或在特定平台。 代码示例: - Editor脚本示例。 - 运行时脚本示例(Windows)。 生成相关问题: - 类似示例中的:如何自定义文件选择器?跨平台方案?集成到Unity UI? 最终回答: - 用中文。 - 结构清晰。 </think>根据您的查询,您希望在Unity中实现可视化选择文件夹的功能。这通常涉及创建一个对话框让用户浏览和选择目录。Unity本身没有内置的运行时文件夹选择器,但可以通过多种方法实现,包括Editor Scripting用于编辑器工具,以及运行时使用平台特定API或第三方插件。以下我将逐步解释实现方法,提供代码示例。注意: - **Editor Scripting方法**:仅限于Unity编辑器内部使用(如开发工具或编辑器扩展),不能用于运行时游戏。 - **运行时方法**:需要在游戏执行时使用,但依赖操作系统API,可能跨平台兼容性差;建议使用第三方插件简化。 - 您的参考资料已参考(如Editor文件夹和持久数据路径),但这里基于Unity标准实践回答[^1][^2]。 ### 1. **使用Editor Scripting实现可视化文件夹选择(编辑器专用)** 在Editor脚本中,Unity提供了`UnityEditor.EditorUtility.OpenFolderPanel`方法,这是一个内置对话框,允许用户可视化选择文件夹。此方法仅能在`Editor`文件夹下的脚本中使用。 - **操作步骤**: 1. 在Unity项目中创建`Assets/Editor`文件夹(如果不存在),这是Unity标准特殊文件夹用于存放编辑器脚本[^1]。 2. 在`Editor`文件夹下创建C#脚本。 3. 使用`OnGUI`或自定义菜单项触发对话框。 **代码示例**: 以下脚本添加了一个Unity编辑器菜单项,点击后可打开文件夹选择对话框,输出所选路径。 ```csharp using UnityEditor; using UnityEngine; public class FolderSelector : EditorWindow { [MenuItem("Tools/选择文件夹")] public static void ShowWindow() { // 打开文件夹选择对话框 string path = EditorUtility.OpenFolderPanel("选择文件夹", "", ""); if (!string.IsNullOrEmpty(path)) { Debug.Log("选中文件夹: " + path); // 可选:将路径保存到PlayerPrefs或项目设置中 } } } ``` - **解释**: - `EditorUtility.OpenFolderPanel`创建一个模态对话框,参数依次为标题、初始路径和默认文件夹名(""表示根目录)。 - 在Unity编辑器中,通过菜单栏"Tools > 选择文件夹"激活。 - 输出路径可用于Asset管理或导入资源,但无法在运行时游戏中使用[^1][^2]。 ### 2. **使用运行时API实现可视化文件夹选择(平台特定)** 在游戏运行时(Build后),Unity没有内置文件夹选择器,但可以调用操作系统API。以下是Windows平台的实现示例(使用.NET Framework)。注意: - 此方法依赖`System.Windows.Forms`,需在Unity中导入DLL(通过`Plugins`文件夹)。 - 不支持跨平台(如MacOS或Android需要不同API)。 - 路径可保存到`Application.persistentDataPath`用于持久存储[^2]。 **操作步骤**: 1. 在Unity项目中创建`Assets/Plugins`文件夹。 2. 导入`System.Windows.Forms.dll`(位于.NET安装路径,如`C:\Windows\Microsoft.NET\Framework\v4.0.30319`)。 3. 创建脚本附加到GameObject。 **代码示例**: ```csharp using UnityEngine; using System.Windows.Forms; // 需要System.Windows.Forms引用 public class RuntimeFolderSelector : MonoBehaviour { public void OpenFolderDialog() { // 创建文件夹浏览器对话框 FolderBrowserDialog folderDialog = new FolderBrowserDialog(); folderDialog.Description = "选择文件夹"; folderDialog.ShowNewFolderButton = true; // 允许创建新文件夹 // 显示对话框 if (folderDialog.ShowDialog() == DialogResult.OK) { string path = folderDialog.SelectedPath; Debug.Log("选中文件夹: " + path); // 可选:保存到持久数据路径,例如 Application.persistentDataPath } } } ``` - **解释**: - 在运行时调用`OpenFolderDialog`方法(如通过UI按钮事件)。 - `DialogResult.OK`确保用户确认选择后处理路径。 - 路径可结合`Application.persistentDataPath`用于保存用户数据[^2]。 - **局限性**:不支持移动设备;MacOS需使用`NSOpenPanel`,Android需用Java插件。 ### 3. **替代方案:第三方插件** 为了方便跨平台运行时实现,推荐使用Unity Asset Store中的插件: - **Standalone File Browser**:免费插件,支持Windows、MacOS和Linux的文件夹选择。 - **Native File Picker**:付费插件,扩展支持iOS和Android。 - 安装后,遵循插件文档集成,通常只需几行代码调用类似`NativeFilePicker.PickFolder`的方法。 ### 注意事项 - **性能与兼容性**:Editor方法高效,但不能用于Build;运行时方法需测试目标平台。Library文件夹可能因资源残留增大,定期清理可优化性能[^2]。 - **可视化辅助**:如果您需要可视化调试文件夹位置,可使用Gizmos绘制边界框(参考`OnDrawGizmos`),但这不实现选择功能[^3]。 - **安全提示**:运行时文件夹选择涉及文件系统访问,确保权限设置合理,避免安全风险。 ###
评论 16
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

四夕立羽

你的鼓励将是我创作的最大动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值