一,实现的功能
如下图所示:实现效果
二,开始代码
遇到的问题解决:
下面开始兴建一个项目MyFileManager
拿到外置存储卡: Environment.getExternalStorageDirectory().absolutePath
Environment.getExternalStorageDirectory().absolutePath可以拿到存储卡的,如果有外置sd卡就拿到外置sd卡,如果没有就会内置sd卡,由于我手机都是内置。
当然这里我们可以判断是否具有外置存储卡。这一两年内好多手机都没了外置卡吧,我记得我老爷手机有外置卡槽。这里通过反射我们可以判断是否具有外置卡。代码如下:
/**
* 反射调用获取内置存储和外置sd卡根路径
* @param mContext 上下文
* @param haveSdCard 是否有卡槽外置卡,false返回内部存储,true返回外置sd卡
* @return
*/
private fun getStoragePath(mContext: Context, haveSdCard: Boolean): String? {
val mStorageManager = mContext.getSystemService(Context.STORAGE_SERVICE) as StorageManager
var storageVolumeClazz: Class<*>? = null
try {
storageVolumeClazz = Class.forName("android.os.storage.StorageVolume")
val getVolumeList = mStorageManager.javaClass.getMethod("getVolumeList")
val getPath = storageVolumeClazz!!.getMethod("getPath")
val isRemovable = storageVolumeClazz.getMethod("isRemovable")
val result = getVolumeList.invoke(mStorageManager)
val length = Array.getLength(result)
for (i in 0 until length) {
val storageVolumeElement = Array.get(result, i)
val path = getPath.invoke(storageVolumeElement) as String
val removable = isRemovable.invoke(storageVolumeElement) as Boolean
Log.e("isRemove",removable.toString())
if (haveSdCard == removable) {
return path
}
}
} catch (e: ClassNotFoundException) {
e.printStackTrace()
} catch (e: InvocationTargetException) {
e.printStackTrace()
} catch (e: NoSuchMethodException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
return null
}
有了路径我们可以获取这个根路径下面所有的文件信息,我想android开发的你也也用过的。
/**
* 通过传入的路径,返回该路径下的所有的文件和文件夹列表
*
* @param path
* @return
*/
fun getListData(path: String): List {
val list = ArrayList()//用来存储便利之后每一个文件中的信息
val pfile = File(path)// 兴建实例化文件对象
var files: Array? = null// 声明了一个文件对象数组
if (pfile.exists()) {// 判断路径是否存在
files = pfile.listFiles()// 该文件对象下所属的所有文件和文件夹列表
}else{//如果没有这个文件目录。那么这里去创建
pfile.mkdirs()
Log.e("name",pfile.absolutePath)
files=pfile.listFiles()
}
if (files != null && files.size > 0) {// 非空验证
for (file in files) {//循环遍历然后给适配器所要展示的数据赋值。
val item = FileInfo()
if (file.isHidden) {
continue// 跳过隐藏文件
}
if (file.isDirectory// 文件夹
&& file.canRead()//是否可读
) {
file.isHidden// 是否是隐藏文件
// 获取文件夹目录结构
item.icon = R.drawable.folder//图标
item.bytesize = file.length()
item.size = getSize(item.bytesize.toFloat())//大小
item.type = MainActivity.T_DIR
} else if (file.isFile) {// 文件
Log.i("spl", file.name)
val ext = getFileEXT(file.name)
Log.i("spl", "ext=$ext")
// 文件的图标
item.icon = getDrawableIcon(ext)// 根据扩展名获取图标
// 文件的大小
val size = getSize(file.length().toFloat())
item.size = size
item.type = MainActivity.T_FILE
} else {// 其它
item.icon = R.drawable.mul_file
}
item.name = file.name// 名称
item.lastModify = file.lastModified()
item.path = file.path// 路径
// 最后修改时间
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm")
val date = Date(file.lastModified())
val time = sdf.format(date)
item.time = time
list.add(item)
}
}
files = null
return list
}
这里我们获取路径:
如上图可以获取所有的路径和信息我们接下来将所有的这些路径来用一个ListView展示。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//1.动态权限的获取。
checkPermission()
//2.获取文件列表数据
list = FileUtils.getListData(getStoragePath(this, false)!!)// 数据
iniView()
}
private fun iniView() {
mAdapter= FileAdapter(this)
mAdapter.list=list
mAdapter.setProxy(this)
list_file.adapter=mAdapter
mAdapter.notifyDataSetChanged()
}
如下图所示:
在Adapter里面我们定义一个HashMap用来存储我们选择的item的position,然后item点击事件我们可以通过操作和比较这个hashMap里面的个数来决定下面布局是否显示,当我们点击下面取消时候,我们清除适配器里面的hashMap,然后跟新我们的适配器达到列表的更新和最下面布局的是否消失或者显示。代码如下:
//点击Item时候,选择item然后再次点击取消,如果slectMap中没有存储的key那么就可以影藏下面的布局文件。
override fun itemClick(position: Int) {
//点击多选的实现
if (mAdapter.selectMap.containsKey(position)) {
//删除key
mAdapter.selectMap.remove(position)
if (mAdapter.selectMap.size === 0) {
bottom.setVisibility(View.GONE)
} else {
bottom.setVisibility(View.VISIBLE)
}
} else {
bottom.setVisibility(View.VISIBLE)
mAdapter.selectMap.put(position, position)
}
mAdapter.notifyDataSetChanged()
}
//全不选操作
fun selectNone(v: View) {
mAdapter.selectMap.clear()
mAdapter.notifyDataSetChanged()
}
//复制
fun cope(view: View) {
if (mAdapter.selectMap.size === 0) {
Toast.makeText(this, "您还没选中任何项目!", Toast.LENGTH_SHORT).show()
} else {
//把用户信息保存到一个合理的数据结构中等待粘贴时候遍历兴建
copyMap.clear()
for (position in mAdapter.selectMap.keys) {
copyMap[position] = list[position!!].path
}
Toast.makeText(this, copyMap.size.toString() + "个项目已保存", Toast.LENGTH_SHORT).show()
//切换粘贴为激活状态
//切换粘贴为激活状态
layout.isEnabled = true
img.setImageResource(R.drawable.ic_menu_paste_holo_light)
}
}
//剪切
public void pathDelete(View v) {
if (adapter.selectMap.size() > 0) {
copyMap.clear();
for (Integer position : adapter.selectMap.keySet()) {
copyMap.put(position, list.get(position).path);
}
for (String path : copyMap.values()) {
File file = new File(path);
if (file.isFile()) {
int res = Utils.pasteFile(currPath, new File(path));
isCut=true;
//切换粘贴为激活状态
layout.setEnabled(true);
img.setImageResource(R.drawable.ic_menu_paste_holo_light);
Toast.makeText(this, path + "文件剪切成功", Toast.LENGTH_SHORT).show();
}
if (file.isDirectory()) {
Utils.pasteDir(currPath, new File(path));
}
}
} else {
Toast.makeText(this, "没有可粘帖的项目", Toast.LENGTH_SHORT).show();
}
}
fun delete(view: View) {
if (mAdapter.selectMap.size === 0) {
Toast.makeText(this, "您还没选中任何项目!", Toast.LENGTH_SHORT).show()
} else {
//显示确认框
AlertDialog.Builder(this)
.setTitle("系统提示")
.setMessage("您是否要删除这" + mAdapter.selectMap.size + "个项目")
.setPositiveButton("确定") { dialog, which ->
//遍历
for (position in mAdapter.selectMap.keys) {
val path = list[position!!].path
val file = File(path)
//根据path路径删除文件
if (file.isFile()) {
FileUtils.deleteFile(path)
}
if (file.isDirectory()) {
FileUtils.deleteDir(path)
}
}
//更新
mAdapter.selectMap.clear()
updateData(currPath)
}
.setNegativeButton("取消", null)
.create().show()
}
}
如下图所示:
https://github.com/luhenchang/MyFileManager.git不知道是不是这一个。你们试一试找了好久