欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android编写简易文件管理模块

程序员文章站 2022-06-02 22:20:44
最近在做一个将word文档导入到sqlite的程序。对于文件选择问题,经过再三考虑决定写一个简易的文件管理模块,用来选择需要导入的文件文件 先看下效果图: 思...

最近在做一个将word文档导入到sqlite的程序。对于文件选择问题,经过再三考虑决定写一个简易的文件管理模块,用来选择需要导入的文件文件

先看下效果图:

Android编写简易文件管理模块

思路:

获取存储器接口
遍历当前目录
利用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);
 }

参考文献:

android获取内外置存储卡的方法

如何获取android设备挂载的所有存储器

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。