Skip to content

Commit a932213

Browse files
committed
♻️ 🎨 🚀 📝
[Add] - 链式调用 - 添加文件类型抽象类 - 公开文件类型接口 - 添加文件过滤抽象类 - 公开文件过滤器接口 - 公开条目点击接口,可以自实现条目点击效果 - 添加界面字符串自定义功能 - 返回键返回上层目录功能 - 添加 FilePickerConfig 类保存配置 - 新增四种主题配色 [Update] - 文件类型可由调用者自己实现,也可以使用默认实现 - FileItemBean 添加图标资源变量,支持自定义类型图标 - 调用 FilePickerManager.obtainData() 获取数据,Intent 仅作消息发送功能 - 更新部分文件类型默认 icon - README
1 parent b88b596 commit a932213

30 files changed

+848
-273
lines changed

README.md

Lines changed: 103 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,15 @@
11
# AndroidFilePicker
22
[![](https://jitpack.io/v/me.rosuh/AndroidFilePicker.svg)](https://jitpack.io/#me.rosuh/AndroidFilePicker)
33

4-
:bookmark: FilePicker 是一个小巧快速的文件选择器框架,以快速集成、高自定义化和可配置化为目标不断前进~
5-
64
# I 简介
75

8-
## 功能
9-
1. 独立的 Activity 浏览视图
10-
- 通过`Intent`启动`Activity`,通过`onActivityResult()`获取返回结果
11-
2. 文件筛选显示
12-
- 通过自定义文件类型来配置的文件筛选/过滤,如果你只想显示压缩文件,一行代码搞定
13-
- 自定义过滤器:暴露接口让你自定义过滤器
14-
- 可选择是否显示隐藏的文件/文件夹
15-
- 可选择是否选中文件夹
16-
3. 文件多选/全选
17-
4. 文件夹导航栏
6+
🔖 FilePicker 是一个小巧快速的文件选择器框架,以快速集成、高自定义化和可配置化为目标不断前进~🚩
187

19-
# II 使用
8+
## 展示
209

21-
*`sample`模块是使用示例*
10+
展示图正在努力绘制中...不如 clone 后 build 出来看看?😝
11+
12+
# II 使用
2213

2314
1. 在你的项目中添加依赖
2415

@@ -41,23 +32,28 @@ dependencies {
4132

4233
2. 开始使用
4334

44-
*启动*:在你的`Activity`中启动文件选择器
35+
简单的链式调用示意:
4536

4637
```java
47-
val intent = Intent(this, FilePickerActivity::class.java)
48-
startActivityForResult(intent, FilePickerManager.REQUEST_CODE)
38+
FilePickerManager.instance
39+
.from(this@SampleActivity)
40+
// 主题设置
41+
.setTheme(R.style.FilePickerThemeReply)
42+
// 自定义过滤器(可选)
43+
.filter(fileFilter)
44+
.forResult(FilePickerManager.instance.REQUEST_CODE)
4945
```
5046

51-
*获取结果*`onActivityResult`获取返回的结果,结果是所选取文件的路径列表(`ArrayList<String>()`)
47+
*获取结果*`onActivityResult`接受消息,然后调用`FilePickerManager.obtainData()`获取保存的数据,结果是所选取文件的路径列表(`ArrayList<String>()`)
5248

5349
```java
5450
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
5551
when (requestCode) {
56-
FilePickerManager.REQUEST_CODE -> {
52+
FilePickerManager.instance.REQUEST_CODE -> {
5753
if (resultCode == Activity.RESULT_OK) {
58-
val bundle = data!!.extras
59-
val list = bundle!!.getStringArrayList(FilePickerManager.RESULT_KEY)
60-
// do your work...
54+
val list = FilePickerManager.instance.obtainData()
55+
rv!!.adapter = SampleAdapter(R.layout.demo_item, ArrayList(list))
56+
rv!!.layoutManager = LinearLayoutManager(this@SampleActivity)
6157
} else {
6258
Toast.makeText(this@SampleActivity, "没有选择图片", Toast.LENGTH_SHORT).show()
6359
}
@@ -68,20 +64,94 @@ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
6864

6965

7066

71-
### 细节
67+
## 功能 & 特点
7268

69+
1. 链式调用
70+
2. 内置四种主题配色 + 可自定义配色
71+
- 查看主题颜色示意图,然后调用`setTheme()`传入自定义主题
72+
3. 默认实现多种文件类型
73+
- 实现`IFileType`接口来实现你的文件类型
74+
- 实现`AbstractFileType`抽象类来实现你的文件类型甄别器
75+
4. 公开文件过滤接口
76+
- 实现`AbstractFileFilter`抽象类来定制你自己的文件过滤器,这样可以控制文件列表的展示内容
77+
5. 多种可配置选项
78+
1. 选中时是否忽略文件夹
79+
2. 是否显示隐藏文件夹(以符号`.`开头的,视为隐藏文件或隐藏文件夹)
80+
3. 可配置导航栏的文本,默认显示、多选文本、取消选择文本以及根目录默认名称
81+
6. 公开条目(`item`)选择监听器,可自定义条目被点击的实现
7382

83+
### 部分源码说明
7484

75-
1. `FilePickerManager`类是配置类,你可以配置『是否显示隐藏文件』、『是否忽略选中文件夹』等选项
76-
2. `FileFilter.selfFilter()`接口是用以让您自定义过滤器的
77-
- 传入的是文件列表的数据集
78-
- 数据集经过您的处理之后,再生出`Adapter`,绑定视图之后展示出来
85+
1. 包和文件夹
7986

80-
# TODO
87+
- `adapter`包:两个列表(导航栏和文件列表)的数据适配器类
88+
89+
- `bean`包:所有用到的`Model`
90+
91+
- `IFileBean`是文件对象所需要实现的接口
92+
93+
- `config`:管理类、配置类所在
94+
95+
- `AbstractFileFilter`:文件过滤器抽象类,用于给调用者自实现文件过滤器
96+
- `AbstractFileType`:文件类型抽象类,用于给调用者自实现自己的文件类型
97+
- 其中的抽象函数`fillFileType`为文件甄别器,如果你实现了自己的文件类型,那么最好也要实现自己的文件甄别器
98+
- `DefaultFileType`:默认文件类型,文件类型类的默认实现,里面实现了默认的文件甄别器
99+
100+
- `filetype`:一些默认实现的文件类型
101+
102+
- 实现接口`IFileType`以实现自己的文件类型
103+
104+
- `utils`:一些工具类
105+
106+
- `FileUtils`类包含了文件相关的大部分所需的函数
107+
- `PercentLayoutUtils``PercentTextView``TextView`的相对布局实现(*1)
108+
109+
110+
# Log
111+
112+
## 2018-11-27
113+
114+
:recycle: :art: :rocket: :memo:
115+
116+
### Add
81117

82-
- 解耦视图和控制逻辑,为后续自定义布局铺路
83-
- 列表项可打开,可配置打开方式
84-
- 记住父文件夹浏览位置
85-
- 更优雅的方式获取返回结果,`onActivityResult()`只发送通知消息,从另一容器拿到结果
86118
- 链式调用
87-
- 默认视图美观度提升
119+
- 添加文件类型抽象类
120+
- 公开文件类型接口
121+
- 添加文件过滤抽象类
122+
- 公开文件过滤器接口
123+
- 公开条目点击接口,可以自实现条目点击效果
124+
- 添加界面字符串自定义功能
125+
- 返回键返回上层目录功能
126+
- 添加 FilePickerConfig 类保存配置
127+
- 新增四种主题配色
128+
129+
### Update
130+
131+
- 文件类型可由调用者自己实现,也可以使用默认实现
132+
- FileItemBean 添加图标资源变量,支持自定义类型图标
133+
- 调用 FilePickerManager.obtainData() 获取数据,Intent 仅作消息发送功能
134+
- 更新部分文件类型默认 icon
135+
- README
136+
137+
138+
# TODO
139+
140+
- [x] 列表项可打开,可配置打开方式
141+
- [x] 更优雅的方式获取返回结果,`onActivityResult()`只发送通知消息,从另一容器拿到结果
142+
- [x] 默认视图美观度提升
143+
- [x] 链式调用
144+
- [ ] 记住父文件夹浏览位置
145+
- [ ] 解耦视图和控制逻辑,为后续自定义布局铺路
146+
147+
148+
149+
---
150+
151+
# Special Thanks To:
152+
153+
- [*1 @whichName](https://github.com/whichname)
154+
155+
- [BRVAH](https://github.com/CymChad/BaseRecyclerViewAdapterHelper)
156+
- [Matisse](https://github.com/zhihu/Matisse)
157+

filepicker/src/main/java/me/rosuh/filepicker/FilePickerActivity.kt

Lines changed: 42 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package me.rosuh.filepicker
22

33
import android.Manifest
4+
import android.annotation.SuppressLint
45
import android.app.Activity
5-
import android.content.ActivityNotFoundException
66
import android.content.Intent
77
import android.content.pm.PackageManager
8-
import android.net.Uri
98
import android.support.v7.app.AppCompatActivity
109
import android.os.Bundle
1110
import android.os.Environment
@@ -18,24 +17,20 @@ import android.support.v7.widget.LinearLayoutManager
1817
import android.support.v7.widget.RecyclerView
1918
import android.view.View
2019
import android.view.ViewGroup
20+
import android.widget.Button
2121
import android.widget.CheckBox
2222
import android.widget.Toast
2323
import com.chad.library.adapter.base.BaseQuickAdapter
2424
import com.chad.library.adapter.base.BaseViewHolder
25-
import me.rosuh.filepicker.bean.FileTypeEnum.COMPRESSED
26-
import me.rosuh.filepicker.bean.FileTypeEnum.DIR
27-
import me.rosuh.filepicker.bean.FileTypeEnum.IMAGE
28-
import me.rosuh.filepicker.bean.FileTypeEnum.OCTET_STREAM
29-
import me.rosuh.filepicker.bean.FileTypeEnum.UNKNOWN
30-
import me.rosuh.filepicker.bean.FileTypeEnum.VIDEO
3125
import me.rosuh.filepicker.adapter.FileListAdapter
3226
import me.rosuh.filepicker.adapter.FileNavAdapter
3327
import me.rosuh.filepicker.bean.FileItemBean
3428
import me.rosuh.filepicker.bean.FileNavBean
3529
import me.rosuh.filepicker.bean.IFileBean
30+
import me.rosuh.filepicker.config.FilePickerConfig
3631
import me.rosuh.filepicker.config.FilePickerManager
37-
import me.rosuh.filepicker.config.FilePickerManager.RESULT_KEY
3832
import me.rosuh.filepicker.utils.FileUtils
33+
import me.rosuh.filepicker.utils.PercentTextView
3934
import java.io.File
4035
import java.util.concurrent.atomic.AtomicBoolean
4136

@@ -71,11 +66,11 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
7166
private var mBtnConfirm: AppCompatButton? = null
7267
private var mBtnGoBack: AppCompatImageButton? = null
7368
private val mFilesIsChecked: AtomicBoolean? = AtomicBoolean(false)
74-
75-
private val FILE_PICKER_PERMISSION_REQUEST_CODE = 10201
69+
private var mTvSelected: PercentTextView? = null
70+
private val pickerConfig by lazy { FilePickerConfig.getInstance(FilePickerManager.instance) }
7671

7772
override fun onCreate(savedInstanceState: Bundle?) {
78-
setTheme(FilePickerManager.themeId)
73+
setTheme(pickerConfig.themeId)
7974

8075
super.onCreate(savedInstanceState)
8176
setContentView(R.layout.activity_file_picker)
@@ -101,7 +96,7 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
10196
ActivityCompat.requestPermissions(
10297
this@FilePickerActivity,
10398
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
104-
FILE_PICKER_PERMISSION_REQUEST_CODE
99+
Companion.FILE_PICKER_PERMISSION_REQUEST_CODE
105100
)
106101
}
107102

@@ -111,7 +106,7 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
111106
grantResults: IntArray
112107
) {
113108
when (requestCode) {
114-
FILE_PICKER_PERMISSION_REQUEST_CODE -> {
109+
Companion.FILE_PICKER_PERMISSION_REQUEST_CODE -> {
115110
if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
116111
Toast.makeText(this@FilePickerActivity, "未授予存储权限", Toast.LENGTH_SHORT).show()
117112
} else {
@@ -126,7 +121,7 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
126121
*/
127122
private fun prepareLauncher() {
128123
if (Environment.getExternalStorageState() != MEDIA_MOUNTED) {
129-
throw Throwable(IllegalStateException("外部存储不可用"))
124+
throw Throwable(cause = IllegalStateException("外部存储不可用"))
130125
}
131126

132127
// 根目录文件对象
@@ -149,6 +144,7 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
149144
mBtnSelectedAll = findViewById(R.id.btn_selected_all_file_picker)
150145
mBtnConfirm = findViewById(R.id.btn_confirm_file_picker)
151146
mBtnGoBack = findViewById(R.id.btn_go_back_file_picker)
147+
mTvSelected = findViewById(R.id.tv_toolbar_title_file_picker)
152148
mBtnGoBack!!.setOnClickListener(this)
153149
mBtnSelectedAll!!.setOnClickListener(this)
154150
mBtnConfirm!!.setOnClickListener(this)
@@ -198,7 +194,7 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
198194
}
199195

200196
/**
201-
* 根据被点击项的类型,触发不同的操作
197+
* 传递条目点击事件给调用者
202198
* @param adapter BaseQuickAdapter<*, *>?
203199
* @param view View?
204200
* @param position Int
@@ -207,40 +203,13 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
207203
// 如果不是点击列表,则返回
208204
if (view!!.id != R.id.item_list_file_picker) return
209205
val item = adapter!!.getItem(position) as FileItemBean
206+
val file = File(item.filePath)
210207

211-
val intent = Intent()
212-
intent.action = Intent.ACTION_SEND
213-
try {
214-
when (item.fileType) {
215-
DIR -> {
216-
// 文件夹则进入
217-
enterDirAndUpdateUI(item)
218-
}
219-
IMAGE -> {
220-
intent.type = "image/*"
221-
intent.data = Uri.parse(item.mFilePath)
222-
startActivity(intent)
223-
}
224-
VIDEO -> {
225-
intent.type = "video/*"
226-
intent.data = Uri.parse(item.filePath)
227-
startActivity(intent)
228-
}
229-
COMPRESSED -> {
230-
val sub = item.mFileName.substring(item.mFileName.lastIndexOf("."))
231-
intent.type = "application/$sub"
232-
intent.data = Uri.parse(item.filePath)
233-
startActivity(intent)
234-
}
235-
UNKNOWN -> {
236-
Toast.makeText(this@FilePickerActivity, "我们不知道如何打开该文件", Toast.LENGTH_SHORT).show()
237-
}
238-
OCTET_STREAM -> {
239-
Toast.makeText(this@FilePickerActivity, "我们不知道如何打开该文件", Toast.LENGTH_SHORT).show()
240-
}
241-
}
242-
} catch (ae: ActivityNotFoundException) {
243-
Toast.makeText(this@FilePickerActivity, "没有应用可以打开该文件", Toast.LENGTH_SHORT).show()
208+
if (file.exists() && file.isDirectory) {
209+
// 如果是文件夹,则进入
210+
enterDirAndUpdateUI(item)
211+
} else {
212+
FilePickerConfig.getInstance(FilePickerManager.instance).fileIFileItemOnClickListener?.onItemClick(item, position)
244213
}
245214
}
246215

@@ -263,6 +232,8 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
263232

264233
/**
265234
* 从导航栏中调用本方法,需要传入 pos,以便生产新的 nav adapter
235+
* @param iFileBean IFileBean
236+
* @param position Int 用来定位导航栏的当前 item,如果是后退按钮,则传入倒数第二个 position
266237
*/
267238
private fun enterDirAndUpdateUI(iFileBean: IFileBean, position: Int) {
268239
// 获取文件夹文件
@@ -301,7 +272,13 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
301272
}
302273

303274
override fun onBackPressed() {
304-
super.onBackPressed()
275+
if (mNavDataSource.size <= 1) {
276+
super.onBackPressed()
277+
} else {
278+
// 即将进入的 item 的索引
279+
val willEnterItemPos = mNavDataSource.size - 2
280+
enterDirAndUpdateUI(mNavDataSource[willEnterItemPos], willEnterItemPos)
281+
}
305282
}
306283

307284
override fun onClick(v: View?) {
@@ -310,20 +287,26 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
310287
R.id.btn_selected_all_file_picker -> {
311288
if (mFilesIsChecked!!.get()) {
312289
for (data in mListAdapter!!.data) {
313-
if (FilePickerManager.isSkipDir && data.fileType == DIR) {
290+
val file = File(data.filePath)
291+
if (pickerConfig.isSkipDir && file.exists() && file.isDirectory) {
314292
continue
315293
}
316294
data.isChecked = false
317295
}
318-
mBtnSelectedAll!!.text = "图片全选"
296+
mBtnSelectedAll!!.text = pickerConfig.selectAllText
297+
mTvSelected!!.text = pickerConfig.goBackText
319298
} else {
299+
var checkedCount = 0
320300
for (data in mListAdapter!!.data) {
321-
if (FilePickerManager.isSkipDir && data.fileType == DIR) {
301+
val file = File(data.filePath)
302+
if (pickerConfig.isSkipDir && file.exists() && file.isDirectory) {
322303
continue
323304
}
324305
data.isChecked = true
306+
checkedCount++
325307
}
326-
mBtnSelectedAll!!.text = "取消选中"
308+
mBtnSelectedAll!!.text = pickerConfig.unSelectAllText
309+
mTvSelected!!.text = pickerConfig.hadSelectedText + checkedCount
327310
}
328311

329312
mListAdapter!!.notifyDataSetChanged()
@@ -344,7 +327,7 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
344327
finish()
345328
}
346329

347-
intent.putExtra(RESULT_KEY, list)
330+
FilePickerManager.instance.saveData(list)
348331
this@FilePickerActivity.setResult(Activity.RESULT_OK, intent)
349332
finish()
350333
}
@@ -353,4 +336,8 @@ class FilePickerActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickList
353336
}
354337
}
355338
}
339+
340+
companion object {
341+
const val FILE_PICKER_PERMISSION_REQUEST_CODE = 10201
342+
}
356343
}

0 commit comments

Comments
 (0)