Android 长文件名视频选择对话框解决方案
2024-12-13 16:00:03
Android长文件名视频选择对话框解决方案
在Android应用开发中,选择视频文件是一个常见需求。当视频文件名过长,特别是文件名只有末尾少数几个字符不同时,系统自带的文件选择对话框(如Gallery)可能会因为显示空间不足而无法完整展示文件名,给用户选择带来困扰。 本文将探讨这个问题,并提供几种解决方案。
问题分析
Android系统提供了两种Intent方式来启动文件选择器:ACTION_OPEN_DOCUMENT
和 ACTION_PICK
。这两种方式都可以指定文件类型,但它们在UI展示上依赖于系统或第三方应用提供的文件管理器,开发者难以直接控制其文件名显示方式。 当文件名超出显示限制时,被截断的部分可能会丢失重要信息,用户无法有效区分相似文件。
解决方案
针对长文件名视频选择问题,以下提供几种解决方案,兼顾文件名完整显示与视频预览功能。
1. 自定义文件选择对话框
最直接的方案是创建自定义对话框,完全控制文件列表的显示方式。
原理: 通过 ContentResolver
查询媒体库,获取视频文件信息,然后使用 RecyclerView
或其他布局控件构建自定义列表界面。利用 TextView
的滚动特性或调整布局以适应长文件名。同时使用 ThumbnailUtils
或第三方库加载视频缩略图实现预览。
实现:
-
权限申请: 在
AndroidManifest.xml
中声明读取外部存储权限:<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
并在运行时动态申请权限。
-
查询视频: 使用
ContentResolver
查询视频文件信息,包括路径、文件名、大小等:fun getVideoFiles(context: Context): List<VideoFile> { val videoList = mutableListOf<VideoFile>() val projection = arrayOf( MediaStore.Video.Media._ID, MediaStore.Video.Media.DATA, MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.SIZE ) val selection = "${MediaStore.Video.Media.DURATION} >= ?" val selectionArgs = arrayOf("0") val sortOrder = "${MediaStore.Video.Media.DISPLAY_NAME} ASC" context.contentResolver.query( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder )?.use { cursor -> val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID) val pathColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA) val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME) val sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE) while (cursor.moveToNext()) { val id = cursor.getLong(idColumn) val path = cursor.getString(pathColumn) val name = cursor.getString(nameColumn) val size = cursor.getLong(sizeColumn) videoList.add(VideoFile(id, path, name, size)) } } return videoList } data class VideoFile(val id: Long, val path: String, val name: String, val size: Long)
-
创建自定义对话框: 使用
AlertDialog.Builder
创建对话框,设置自定义布局,并使用RecyclerView
显示视频列表。fun showVideoPickerDialog(context: Context) { val videoFiles = getVideoFiles(context) val recyclerView = RecyclerView(context).apply { layoutManager = LinearLayoutManager(context) adapter = VideoAdapter(videoFiles) { videoFile -> // 处理选中视频 Toast.makeText(context, "选择了 ${videoFile.name}", Toast.LENGTH_SHORT).show() } } AlertDialog.Builder(context) .setTitle("选择视频") .setView(recyclerView) .setNegativeButton("取消", null) .show() } class VideoAdapter(private val videoFiles: List<VideoFile>, val clickListener: (VideoFile) -> Unit) : RecyclerView.Adapter<VideoAdapter.ViewHolder>() { class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val videoName: TextView = view.findViewById(android.R.id.text1) val videoThumb : ImageView = view.findViewById(android.R.id.icon) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(android.R.layout.two_line_list_item, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val videoFile = videoFiles[position] holder.videoName.text = videoFile.name holder.itemView.setOnClickListener{ clickListener(videoFile) } val thumb = ThumbnailUtils.createVideoThumbnail(videoFile.path, MediaStore.Images.Thumbnails.MINI_KIND); if(thumb != null) { holder.videoThumb.setImageBitmap(thumb); } else{ holder.videoThumb.setImageResource(android.R.drawable.ic_media_play) } } override fun getItemCount() = videoFiles.size
}
```
-
视频预览: 在
RecyclerView
的 Item 布局中添加ImageView
,使用ThumbnailUtils.createVideoThumbnail()
生成视频缩略图并显示。val thumb = ThumbnailUtils.createVideoThumbnail(videoFile.path, MediaStore.Images.Thumbnails.MINI_KIND); if(thumb != null) { holder.videoThumb.setImageBitmap(thumb); } else{ holder.videoThumb.setImageResource(android.R.drawable.ic_media_play) }
-
安全建议: 为了应用的安全和用户隐私,对于 Android 13 及以上版本,对于读取的图片或者视频需要获取对应的
READ_MEDIA_IMAGES
以及READ_MEDIA_VIDEO
运行时权限。同时, 针对通过MediaStore选择的文件进行操作时,需要申请到该文件在外部存储的路径授权才能访问成功,这个也是在Android 10 引入的分区存储机制,可以根据实际场景,在获取用户授权的情况下,通过ContentResolver
获取该文件的访问 Uri 后使用takePersistableUriPermission()
方法获取文件的永久访问权限。 -
调用代码:
showVideoPickerDialog(this);
2. 使用第三方文件选择器库
现有的一些第三方文件选择器库提供了更灵活的UI定制和文件展示功能。
原理: 集成第三方库,利用其提供的 API 构建文件选择对话框。这些库通常已经处理了长文件名显示、缩略图加载等问题。
实现: (以 Material File Picker 为例)
-
添加依赖: 在
build.gradle
文件中添加依赖:implementation 'com.github.Dhaval2404:imagepicker:2.1'
-
启动文件选择器: 使用库提供的 API 启动文件选择器,并设置自定义选项。
import com.github.dhaval2404.imagepicker.ImagePicker fun showVideoPickerWithLibrary(activity: Activity) { ImagePicker.with(activity) .galleryMimeTypes(mimeTypes = arrayOf("video/*")) // Filter only video .cameraOnly(false) // Disable camera feature .show(123); // Activity result code, can be any integer } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if(requestCode == 123 && resultCode == Activity.RESULT_OK){ val fileUri: Uri? = data?.data // 处理返回结果 fileUri val filePath = fileUri?.path; Toast.makeText(this, "选择了 ${filePath}", Toast.LENGTH_SHORT).show(); } }
data.data
将返回选择的文件uri,data.path
将返回选择文件的路径。 你可以通过文件 uri 和路径执行后续的文件操作。 -
安全建议: 仔细评估所用第三方库的安全性、稳定性及许可证,选择信誉良好且维护活跃的库。阅读库的使用文档,了解其具体功能及可能涉及的隐私权限问题。
3. 折中方案:缩短显示文件名
如果自定义UI工作量较大,而第三方库无法完全满足需求,可以考虑折中方案,即缩短显示文件名。
**原理