Android 文件選擇/文件管理

你們應該都有遇到過這種需求:上傳圖片,視頻文件時,直接調用相冊便可,無需過多的操做!可是若是說如今須要上傳pdf,zip,或者其餘相似xxx.sb文件類型時,你該怎麼搞了?調用系統文件管理(呵呵)!我就遇到過這種需求,並且遇到不止兩次了!咱們寫程序的都是幹大事的,這種繁瑣的工做怎麼能夠難住我了。因此 ZFileManager 就誕生了!完整代碼已上傳至GitHub ,記得start一下哦!!!java

小聲BB:圖片選擇框架也有哦GitHub android

ZFileManager 特色

  • 默認支持 音頻,視頻,圖片,txt,zip,word,excel,ppt,pdf 9種文件
  • 支持音頻、視頻播放,圖片查看,zip解壓,文件重命名、複製、移動、刪除、查看詳情
  • 支持查看指定文件類型,支持文件類型拓展
  • 支持多選,數量、文件大小限制、實時排序、指定文件路徑訪問
  • 支持QQ、微信文件選擇(支持自定義獲取)
  • 高度可定製化,支持AndroidX、DSL,兼容Java

部分截圖

使用(Java使用

舒適提示: targetSdkVersion >= 29 清單文件中加上 android:requestLegacyExternalStorage="true"

Step 0. 添加依賴

implementation 'com.github.zp:z_file:1.2.6'

	若是報錯加上
    
    // Kotlin Parcelable 支持
    androidExtensions {
        experimental = true
    }

複製代碼

Step 1. 實現ZFileImageListener,並在調用前或Application中初始化

class MyFileImageListener : ZFileImageListener() {

    override fun loadImage(imageView: ImageView, file: File) {
        // 以Glide爲例
        Glide.with(imageView.context)
            .load(file)
            .apply(RequestOptions().apply {
                placeholder(R.drawable.ic_zfile_other)
                error(R.drawable.ic_zfile_other)
            })
            .into(imageView)
    }
}

// 在調用前或Application中初始化 
getZFileHelp().init(MyFileImageListener())
複製代碼

Step 2. 在Activity或Fragment中使用

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        main_defaultMangerBtn.setOnClickListener {
            // 打開文件管理
            getZFileHelp()
                .result(this) {
                    val sb = StringBuilder()
                    this?.forEach {
                        sb.append(it).append("\n\n")
                    }
                    main_resultTxt.text = sb.toString()
                }
        }
    }

}


複製代碼

高級用法

ZFileManager提供了對外的幫助類 ZFileHelp

文件類型拓展

Step 1. 新建一個類:ZFileType,重寫裏面的openFile()、loadingFile()方法

// 自定義的類型
const val APK = "apk"

/** * 自定義Apk文件類型 */
class ApkType : ZFileType() {

    /** * 打開文件 * @param filePath 文件路徑 * @param view 當前視圖 */
    override fun openFile(filePath: String, view: View) {
        Toast.makeText(view.context, "打開自定義拓展文件", Toast.LENGTH_SHORT).show()
    }

    /** * 加載文件 * @param filePath 文件路徑 * @param pic 文件展現的圖片 */
    override fun loadingFile(filePath: String, pic: ImageView) {
        pic.setImageResource(R.mipmap.ic_launcher_round)
    }
    
}

複製代碼

Step 2. 新建一個類:ZFileTypeListener,重寫裏面的getFileType()方法 (有多個自定義類型,公用便可)

class MyFileTypeListener : ZFileTypeListener() {

    override fun getFileType(filePath: String) =
        when (ZFileHelp.getFileTypeBySuffix(filePath)) {
            APK -> ApkType()
            else -> super.getFileType(filePath)
        }
}

複製代碼

Step 3. 在調用前或Application中配置

getZFileHelp().setFileTypeListener(MyFileTypeListener())

複製代碼

QQ或微信文件選擇

super_qqTxt.setOnClickListener {
        // 打開QQ文件選擇
        getZFileHelp().setConfiguration(getZFileConfig().apply {
            filePath = ZFileConfiguration.QQ // 必須設置
        }).result(this) {
            setResult(this)
        }
    }

    super_wechatTxt.setOnClickListener {
        // 打開微信文件選擇
        getZFileHelp().setConfiguration(getZFileConfig().apply {
            filePath = ZFileConfiguration.WECHAT // 必須設置
        }).result(this) {
            setResult(this)
        }
    }

    private fun setResult(selectList: MutableList<ZFileBean>?) {
        val sb = StringBuilder()
        selectList?.forEach {
            sb.append(it).append("\n\n")
        }
        super_resultTxt.text = sb.toString()        
    }

