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

Android存储访问框架中如何同时选择文件与文件夹?

解决Android存储访问框架(SAF)中文件夹选择的问题

首先明确:SAF支持文件夹选择,但默认的ActivityResultContracts存在功能局限,需要根据需求调整实现方式。

针对你的两种尝试的问题分析

  • 尝试1(GetMultipleContents()):这个Contract默认仅支持多选文件,因为它默认MIME类型为*/*,会过滤掉文件夹。若指定MIME类型为vnd.android.document/directory,可切换为多选文件夹,但无法同时选择文件。
  • 尝试2(OpenDocumentTree()):这个Contract的设计目的就是选择单个文件夹,本身不支持选择文件,也不支持多选文件夹,你的结果符合该API的预期行为。

实现不同需求的方案

1. 多选文件夹

如果仅需要多选文件夹,可通过自定义StartActivityForResult结合Intent实现:

class MainFragment : Fragment() {
    private val multipleFolderLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            result.data?.let { intent ->
                val selectedUris = mutableListOf<Uri>()
                // 处理多选结果
                intent.clipData?.let { clipData ->
                    for (i in 0 until clipData.itemCount) {
                        selectedUris.add(clipData.getItemAt(i).uri)
                    }
                } ?: intent.data?.let { selectedUris.add(it) }
                
                // 后续操作选中的文件夹Uri列表
            }
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.materialButton.setOnClickListener {
            val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
                addCategory(Intent.CATEGORY_OPENABLE)
                type = "vnd.android.document/directory"
                putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
            }
            multipleFolderLauncher.launch(intent)
        }
    }
}

2. 同时选择文件和文件夹

SAF原生没有支持同时多选文件和文件夹的API,可采用两种思路:

  • 提供两个独立按钮,分别用于选择文件、选择文件夹,最后合并两组结果。
  • 使用适配SAF的第三方文件选择库(需注意适配Android 10+存储权限规则),若必须依赖原生SAF,推荐第一种方案。

3. 单个文件夹选择(优化尝试2)

如果仅需要选择单个文件夹,你的OpenDocumentTree()用法是正确的,返回的Uri可用于遍历文件夹内容:

class MainFragment : Fragment() {
    private val singleFolderLauncher = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { treeUri ->
        treeUri?.let {
            val folder = DocumentFile.fromTreeUri(requireContext(), it)
            // 遍历文件夹内的文件与子文件夹
            folder?.listFiles()?.forEach { documentFile ->
                // 处理文件或子文件夹
            }
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.materialButton.setOnClickListener {
            singleFolderLauncher.launch(null)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 03:15:42