最近在做一个将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;
}
//私有函数
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();
}
弄了这么多基本算是完成了,为了用户友好,我对返回键进行了重载
@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文件管理器用法详解
如何获取Android设备挂载的所有存储器
您可能感兴趣的文章:Android获取手机文件夹及文件列表的方法android文件管理器用法详解android简易文件管理器实例(列表式文件目录)