複製代碼

DSL

zfile {
            imageLoade {
                // 配置 ZFileImageListener
            }
            fileLoade {
                // 配置 ZFileLoadListener
            }
            qwLoade {
                // 配置 ZQWFileLoadListener
            }
            config {
                // 配置 ZFileConfiguration
            }
            fileType {
                // 配置 ZFileTypeListener
            }
            fileOperate {
                // 配置 ZFileOperateListener
            }
            fileOpen {
                // 配置 ZFileOpenListener
            }
            result {
                // 獲取結果
            }
        }
複製代碼

舉個栗子

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        main_defaultMangerBtn.setOnClickListener {
            dsl()
        }
    }
    
    private fun dsl() {
        zfile {
            fileType {
                MyFileTypeListener()
            }
            config {
                ZFileConfiguration().apply {
                    filePath = ZFileConfiguration.WECHAT
                }
            }
            result {
                val sb = StringBuilder()
                this?.forEach {
                    sb.append(it).append("\n\n")
                }
                main_resultTxt.text = sb.toString()
            }
        }
    }

}

    
複製代碼
搞定,是否是很簡單 ^ _ ^
切,簡單是簡單,可是你這個獲取文件實現的方式不優雅,你這個QQ、微信文件根本不能獲取,你界面上的圖片太醜了,打開文件你所有都是調用系統方式打開的(做者你個渣渣),我只想選擇文件,不想要長按事件,文件操做我怎麼用都感受不正確,還有長按事件彈出的功能有些不是我想要的...
扶我起來,我要搞死槓精

自定義文件獲取

/** * 獲取文件 * 此方式,排序、是否顯示隱藏文件、過濾規則等等操做都須要本身實現 * Kotlin 獲取配置信息:getZFileConfig() * Java 獲取配置信息:ZFileManageHelp.getInstance().getConfiguration() */
class MyFileLoadListener : ZFileLoadListener {

    /** * 獲取手機裏的文件List * @param filePath String? 指定的文件目錄訪問,空爲SD卡根目錄 * @return MutableList<ZFileBean>? list */
    override fun getFileList(context: Context?, filePath: String?) =
        getFileList(context, filePath)

    private fun getFileList(context: Context?, filePath: String?): MutableList<ZFileBean> {
         
    }
}

// 在調用前或Application中配置
getZFileHelp().setFileLoadListener(MyFileLoadListener())


複製代碼

自定義QQ、微信文件獲取

class MyQWFileListener : ZQWFileLoadListener() {

    /** * 獲取標題 * @return Array<String> */
    override fun getTitles(): Array<String>? {
        return arrayOf("圖片", "媒體", "文檔", "其餘")
    }

    /** * 獲取過濾規則 * @param fileType Int 文件類型 see [ZFILE_QW_PIC] [ZFILE_QW_MEDIA] [ZFILE_QW_DOCUMENT] [ZFILE_QW_OTHER] */
    override fun getFilterArray(fileType: Int): Array<String> {
        return when (fileType) {
            ZFILE_QW_PIC -> arrayOf(PNG, JPG, JPEG, "gif")
            ZFILE_QW_MEDIA -> arrayOf(MP4, "3gp", "mp3")
            ZFILE_QW_DOCUMENT -> arrayOf(PDF, PPT, DOC, XLS)
            else -> arrayOf(TXT, JSON, XML, ZIP, "rar")
        }
    }

