如何在RecyclerView中同时列出视频文件及其所在文件夹?
获取视频文件所在文件夹并在RecyclerView中展示
我来帮你搞定这个需求~要在RecyclerView里同时展示视频文件和它们所在的文件夹,关键是先从MediaStore里拿到文件夹路径,然后做去重和数据区分,具体步骤如下:
1. 修改MediaStore查询的投影字段
首先,你需要扩展查询的projection数组,加入能获取文件路径的字段。Android 10(API 29)及以上推荐使用RELATIVE_PATH,它返回的是相对于外部存储的相对路径(比如"Movies/");更早的版本可以用DATA获取完整文件路径。为了兼容,你可以同时加入这两个字段:
val projection = arrayOf( MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.RELATIVE_PATH, // Android 10+ 适用 MediaStore.Video.Media.DATA // 旧版本兼容 )
2. 查询并提取文件夹信息,做去重处理
接下来,遍历cursor的时候,提取每个视频的文件夹路径,用一个集合来记录已经处理过的文件夹,避免重复添加。同时,建议创建一个密封类来区分“文件夹”和“视频文件”两种类型,方便后续RecyclerView适配:
首先定义数据类:
sealed class MediaItem { data class VideoFile(val name: String) : MediaItem() data class Folder(val name: String) : MediaItem() }
然后处理cursor(用use函数可以自动关闭cursor,省去手动close的步骤):
val mediaItems = mutableListOf<MediaItem>() val processedFolders = mutableSetOf<String>() // 用于去重文件夹,确保每个文件夹只显示一次 val cursor = CursorLoader( applicationContext, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null, null, null ).loadInBackground() cursor?.use { while (it.moveToNext()) { val videoName = it.getString(it.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME)) // 添加当前视频到列表 mediaItems.add(MediaItem.VideoFile(videoName)) // 根据系统版本获取文件夹路径 val folderName = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val relativePath = it.getString(it.getColumnIndex(MediaStore.Video.Media.RELATIVE_PATH)) relativePath?.trimEnd('/') // 去掉末尾的斜杠,显示更友好 } else { val fullPath = it.getString(it.getColumnIndex(MediaStore.Video.Media.DATA)) // 从完整路径中提取文件夹名称 File(fullPath).parentFile?.name } folderName?.let { name -> // 如果这个文件夹还没被添加过,就加入列表 if (processedFolders.add(name)) { mediaItems.add(MediaItem.Folder(name)) } } } }
3. 适配RecyclerView,区分显示文件夹和视频
接下来,在RecyclerView的Adapter里,根据MediaItem的类型加载不同的布局。你需要先创建两个布局文件:item_folder.xml(用于显示文件夹样式)和item_video.xml(用于显示视频文件样式)。
然后编写Adapter:
class MediaAdapter(private val items: List<MediaItem>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { companion object { private const val TYPE_FOLDER = 0 private const val TYPE_VIDEO = 1 } // 区分不同的item类型 override fun getItemViewType(position: Int): Int { return when (items[position]) { is MediaItem.Folder -> TYPE_FOLDER is MediaItem.VideoFile -> TYPE_VIDEO } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val inflater = LayoutInflater.from(parent.context) return when (viewType) { TYPE_FOLDER -> FolderViewHolder(inflater.inflate(R.layout.item_folder, parent, false)) else -> VideoViewHolder(inflater.inflate(R.layout.item_video, parent, false)) } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (val item = items[position]) { is MediaItem.Folder -> (holder as FolderViewHolder).bind(item.name) is MediaItem.VideoFile -> (holder as VideoViewHolder).bind(item.name) } } override fun getItemCount() = items.size // 文件夹ViewHolder inner class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(folderName: String) { itemView.tv_folder_name.text = folderName // 可以在这里添加文件夹图标、点击事件等逻辑 } } // 视频文件ViewHolder inner class VideoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(videoName: String) { itemView.tv_video_name.text = videoName // 可以在这里添加视频缩略图、播放按钮等逻辑 } } }
额外优化提示
- 排序调整:上面的代码会先添加视频再添加文件夹,你可以根据需求调整排序逻辑,比如先显示所有文件夹,再显示视频,或者按名称排序。
- 权限适配:别忘了在AndroidManifest.xml中添加对应权限,Android 13+需要申请
READ_MEDIA_VIDEO,旧版本需要READ_EXTERNAL_STORAGE。 - 性能优化:如果视频文件较多,建议在后台线程处理cursor查询,避免阻塞主线程。
内容的提问来源于stack exchange,提问作者user8403860
相关产品推荐
相关产品推荐

