1️⃣ 🧑💻 Full time Mobile Developer, most focus on Android
2️⃣ ▶ Responsible for live Streaming-Related business.
3️⃣ ⛏️ Digging into Kotlin Multiplatform now ;)
4️⃣ Chinese / Enghlish, Currently working in Shenzhen, China. Open to relocation
FilePicker is a small and fast file selector library that is constantly evolving with the goal of rapid integration, high customization, and configurability~
Home Page: https://afp.rosuh.me
License: MIT License
我看了下FileUtils.kt 中
方法fun getRootFile():File
发现无论我设置说明存储类型他都说返回同一个位置的路径:
File(Environment.getExternalStorageDirectory().absoluteFile.toURI())
是否可以实现单选?
please add ogg to audio filter
AudioFileType :
return when (suffix){
"aif", "iff", "m3u", "m4a", "mid", "mp3", "mpa", "wav", "wma","ogg" -> {
true
}
else -> {
false
}
}
doesn't work on Samsung SM-G9550 Android 8.0.0 API 26
如题,文件排序现在支持了么?
在oppo a59s 和红米note6手机上运行demo,点击只展示文件夹和只展示图片,分别无效。
例如webview调用系统自带的文件选择器返回的结果是以content://格式的路径
背景:java代码中调用
在0.3.0 中 没有"APP" 这个application文件
故没有问题
在0.4.0中 因为框架中有"APP"这个application文件
导致与项目本身的application文件冲突,需要加入 'tools:replace="android:name"' 来解决
当加入 'tools:replace="android:name"' 解决后 调用文件选择器时,会出现如下崩溃
kotlin.UninitializedPropertyAccessException: lateinit property appResources has not been initialized
at me.rosuh.filepicker.App.access$getAppResources$cp(App.kt:11)
at me.rosuh.filepicker.App$Companion.getAppResources(App.kt:14)
at me.rosuh.filepicker.config.FilePickerConfig.(FilePickerConfig.kt:34)
at me.rosuh.filepicker.config.FilePickerManager.from(FilePickerManager.kt:30)
at com.xxx.xxx.xxx.xxxActivity.selectDocument(xxxActivity.java:608)
at com.xxx.xxx.xxx.xxxActivity$5.onClick(xxxActivity.java:291)
at android.view.View.performClick(View.java:6897)
at android.view.View$PerformClick.run(View.java:26101)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
框架中的application文件"APP" 中 appResources 未初始化?
这难道是java代码与Kotlin代码联结的问题么?
在调试的时候发现console会出RecyclerView: No adapter attached; skipping layout这个错误
如题。现在单选的时候,出现了全选,取消全选,选择框也是复选框,我们测试说会误导客户...大佬,这些能改成单选时,全选和取消全选去掉,选择框改为单选框么?
目前可用CustomRootPath指定打开时的根目录,但因为是根目录的关系,选择时无法返回上一级目录。
能否指定一个起始目录,我打开选择器后会进入该起始目录,如果起始目录非根目录时,还可以正常返回上一级目录。
这在实现有记忆上一次用户选择的路径的功能的文件选择器时很有用。
系统为中文的情况下,默认显示为英文,此时自定义文字内容,将文字改为中文,初次打开依旧为英文(初次打开自定义文字没生效,需要选中或打开打开下一级的文件夹文字才会更新)
Hi,
Thank you very much for your contributions. I have to use your library with many customize and this is some point that I have made a change to my own.
This is some sample code that anyone can try
/**
* 根据配置参数获取根目录文件
* @return File
*/
fun getRootFile(): File {
return when (FilePickerManager.config.mediaStorageType) {
STORAGE_EXTERNAL_STORAGE -> {
File(Environment.getExternalStorageDirectory().absoluteFile.toURI())
}
STORAGE_CUSTOM_ROOT_PATH -> {
if (FilePickerManager.config.customRootPath.isEmpty()) {
File(Environment.getExternalStorageDirectory().absoluteFile.toURI())
} else {
File(FilePickerManager.config.customRootPath)
}
}
else -> {
File(Environment.getExternalStorageDirectory().absoluteFile.toURI())
}
}
}
/**
* Function to get all external storage list
*/
fun getExternalStorageList(context: Context): List<String> {
val files = ContextCompat.getExternalFilesDirs(context, null)
val externalFiles = mutableListOf<String>()
for (i in files.indices) {
val file = files[i]
if (file != null && file.canRead()) {
val substring = file.absolutePath
.substring(0, file.absolutePath.indexOf("/Android/data/"))
externalFiles.add(substring)
}
}
return externalFiles
}
override fun onItemClick(
recyclerAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>,
view: View,
position: Int
) {
val item = (recyclerAdapter as me.rosuh.filepicker.adapter.BaseAdapter).getItem(position)
item ?: return
val file = File(item.filePath)
if (!file.exists()) {
return
}
when (view.id) {
R.id.item_list_file_picker -> {
if (file.isDirectory) {
(rv_nav_file_picker?.adapter as? FileNavAdapter)?.let {
saveCurrPos(it.data.last(), position)
}
// 如果是文件夹,则进入
enterDirAndUpdateUI(item)
} else {
// Navigate data to activity
FilePickerManager.config.fileItemOnClickListener.onItemClick(
recyclerAdapter,
view,
position
)
}
}
R.id.item_nav_file_picker -> {
if (file.isDirectory) {
// In-case this is the root of path
if (position == 0 && strExternalStoragePaths.size > 1) {
// Display the popup that the user can switch between external storage like SD-card, internal storage ...
/* Creating the instance of PopupMenu */
popup = PopupMenu(
requireContext(),
(rv_nav_file_picker?.layoutManager as? LinearLayoutManager)?.findViewByPosition(
0
) ?: rv_nav_file_picker
)
for (index in strExternalStoragePaths.indices) {
popup?.menu?.add(
0,
index,
index,
if (index == 0) R.string.file_picker_tv_internal_storage else R.string.file_picker_tv_sd_card
)
}
// Set handle event click
popup?.setOnMenuItemClickListener { item ->
/*
* In-case data is invalid
*/
if (item.itemId < 0 || item.itemId >= strExternalStoragePaths.size) {
return@setOnMenuItemClickListener false
}
// Get new path of selected menu item
val strNewPath = strExternalStoragePaths[item.itemId]
// In-case the path is null
if (TextUtils.isEmpty(strNewPath)) {
return@setOnMenuItemClickListener false
}
// Handle UI/ update view after get new root path
pickerConfig.setCustomRootPath(strNewPath)
// Clear data and re-load list
navDataSource.clear()
// Reload list.
loadList()
true
}
popup?.show()
} else {
navAdapter?.let {
saveCurrPos(it.data.last(), position)
}
// 如果是文件夹,则进入
enterDirAndUpdateUI(item)
}
}
}
}
}
All rights, you can implement it for your own caused my code is changed so much so I can't publish the sample.
The video below is demoed my code.
/**
* 不能使用 fragment.getContext(),因为无法保证外部的代码环境
*/
fun from(fragment: Fragment):FilePickerConfig{
this.fragment = WeakReference(fragment)
this.context = WeakReference(fragment.activity!!)
return config!!
}
在fragment 中 FilePickerConfig 是不是没有初始化?还是我使用方法不对。
好像没有设置默认打开目录的功能,希望能加上
是高版本原因导致还是目前没有适配
1.指定数量功能失效:
当指定选择数量后,点击全选功能,仍然可以选中全部(超出指定数量),并且过滤掉的文件貌似也被选中,但是不会将数据返回至activity中.
2.能选中过滤掉的文件
当指定选择数量后,在文件过滤后,点击全选,估计是选中了过滤掉的文件,只是没显示,然后点击确认 会提示选择超过多个,界面上看起来并没有选这么多,可能选中的那些是过滤掉的文件,
快速滑动时,列表会掉帧严重。
不建议在recycleView的item中使用android.support.constraint.ConstraintLayout
约束布局作为根布局,其重复测量会导致严重性能问题。
建议使用传统的布局控件,例如线性布局、帧布局等等
既然名字叫FilePicker
,而实际上 Directory 也是 File,所以添加选择文件夹的功能也是很正常的吧?
以上都是胡扯,就是我需要这个功能(单选/多选文件夹)……
比如只想显示视频,
AbstractFileFilter aFilter = new AbstractFileFilter() {
@NotNull
@Override
public ArrayList<FileItemBeanImpl> doFilter(@NotNull final ArrayList<FileItemBeanImpl> arrayList) {
ArrayList<FileItemBeanImpl> fileItemBeans = new ArrayList<>();
for (FileItemBeanImpl fileItemBean : arrayList){
if (fileItemBean.isChecked() || fileItemBean.getFileType() instanceof VideoFileType){
fileItemBeans.add(fileItemBean);
}
}
return fileItemBeans;
}
};
FilePickerManager.INSTANCE
.from(mActivity)
.filter(aFilter)
.maxSelectable(1)
.setTheme(R.style.FilePickerThemeRail)
.showCheckBox(false)
.forResult(FilePickerManager.REQUEST_CODE);
还是可以看到并选择其他类型的文件
1、使用了kotlin,为何还要使用findViewById
。是有什么特殊的原因么?
2、进入activity后,扫描文件的工作应该放到子线程
或 协程
中去做,就不出现莫名其妙的启动等待时间。(个人建议放入协程,第一轻量级,第二异地可以当同步,不影响现有逻辑顺序)
java.lang.IllegalStateException: Module with the Main dispatcher is missing. Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android'
在调用的时候出现这个问题
我的项目编译和目标版本是27,用gradle依赖下来,编译报错
Unable to open folder and mark file using remote control.
// 自定义过滤器(可选)
.filter(fileFilter)
未添加过滤器会导致崩溃,因此过滤器为必须添加,并不是可选
现在每次打开下一级目录,再返回上一级目录时,没有记录上次滑动的位置,导致体验上很差。
现在好像是默认到根目录 文件比较多 文件夹很散乱 没有顺序
找个文件都特别不好找
如题。公司的测试非要排序按照系统那样,数字,aA,bB这样排序
比如BRVAH,非必要的三方库是否会增加库 本身的体积?
How to translate "Select all" and "Done" buttons ?
.showCheckBox(false) 没有作用 , 能不能点击文件列表item, 直接回调, 而不是勾了复选框 , 再点击确认 , 在返回, 即FileItemOnClickListenerImpl 这个里面提供下单项点击的实现
java.lang.IllegalStateException: Module with the Main dispatcher is missing. Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android'
at kotlinx.coroutines.internal.q.a(MainDispatchers.kt:72)
at kotlinx.coroutines.internal.q.a(MainDispatchers.kt:53)
at kotlinx.coroutines.ac.a(Dispatched.kt:410)
at kotlinx.coroutines.a.a.a(Cancellable.kt:25)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
at kotlinx.coroutines.a.a(AbstractCoroutine.kt:154)
at kotlinx.coroutines.d.a(Builders.common.kt:54)
at kotlinx.coroutines.c.a(Unknown Source)
at kotlinx.coroutines.d.a(Builders.common.kt:47)
at kotlinx.coroutines.c.a(Unknown Source)
at me.rosuh.filepicker.FilePickerActivity.g(FilePickerActivity.kt:121)
at me.rosuh.filepicker.FilePickerActivity.onCreate(FilePickerActivity.kt:73)
at android.app.Activity.performCreate(Activity.java:6497)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2519)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2626)
at android.app.ActivityThread.access$1100(ActivityThread.java:170)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1494)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:179)
at android.app.ActivityThread.main(ActivityThread.java:5769)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:674)
09-04 17:26:25.101 1606-2697/? E/libprocessgroup: failed to kill 1 processes for processgroup 2676
09-04 17:26:25.230 2832-2832/? E/Tinker.ReflectApp: replaceApplicationLike delegateClass:class com.tencent.bugly.beta.tinker.TinkerApplicationLike
09-04 17:26:25.364 2832-2832/? E/ExtMediaPlayer-JNI: env->IsInstanceOf fails
09-04 17:26:25.364 2832-2832/? E/MediaPlayer-JNI: JNIMediaPlayerFactory: bIsQCMediaPlayerPresent 0
09-04 17:26:25.364 2832-2832/? E/ExtMediaPlayer-JNI: env->IsInstanceOf fails
09-04 17:26:25.364 2832-2832/? E/MediaPlayer-JNI: JNIMediaPlayerFactory: bIsQCMediaPlayerPresent 0
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.