    /** * 獲取 QQ 或 WeChat 文件路徑 * @param qwType String QQ 或 WeChat see [ZFileConfiguration.QQ] [ZFileConfiguration.WECHAT] * @param fileType Int 文件類型 see [ZFILE_QW_PIC] [ZFILE_QW_MEDIA] [ZFILE_QW_DOCUMENT] [ZFILE_QW_OTHER] * @return MutableList<String> 文件路徑集合(由於QQ或WeChat保存的文件可能存在多個路徑) */
    override fun getQWFilePathArray(qwType: String, fileType: Int): MutableList<String> {
        val listArray = arrayListOf<String>()
        if (qwType == ZFileConfiguration.QQ) { // QQ
            when (fileType) {
                ZFILE_QW_PIC -> { 
                    listArray.add("/storage/emulated/0/tencent/QQ_Images/")
                    listArray.add("/storage/emulated/0/Pictures/") // QQ文件保存路徑1,僅作演示
                    listArray.add("/storage/emulated/0/DCIM/") // QQ文件保存路徑2,僅作演示
                    listArray.add("/storage/emulated/0/Pictures/QQ/")
                }
                ZFILE_QW_MEDIA -> {
                    listArray.add("/storage/emulated/0/Pictures/QQ/")
                }
                ZFILE_QW_DOCUMENT -> {
                    listArray.add("/storage/emulated/0/Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/")
                    listArray.add("/storage/emulated/0/Android/data/com.tencent.mobileqq/Tencent/QQ_business/")
                }
                else -> {
                    listArray.add("/storage/emulated/0/Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/")
                    listArray.add("/storage/emulated/0/Android/data/com.tencent.mobileqq/Tencent/QQ_business/")
                }
            }
        } else { // WeChat
            when (fileType) {
                ZFILE_QW_PIC -> {
                    listArray.add("/storage/emulated/0/tencent/MicroMsg/WeiXin/")
                    listArray.add("/storage/emulated/0/tencent/MicroMsg/WeiXin23/") // 微信文件保存路徑1,僅作演示
                    listArray.add("/storage/emulated/0/tencent/MicroMsg/WeiXin233/") // 微信文件保存路徑2,僅作演示
                }
                ZFILE_QW_MEDIA -> {
                    listArray.add("/storage/emulated/0/tencent/MicroMsg/WeiXin/")
                    listArray.add("/storage/emulated/0/Pictures/WeiXin/") // 微信文件保存路徑
                }
                ZFILE_QW_DOCUMENT -> {
                    listArray.add("/storage/emulated/0/tencent/MicroMsg/Download/")
                }
                else -> {
                    listArray.add("/storage/emulated/0/tencent/MicroMsg/Download/")
                }
            }
        }
        return listArray
    }

    /** * 獲取數據 * @param fileType Int 文件類型 see [ZFILE_QW_PIC] [ZFILE_QW_MEDIA] [ZFILE_QW_DOCUMENT] [ZFILE_QW_OTHER] * @param qwFilePathArray MutableList<String> QQ 或 WeChat 文件路徑集合 * @param filterArray Array<String> 過濾規則 */
    override fun getQWFileDatas(fileType: Int, qwFilePathArray: MutableList<String>, filterArray: Array<String>): MutableList<ZFileBean> {
        val pathListFile = arrayListOf<Array<File>?>()
        qwFilePathArray.forEach {
            val file = File(it)
            if (file.exists()) {
                pathListFile.add(file.listFiles(MyQWFilter(filterArray)))
            }
        }
        if (pathListFile.isEmpty()) return mutableListOf()
        val list = mutableListOf<ZFileBean>()
        pathListFile.forEach { item ->
            item?.forEach {
                if (!it.isHidden) {
                    val bean = ZFileBean(
                            it.name,
                            it.isFile,
                            it.path,
                            ZFileHelp.getFormatFileDate(it),
                            it.lastModified().toString(),
                            ZFileHelp.getFileSize(it.path),
                            it.length()
                    )
                    list.add(bean)
                }
            }
        }
        if (!list.isNullOrEmpty()) {
            list.sortByDescending { it.originalDate }
        }
        return list
    }

    class MyQWFilter(private var filterArray: Array<String>) : FileFilter {

        override fun accept(file: File): Boolean {
            filterArray.forEach {
                if (file.name.accept(it)) {
                    return true
                }
            }
            return false
        }

        private fun String.accept(type: String) =
                this.endsWith(type.toLowerCase(Locale.CHINA)) || this.endsWith(type.toUpperCase(Locale.CHINA))
    }
}

// 在調用前或Application中配置
getZFileHelp().setQWFileLoadListener(MyQWFileListener())


複製代碼

UI 或操做自定義 更多可查看 ZFileConfigurationvalues

