android 开发系统应用文件管理

我来为您详细介绍Android系统应用中的文件管理开发。

## 1. 基础权限配置

### 在 AndroidManifest.xml 中添加必要权限

```xml

<!-- 读写存储权限 -->

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!-- Android 11+ 需要 -->

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

<!-- 访问所有文件 -->

<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

```

## 2. 动态权限请求

### Android 6.0+ 需要动态请求权限

```kotlin

class FileManagerActivity : AppCompatActivity() {


    private val PERMISSION_REQUEST_CODE = 100


    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        checkAndRequestPermissions()

    }


    private fun checkAndRequestPermissions() {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {

            // Android 11+ 处理

            if (!Environment.isExternalStorageManager()) {

                val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)

                intent.data = Uri.parse("package:$packageName")

                startActivity(intent)

            }

        } else {

            // Android 10 及以下

            val permissions = arrayOf(

                Manifest.permission.READ_EXTERNAL_STORAGE,

                Manifest.permission.WRITE_EXTERNAL_STORAGE

            )


            if (!hasPermissions(permissions)) {

                ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE)

            }

        }

    }


    private fun hasPermissions(permissions: Array<String>): Boolean {

        return permissions.all {

            ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED

        }

    }

}

```

## 3. 文件操作工具类

```kotlin

object FileUtils {


    // 获取文件列表

    fun getFileList(path: String): List<FileModel> {

        val fileList = mutableListOf<FileModel>()

        val directory = File(path)


        if (directory.exists() && directory.isDirectory) {

            directory.listFiles()?.forEach { file ->

                fileList.add(FileModel(

                    name = file.name,

                    path = file.absolutePath,

                    isDirectory = file.isDirectory,

                    size = if (file.isFile) file.length() else 0,

                    lastModified = file.lastModified(),

                    extension = file.extension

                ))

            }

        }


        // 排序:文件夹在前,文件在后

        return fileList.sortedWith(compareBy({ !it.isDirectory }, { it.name.lowercase() }))

    }


    // 复制文件

    fun copyFile(source: File, destination: File): Boolean {

        return try {

            source.copyTo(destination, overwrite = true)

            true

        } catch (e: Exception) {

            e.printStackTrace()

            false

        }

    }


    // 移动文件

    fun moveFile(source: File, destination: File): Boolean {

        return try {

            source.renameTo(destination)

        } catch (e: Exception) {

            e.printStackTrace()

            false

        }

    }


    // 删除文件或文件夹

    fun deleteFile(file: File): Boolean {

        return if (file.isDirectory) {

            file.deleteRecursively()

        } else {

            file.delete()

        }

    }


    // 创建文件夹

    fun createDirectory(path: String, name: String): Boolean {

        val newDir = File(path, name)

        return newDir.mkdirs()

    }


    // 重命名文件

    fun renameFile(file: File, newName: String): Boolean {

        val newFile = File(file.parent, newName)

        return file.renameTo(newFile)

    }


    // 获取文件大小(格式化)

    fun getFormattedFileSize(size: Long): String {

        val units = arrayOf("B", "KB", "MB", "GB", "TB")

        var fileSize = size.toDouble()

        var unitIndex = 0


        while (fileSize >= 1024 && unitIndex < units.size - 1) {

            fileSize /= 1024

            unitIndex++

        }


        return String.format("%.2f %s", fileSize, units[unitIndex])

    }


    // 获取文件MIME类型

    fun getMimeType(file: File): String {

        val extension = MimeTypeMap.getFileExtensionFromUrl(file.absolutePath)

        return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) ?: "*/*"

    }

}

```

## 4. 文件数据模型

```kotlin

data class FileModel(

    val name: String,

    val path: String,

    val isDirectory: Boolean,

    val size: Long,

    val lastModified: Long,

    val extension: String,

    var isSelected: Boolean = false

) {

    val formattedSize: String

        get() = FileUtils.getFormattedFileSize(size)


    val formattedDate: String

        get() = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(Date(lastModified))

}

```

