Android编写简易文件管理模块
程序员文章站
2022-06-02 22:20:44
最近在做一个将word文档导入到sqlite的程序。对于文件选择问题,经过再三考虑决定写一个简易的文件管理模块,用来选择需要导入的文件文件
先看下效果图:
思...
最近在做一个将word文档导入到sqlite的程序。对于文件选择问题,经过再三考虑决定写一个简易的文件管理模块,用来选择需要导入的文件文件
先看下效果图:
思路:
获取存储器接口
遍历当前目录
利用listview显示文件文件夹
先是布局
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <horizontalscrollview android:layout_width="match_parent" android:layout_height="wrap_content" android:scrollbars="none"> <linearlayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:id="@+id/lypath"> <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:textappearance="?android:textappearance" android:text="@string/txt_path_now"/> <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:textappearance="?android:textappearance" android:text="mnt/sdcard" android:id="@+id/txtpath"/> </linearlayout> </horizontalscrollview> <view android:layout_width="match_parent" android:layout_height="1dp" android:background="@android:color/darker_gray"/> <linearlayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> <listview android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/folderlist"/> </linearlayout> </linearlayout>
用于加载文件的item布局
list_file_style.xml
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <linearlayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:scaley="0.9"> <checkbox android:layout_width="wrap_content" android:layout_height="wrap_content" android:focusable = "false" android:focusableintouchmode="false" android:id="@+id/cbselect"/> <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/img" android:src="@mipmap/other" tools:ignore="contentdescription" /> <linearlayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:textappearance="?android:textappearance" android:id="@+id/name" android:text="setting"/> <linearlayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/time" android:text="2017-3-30 21:40:02"/> <view android:layout_width="20dp" android:layout_height="match_parent"/> <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/size" android:text="1.2mb"/> </linearlayout> </linearlayout> </linearlayout> <view android:layout_width="match_parent" android:layout_height="1dp" android:background="@android:color/darker_gray"/> </linearlayout>
自定义类
为了更好的将数据绑定到listview上我选择自定义baseadapter类
package czhy.grey.sun.exam.bin.adapter_; import android.content.context; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.baseadapter; import android.widget.checkbox; import java.io.file; import java.text.decimalformat; import java.text.simpledateformat; import java.util.arraylist; import java.util.hashmap; import czhy.grey.sun.exam.r; import czhy.grey.sun.exam.bin.holder_.fileholder; public class fileadapter extends baseadapter { private arraylist<file> list; private layoutinflater inflater; private boolean isroot; // 用来控制checkbox的选中状况 private hashmap<integer, boolean> isselected; private int selectnum; public fileadapter(context context, arraylist<file> list,boolean isroot) { this.list = list; this.isroot = isroot; inflater = layoutinflater.from(context); isselected = new hashmap<>(); // 初始化数据 initdate(); } @override public int getcount() { return list.size(); } @override public file getitem(int position) { return list.get(position); } @override public long getitemid(int position) { return position; } @override public view getview(int position, view convertview, viewgroup parent) { fileholder holder; file file = getitem(position); if (convertview == null) { convertview = inflater.inflate(r.layout.list_file_style, parent, false); holder = new fileholder(convertview); convertview.settag(holder); } else { holder = (fileholder) convertview.gettag(); } // todo: 2017/4/1 根目录ui优化 if (!isroot && position == 0) { holder.setname("返回上一层", file.isdirectory(),isselectedfor(position)); holder.setid(position,isselectedfor(position),new view.onclicklistener() { @override public void onclick(view v) { int position = (int) v.gettag(); boolean b = !isselected.get(position); isselected.put(position, b); ((checkbox) v).setchecked(b); //全选或全取消操作 for(int i=0;i< getcount();i++){ setchecked(i,b); } selectnum = b?getcount():0; notifydatasetchanged(); } }); holder.settime("全选"); holder.setsize("已选择"+selectnum+"项"); } else { holder.setname(file.getname(), file.isdirectory(),isselectedfor(position)); holder.setid(position,isselectedfor(position),new view.onclicklistener() { @override public void onclick(view v) { int position = (int) v.gettag(); boolean b = !isselectedfor(position); isselected.put(position, b); ((checkbox) v).setchecked(b); //是否已经全选 if(isselectedall()) { isselected.put(0, true); selectnum = getcount(); }else { isselected.put(0, false); selectnum = b?selectnum+1:selectnum-1; } notifydatasetchanged(); } }); holder.settime(new simpledateformat("yyyy/mm/hh/dd hh:mm:ss").format(file.lastmodified())); if (file.isfile()) holder.setsize(filelength(file.length())); else { holder.setsize(""); } } return convertview; } public boolean isselectedfor(int id) { return isselected.get(id); } public int getselectnum() { return selectnum; } //私有函数 /** 初始化isselected的数据 */ private void initdate() { selectnum = 0; for (int i = 0; i < list.size(); i++) { isselected.put(i, false); } } private void setchecked(int id,boolean ischecked) { isselected.put(id,ischecked); } private string filelength(long length) { string size; if (length > 1024 * 1024) size = new decimalformat("#.00").format(length / (1024.0 * 1024.0)) + "mb"; else if (length > 1024) size = new decimalformat("#.00").format(length / 1024.0) + "kb"; else size = length + "b"; return size; } private boolean isselectedall(){ for(int i=1;i< getcount();i++){ if(!isselectedfor(i)) return false; } return true; } }
以及用于布局导入的holder
package czhy.grey.sun.exam.bin.holder_; import android.view.view; import android.widget.checkbox; import android.widget.imageview; import android.widget.textview; import czhy.grey.sun.exam.r; public class fileholder{ private checkbox cbselect; private textview name; private textview time; private textview size; private imageview img; public fileholder(view convertview) { cbselect = (checkbox)convertview.findviewbyid(r.id.cbselect); name = (textview)convertview.findviewbyid(r.id.name); time = (textview)convertview.findviewbyid(r.id.time); img = (imageview)convertview.findviewbyid(r.id.img); size = (textview)convertview.findviewbyid(r.id.size); } public void settime(string time) { this.time.settext(time); } public void setid(int id,boolean isselected, view.onclicklistener listener) { cbselect.settag(id); cbselect.setchecked(isselected); cbselect.setonclicklistener(listener); } public void setname(string name,boolean isdirectory,boolean ischecked) { this.name.settext(name); cbselect.setchecked(ischecked); if (isdirectory) { if(name.equalsignorecase("返回上一层")){ img.setimageresource(r.mipmap.back); }else img.setimageresource(r.mipmap.folder); }else { string type = name.substring(name.lastindexof(".")+1,name.length()); if ((type.equalsignorecase("doc") || type.equalsignorecase("docx") || type.equalsignorecase("txt"))) { img.setimageresource(r.mipmap.doc_text); } else { img.setimageresource(r.mipmap.other); } } } public void setsize(string size) { this.size.settext(size); } }
控制文件
全局变量
private listview folderlist; private textview txtpath; private fileadapter fileadapter; private arraylist<file> rootfilelist; private boolean isrootnow;
首先是获取存储器列表
private void getrootfile(){ rootfilelist = new arraylist<>(); storagemanager storagemanager = (storagemanager) this.getsystemservice(storage_service); try { //内部存储器 file inside = null; //可移除存储器集合 arraylist<file> outside = new arraylist<>(); //获取存储器接口 api-24以下不支持storagevolume接口 //api-24开始可直接 list<storagevolume> svlist = storagemanager.getstoragevolumes(); method getvolumelist = storagemanager.class.getmethod("getvolumelist"); getvolumelist.setaccessible(true); //获取存储器列表 object[] invokes = (object[]) getvolumelist.invoke(storagemanager); if (invokes != null) { for (object obj:invokes) { //获取存储器地址接口 method getpath = obj.getclass().getmethod("getpath"); //获取存储器地址 string path = (string) getpath.invoke(obj); file file = new file(path); if (file.canwrite()) { //获取存储器是否可移除接口 method isremovable = obj.getclass().getmethod("isremovable"); //存储器是否可移除 if((isremovable.invoke(obj)).equals(true)){ outside.add(file); }else { inside = file; } } } //按0-内部存储器 >0外部存储器 顺序 添加到根目录列表 rootfilelist.add(inside); rootfilelist.addall(outside); } } catch (nosuchmethodexception | illegalargumentexception | illegalaccessexception | invocationtargetexception e1) { e1.printstacktrace(); } }
当我们点击一个文件夹时,打开文件夹以及更新ui
//获取目录数据 private void refurbish(file folder) { txtpath.settext(folder.getpath()); arraylist<file> files = new arraylist<>(); files.add(folder.getparentfile()); file[] folderfile = folder.listfiles(); if (null != folderfile && folderfile.length > 0) { for(file file:folderfile) files.add(file); } //新建集合用做打开文件夹 arraylist<file> openedfolder = new arraylist<>(); //获取第一个文件夹 上一级文件夹 openedfolder.add(files.get(0)); //移除 上一级文件夹 剩下为当前文件夹内容 files.remove(0); //排序 文件夹在前,然后按文件名排序 collections.sort(files, new comparator<file>() { @override public int compare(file f1, file f2) { if (f1.isdirectory()) { if (f2.isdirectory()) { return f1.getname().comparetoignorecase(f2.getname()); } else { return -1; } } else if (f2.isdirectory()) { return 1; } else { return f1.getname().comparetoignorecase(f2.getname()); } } }); //将排序完毕的内容添加到目标集合 目的:解决第一个文件夹不是上一层地址问题 openedfolder.addall(files); fileadapter = new fileadapter(this, openedfolder,folder.getparent() == null); folderlist.setadapter(fileadapter); isrootnow = false; }
程序刚运行时需要加载存储器列表,而不是某一文件夹,所有需要额外定义一个函数
//获取根目录数据 private void rootfile() { txtpath.settext("/"); fileadapter = new fileadapter(this, rootfilelist,true); folderlist.setadapter(fileadapter); isrootnow = true; }
因为存储器挂载点的问题,返回上一层时不会返回到存储器列表也就是/storage目录
加上有些文件夹是空或者为系统文件的安全性考虑需要对其隐藏,在获取存储器列表是已经完成了
但,如果直接让其返回上一层会出现进入不安全的目录,所以需要对其进行判断是否是返回根目录
public boolean isrootfile(file file) { //经过两部不同的手机测试,这两个目录是可能的目录 //如果不能正确返回可以自行测试 //测试方法:输出父目录,然后在这里添加或修改即可 return file.getparent().equalsignorecase("/") || file.getparent().equalsignorecase("/storage"); }
有了以上这些,在控制文件创建是直接调用相应的函数即可
@override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_import); txtpath = (textview) findviewbyid(r.id.txtpath); folderlist = (listview) findviewbyid(r.id.folderlist); folderlist.setonitemclicklistener(new adapterview.onitemclicklistener() { @override public void onitemclick(adapterview<?> parent, view view, int position, long id) { file file = fileadapter.getitem(position); //因为为的程序中不需要对文件进行其他操作,所有不做处理 //有需求的在这里添加else即可 if (file.isdirectory()) { if (isrootnow) refurbish(file); else if (isrootfile(file)) rootfile(); else refurbish(file); } } }); getrootfile(); rootfile(); }
弄了这么多基本算是完成了,为了用户友好,我对返回键进行了重载
/** * 监听物理按键 * 需要注意的是这个函数是有返回值的 * 返回值可以理解为 * true表示做了处理,就不提交给处理系统的back按键事件 * false则是提交给系统处理 */ @override public boolean onkeydown(int keycode, keyevent event) { if ((keycode == keyevent.keycode_back && event.getrepeatcount() == 0)) { if (!isrootnow) { file file = fileadapter.getitem(0); if (isrootfile(file)) rootfile(); else refurbish(file); return true; } else { finish(); } } return super.onkeydown(keycode, event); }
参考文献:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
Python3中类、模块、错误与异常、文件的简易教程
-
Android远程桌面助手之文件管理器
-
android文件管理器用法详解
-
android简易文件管理器实例(列表式文件目录)
-
Android编写简易文件管理模块
-
C语言:自己编写的简易ftp客户端,包含(列表,进入目录,上传文件,下载文件,删除文件)功能
-
Android 系统——Android中各类.mk文件的编写
-
Python3使用TCP编写一个简易的文件下载器功能
-
客户管理模块(文件上传-图片/删除-修改客户/条件查询客户)| CRM客户关系管理系统项目实战三(Struts2+Spring+Hibernate)解析+源代码
-
Android Studio 实现点击按钮 调出设备自带的文件管理器 读取手机设备文件