/** * 起始訪問位置,非空爲指定訪問目錄,空爲SD卡根目錄 * 還可指定QQ或微信目錄 */
    var filePath: String? = null

    ...
    ...
    ...

    /** * 是否須要長按事件 */
    var needLongClick = true

    /** * 默認只有文件纔有長按事件 */
    var isOnlyFileHasLongClick = true

    /** * 長按後須要顯示的操做類型 */
    var longClickOperateTitles: Array<String>? = null

    /** * 相關資源配置 設置:[ZFILE_DEFAULT] 將使用默認資源 * @property audioRes Int 音頻 * @property txtRes Int 文本 * @property pdfRes Int PDF * @property pptRes Int PPT * @property wordRes Int Word * @property excelRes Int Excel * @property zipRes Int ZIP * @property otherRes Int 其餘類型 * @property emptyRes Int 空資源 * @property folderRes Int 文件夾 * @property lineColor Int 列表分割線顏色 */
    @Parcelize
    data class ZFileResources @JvmOverloads constructor(
        var audioRes: Int = R.drawable.ic_zfile_audio,
        var txtRes: Int = R.drawable.ic_zfile_txt,
        var pdfRes: Int = R.drawable.ic_zfile_pdf,
        var pptRes: Int = R.drawable.ic_zfile_ppt,
        var wordRes: Int = R.drawable.ic_zfile_word,
        var excelRes: Int = R.drawable.ic_zfile_excel,
        var zipRes: Int = R.drawable.ic_zfile_zip,
        var otherRes: Int = R.drawable.ic_zfile_other,
        var emptyRes: Int = R.drawable.ic_zfile_empty,
        var folderRes: Int = R.drawable.ic_zfile_folder,
        var lineColor: Int = R.color.zfile_line_color
    ) : Serializable, Parcelable
    
    // 在調用前或Application中配置
     getZFileHelp().setConfiguration(getZFileConfig().apply {
            resources = ZFileResources(R.drawable.ic_diy_audio)
            maxLength = 6
            maxLengthStr = "666"
            ...
            ...
            ...
        })
    
    // 其餘自定義方式 如:Txt類型展現的圖片不符合你的要求,繼承自TxtType,重寫相關方法便可,
    // 這種方式對於內置的文件類型能夠達到徹底自定義操做

複製代碼

自定義打開默認支持的文件

class MyFileOpenListener : ZFileOpenListener() {

    override fun openAudio(filePath: String, view: View) = Unit // 音頻
    override fun openImage(filePath: String, view: View) = Unit // 圖片
    override fun openVideo(filePath: String, view: View) = Unit // 視頻
    override fun openTXT(filePath: String, view: View) = Unit // 文本
    override fun openZIP(filePath: String, view: View) = Unit // zip壓縮包
    override fun openDOC(filePath: String, view: View) = Unit // word
    override fun openXLS(filePath: String, view: View) = Unit // xls
    override fun openPPT(filePath: String, view: View) = Unit // ppt
    override fun openPDF(filePath: String, view: View) = Unit // pdf
    override fun openOther(filePath: String, view: View) = Unit // 其餘文件
}

// 在調用前或Application中配置
getZFileHelp().setFileOpenListener(MyFileOpenListener())

複製代碼

自定義文件操做

class MyFileOperateListener : ZFileOperateListener() {

    /** * 文件重命名(該方式只須要實現重命名邏輯便可) * @param filePath String 文件路徑 * @param fileNewName String 新名字 * @param context Context Context * @param block Function2<Boolean, String, Unit> Boolean:成功或失敗;String:新名字 */
    open fun renameFile( filePath: String, fileNewName: String, context: Context, block: (Boolean, String) -> Unit ) {
       
    }

    /** * 複製文件 耗時操做,建議放在非UI線程中執行 * @param sourceFile String 源文件地址 * @param targetFile String 目標文件地址 * @param context Context Context * @param block 文件操做成功或失敗後的監聽 */
    override fun copyFile( sourceFile: String, targetFile: String, context: Context, block: Boolean.() -> Unit ) {
       thread {
           val success = MyFileUtil.copyFile(sourceFile, targetFile, context)
           (context as? Activity)?.let { 
               it.runOnUiThread { 
                   block.invoke(success)
               }
           }
       }
    }

    /** * 移動文件 耗時操做,建議放在非UI線程中執行 */
    override fun moveFile( sourceFile: String, targetFile: String, context: Context, block: Boolean.() -> Unit ) {
    
    }

    /** * 刪除文件 耗時操做,建議放在非UI線程中執行 */
    override fun deleteFile(filePath: String, context: Context, block: Boolean.() -> Unit) {
         
    }

    /** * 解壓文件 耗時操做,建議放在非UI線程中執行 */
    override fun zipFile( sourceFile: String, targetFile: String, context: Context, block: Boolean.() -> Unit ) {
    }

    /** * 文件詳情 */
    override fun fileInfo(bean: ZFileBean, context: Context) {
        
    }
}

// 在調用前或Application中配置
getZFileHelp().setFileOperateListener(MyFileOperateListener())

複製代碼
更多操做請查看demo 若是以爲能夠記得 star 一下哦

小聲BB:圖片選擇框架也有哦GitHub git

一切都源自以前的項目,從簡單的一個文件選取列表 到 須要預覽文件,再到QQ微信文件選擇,每一次的需求變動都意味着又TM要重寫了。乾脆所有推倒重來,因而乎該庫就誕生了... 下個版本將支持 Android 11,幹就完事了...github

相關文章
相關標籤/搜索