## 5. 文件列表适配器

```kotlin

class FileListAdapter(

    private val onItemClick: (FileModel) -> Unit,

    private val onItemLongClick: (FileModel) -> Boolean

) : RecyclerView.Adapter<FileListAdapter.FileViewHolder>() {


    private var fileList = mutableListOf<FileModel>()

    var isMultiSelectMode = false


    fun updateList(newList: List<FileModel>) {

        fileList.clear()

        fileList.addAll(newList)

        notifyDataSetChanged()

    }


    inner class FileViewHolder(private val binding: ItemFileBinding) :

        RecyclerView.ViewHolder(binding.root) {


        fun bind(file: FileModel) {

            binding.apply {

                tvFileName.text = file.name

                tvFileInfo.text = if (file.isDirectory) {

                    "文件夹"

                } else {

                    "${file.formattedSize} • ${file.formattedDate}"

                }


                // 设置图标

                ivFileIcon.setImageResource(

                    if (file.isDirectory) {

                        R.drawable.ic_folder

                    } else {

                        getFileIcon(file.extension)

                    }

                )


                // 多选模式显示复选框

                cbSelect.visibility = if (isMultiSelectMode) View.VISIBLE else View.GONE

                cbSelect.isChecked = file.isSelected


                root.setOnClickListener {

                    if (isMultiSelectMode) {

                        file.isSelected = !file.isSelected

                        notifyItemChanged(adapterPosition)

                    } else {

                        onItemClick(file)

                    }

                }


                root.setOnLongClickListener {

                    onItemLongClick(file)

                }

            }

        }


        private fun getFileIcon(extension: String): Int {

            return when (extension.lowercase()) {

                "jpg", "jpeg", "png", "gif", "bmp" -> R.drawable.ic_image

                "mp4", "avi", "mkv", "mov" -> R.drawable.ic_video

                "mp3", "wav", "flac", "aac" -> R.drawable.ic_audio

                "pdf" -> R.drawable.ic_pdf

                "doc", "docx" -> R.drawable.ic_word

                "xls", "xlsx" -> R.drawable.ic_excel

                "ppt", "pptx" -> R.drawable.ic_ppt

                "txt" -> R.drawable.ic_text

                "zip", "rar", "7z" -> R.drawable.ic_zip

                "apk" -> R.drawable.ic_apk

                else -> R.drawable.ic_file

            }

        }

    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FileViewHolder {

        val binding = ItemFileBinding.inflate(

            LayoutInflater.from(parent.context), parent, false

        )

        return FileViewHolder(binding)

    }


    override fun onBindViewHolder(holder: FileViewHolder, position: Int) {

        holder.bind(fileList[position])

    }


    override fun getItemCount() = fileList.size


    fun getSelectedFiles(): List<FileModel> {

        return fileList.filter { it.isSelected }

    }

}

```

## 6. 文件管理主界面

```kotlin

class FileManagerFragment : Fragment() {


    private lateinit var binding: FragmentFileManagerBinding

    private lateinit var fileAdapter: FileListAdapter

    private var currentPath = Environment.getExternalStorageDirectory().absolutePath

    private val pathStack = Stack<String>()


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {

        binding = FragmentFileManagerBinding.inflate(inflater, container, false)

        return binding.root

    }


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

        super.onViewCreated(view, savedInstanceState)

        setupRecyclerView()

        setupToolbar()

        loadFiles(currentPath)

    }


    private fun setupRecyclerView() {

        fileAdapter = FileListAdapter(

            onItemClick = { file ->

                if (file.isDirectory) {

                    pathStack.push(currentPath)

                    currentPath = file.path

                    loadFiles(currentPath)

                } else {

                    openFile(file)

                }

            },

            onItemLongClick = { file ->

                showFileOptionsDialog(file)

                true

            }

        )


        binding.recyclerView.apply {

            layoutManager = LinearLayoutManager(context)

            adapter = fileAdapter

            addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))

        }

    }


    private fun setupToolbar() {

        binding.toolbar.setNavigationOnClickListener {

            onBackPressed()

        }


        binding.toolbar.setOnMenuItemClickListener { menuItem ->

            when (menuItem.itemId) {

                R.id.action_create_folder -> showCreateFolderDialog()

                R.id.action_sort -> showSortDialog()

                R.id.action_select_all -> selectAllFiles()

            }

            true

        }

    }


    private fun loadFiles(path: String) {

        lifecycleScope.launch(Dispatchers.IO) {

            val files = FileUtils.getFileList(path)

            withContext(Dispatchers.Main) {

                fileAdapter.updateList(files)

                binding.tvCurrentPath.text = path

                binding.tvEmptyView.visibility = if (files.isEmpty()) View.VISIBLE else View.GONE

            }

        }

    }


    private fun openFile(file: FileModel) {

        val intent = Intent(Intent.ACTION_VIEW)

        val uri = FileProvider.getUriForFile(

            requireContext(),

            "${requireContext().packageName}.fileprovider",

            File(file.path)

        )

        intent.setDataAndType(uri, FileUtils.getMimeType(File(file.path)))

        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)


        try {

            startActivity(intent)

        } catch (e: ActivityNotFoundException) {

            Toast.makeText(context, "没有应用可以打开此文件", Toast.LENGTH_SHORT).show()

        }

    }


    private fun showFileOptionsDialog(file: FileModel) {

        val options = arrayOf("复制", "剪切", "重命名", "删除", "属性")


        AlertDialog.Builder(requireContext())

            .setTitle(file.name)

            .setItems(options) { _, which ->

                when (which) {

                    0 -> copyFile(file)

                    1 -> cutFile(file)

                    2 -> showRenameDialog(file)

                    3 -> showDeleteConfirmDialog(file)

                    4 -> showFileProperties(file)

                }

            }

            .show()

    }


    private fun showCreateFolderDialog() {

        val editText = EditText(context)

        editText.hint = "文件夹名称"


        AlertDialog.Builder(requireContext())

            .setTitle("新建文件夹")

            .setView(editText)

            .setPositiveButton("创建") { _, _ ->

                val folderName = editText.text.toString().trim()

                if (folderName.isNotEmpty()) {

                    if (FileUtils.createDirectory(currentPath, folderName)) {

                        loadFiles(currentPath)

                        Toast.makeText(context, "文件夹创建成功", Toast.LENGTH_SHORT).show()

                    } else {

                        Toast.makeText(context, "文件夹创建失败", Toast.LENGTH_SHORT).show()

                    }

                }

            }

            .setNegativeButton("取消", null)

            .show()

    }


    private fun onBackPressed() {

        if (pathStack.isNotEmpty()) {

            currentPath = pathStack.pop()

            loadFiles(currentPath)

        } else {

            requireActivity().onBackPressed()

        }

    }

}

```

## 7. FileProvider 配置

### 在 AndroidManifest.xml 中添加

```xml

<provider

    android:name="androidx.core.content.FileProvider"

    android:authorities="${applicationId}.fileprovider"

    android:exported="false"

    android:grantUriPermissions="true">

    <meta-data

        android:name="android.support.FILE_PROVIDER_PATHS"

        android:resource="@xml/file_paths" />

</provider>

```

### 创建 res/xml/file_paths.xml

```xml

<?xml version="1.0" encoding="utf-8"?>

<paths>

    <external-path name="external_files" path="." />

    <files-path name="files" path="." />

    <cache-path name="cache" path="." />

</paths>

```

这个文件管理系统提供了基本的文件浏览、复制、移动、删除、重命名等功能。您可以根据需要添加更多功能,如文件搜索、压缩解压、文件分享等。


©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容